blob: 3b569977361a1961e2866d5584c0f0d066b28243 [file] [log] [blame]
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package core
import (
"fmt"
"testing"
)
// loadExample loads a simple core file which resulted from running the
// following program on linux/amd64 with go 1.9.0 (the earliest supported runtime):
// package main
// func main() {
// _ = *(*int)(nil)
// }
func loadExample(t *testing.T, useExePath bool) *Process {
t.Helper()
var p *Process
var err error
if useExePath {
p, err = Core("testdata/core", "", "testdata/tmp/test")
} else {
p, err = Core("testdata/core", "testdata", "")
}
if err != nil {
t.Fatalf("can't load test core file: %s", err)
}
return p
}
// TestMappings makes sure we can find and load some data.
func TestMappings(t *testing.T) {
test := func(t *testing.T, useExePath bool) {
p := loadExample(t, useExePath)
s, err := p.Symbols()
if err != nil {
t.Errorf("can't read symbols: %s\n", err)
}
a := s["main.main"]
m := p.findMapping(a)
if m == nil {
t.Errorf("text mapping missing")
}
if m.Perm() != Read|Exec {
t.Errorf("bad code section permissions")
}
if opcode := p.ReadUint8(a); opcode != 0x31 {
// 0x31 = xorl instruction.
// There's no particular reason why this instruction
// is first. This just tests that reading code works
// for our specific test binary.
t.Errorf("opcode=0x%x, want 0x31", opcode)
}
a = s["runtime.class_to_size"]
m = p.findMapping(a)
if m == nil {
t.Errorf("data mapping missing")
}
if m.Perm() != Read|Write {
t.Errorf("bad data section permissions")
}
if size := p.ReadUint16(a.Add(2)); size != 8 {
t.Errorf("class_to_size[1]=%d, want 8", size)
}
}
for _, useExePath := range []bool{false, true} {
name := fmt.Sprintf("useExePath=%t", useExePath)
t.Run(name, func(t *testing.T) {
test(t, useExePath)
})
}
}
// TestConfig checks the configuration accessors.
func TestConfig(t *testing.T) {
p := loadExample(t, false)
if arch := p.Arch(); arch != "amd64" {
t.Errorf("arch=%s, want amd64", arch)
}
if size := p.PtrSize(); size != 8 {
t.Errorf("ptrSize=%d, want 8", size)
}
if log := p.LogPtrSize(); log != 3 {
t.Errorf("logPtrSize=%d, want 3", log)
}
if bo := p.ByteOrder(); bo.String() != "LittleEndian" {
t.Errorf("got %s, want LittleEndian", bo)
}
}
// TestThread makes sure we get information about running threads.
func TestThread(t *testing.T) {
p := loadExample(t, true)
syms, err := p.Symbols()
if err != nil {
t.Errorf("can't read symbols: %s\n", err)
}
raise := syms["runtime.raise"]
var size int64 = 1 << 30
for _, a := range syms {
if a > raise && a.Sub(raise) < size {
size = a.Sub(raise)
}
}
found := false
for _, thr := range p.Threads() {
if thr.PC() >= raise && thr.PC() < raise.Add(size) {
found = true
}
}
if !found {
t.Errorf("can't find thread that did runtime.raise")
}
}
func TestArgs(t *testing.T) {
p := loadExample(t, true)
if got := p.Args(); got != "./test" {
// this is how the program of testdata/core was invoked.
t.Errorf("Args() = %q, want './test'", got)
}
}