cmd/viewcore: display the origin of core
This change adds the origin of the analyzed core in the welcome
message of interactive mode. The information is from NT_PRPSINFO
(currently assuming linux/amd64 only). This info allows users to
verify they are looking into the correct core file.
This is similar to what gdb does.
Change-Id: Ib626c9007a588e34cf633908dedde591e6f04e3c
Reviewed-on: https://go-review.googlesource.com/121015
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/cmd/viewcore/main.go b/cmd/viewcore/main.go
index 497b911..91ffebd 100644
--- a/cmd/viewcore/main.go
+++ b/cmd/viewcore/main.go
@@ -301,32 +301,12 @@
}
defer shell.Close()
- // TODO(hyangah): the following code retrieves the main
- // program path from the mapping information.
- // The intention is to help users confirm viewcore
- // picked up correct executable and core file for analysis.
- // The usefulness of this info is debatable.
- // Another idea is to present the executable file info
- // embedded in prpsinfo which will provide useful
- // new information about the corefile to analyze.
- exe := "unknown"
- if m := p.Mappings(); len(m) > 0 {
- src, _ := m[0].Source()
- if m[0].CopyOnWrite() {
- src, _ = m[0].OrigSource()
- }
- if src != "" {
- exe = src
- }
+ // nice welcome message.
+ fmt.Fprintln(shell.Terminal)
+ if args := p.Args(); args != "" {
+ fmt.Fprintf(shell.Terminal, "Core %q was generated by %q\n", cfg.corefile, args)
}
-
- welcomeMsg := `
- Corefile: %s
- Program : %s
-
-Entering interactive mode (type 'help' for commands)
-`
- fmt.Fprintf(shell.Terminal, welcomeMsg, cfg.corefile, exe)
+ fmt.Fprintf(shell.Terminal, "Entering interactive mode (type 'help' for commands)\n")
for {
l, err := shell.Readline()
diff --git a/internal/core/core_test.go b/internal/core/core_test.go
index 904a75a..341babf 100644
--- a/internal/core/core_test.go
+++ b/internal/core/core_test.go
@@ -117,3 +117,11 @@
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)
+ }
+}
diff --git a/internal/core/process.go b/internal/core/process.go
index 0e56e0e..65c2fd4 100644
--- a/internal/core/process.go
+++ b/internal/core/process.go
@@ -17,6 +17,7 @@
package core
import (
+ "bytes"
"debug/dwarf"
"debug/elf" // TODO: use golang.org/x/debug/elf instead?
"encoding/binary"
@@ -33,9 +34,10 @@
base string // base directory from which files in the core can be found
exePath string // user-supplied main executable path
- exec []*os.File // executables (more than one for shlibs)
- mappings []*Mapping // virtual address mappings
- threads []*Thread // os threads (TODO: map from pid?)
+ exec []*os.File // executables (more than one for shlibs)
+ mappings []*Mapping // virtual address mappings
+ threads []*Thread // os threads (TODO: map from pid?)
+
arch string // amd64, ...
ptrSize int64 // 4 or 8
logPtrSize uint // 2 or 3
@@ -46,7 +48,9 @@
dwarf *dwarf.Data // debugging info (could be nil)
dwarfErr error // an error encountered while reading DWARF
pageTable pageTable4 // for fast address->mapping lookups
- warnings []string // warnings generated during loading
+ args string // first part of args retrieved from NT_PRPSINFO
+
+ warnings []string // warnings generated during loading
}
// Mappings returns a list of virtual memory mappings for p.
@@ -336,20 +340,22 @@
b = b[(descsz+3)/4*4:]
if name == "CORE" && typ == NT_FILE {
- err := p.readNTFile(f, e, desc)
- if err != nil {
- return err
+ if err := p.readNTFile(f, e, desc); err != nil {
+ return fmt.Errorf("reading NT_FILE: %v", err)
}
}
if name == "CORE" && typ == elf.NT_PRSTATUS {
// An OS thread (an M)
- err := p.readPRStatus(f, e, desc)
- if err != nil {
- return err
+ if err := p.readPRStatus(f, e, desc); err != nil {
+ return fmt.Errorf("reading NT_PRSTATUS: %v", err)
+ }
+ }
+ if name == "CORE" && typ == elf.NT_PRPSINFO {
+ if err := p.readPRPSInfo(desc); err != nil {
+ return fmt.Errorf("reading NT_PRPSINFO: %v", err)
}
}
// TODO: NT_FPREGSET for floating-point registers
- // TODO: NT_PRPSINFO for ???
}
return nil
}
@@ -481,6 +487,21 @@
}
}
+func (p *Process) readPRPSInfo(desc []byte) error {
+ r := bytes.NewReader(desc)
+ switch p.arch {
+ default:
+ // TODO: return error?
+ case "amd64":
+ prpsinfo := &linuxPrPsInfo{}
+ if err := binary.Read(r, binary.LittleEndian, prpsinfo); err != nil {
+ return err
+ }
+ p.args = strings.Trim(string(prpsinfo.Args[:]), "\x00 ")
+ }
+ return nil
+}
+
func (p *Process) readPRStatus(f *os.File, e *elf.File, desc []byte) error {
t := &Thread{}
p.threads = append(p.threads, t)
@@ -575,3 +596,24 @@
func (p *Process) Warnings() []string {
return p.warnings
}
+
+// Args returns the initial part of the program arguments.
+func (p *Process) Args() string {
+ return p.args
+}
+
+// ELF/Linux types
+
+// linuxPrPsInfo is the info embedded in NT_PRPSINFO.
+type linuxPrPsInfo struct {
+ State uint8
+ Sname int8
+ Zomb uint8
+ Nice int8
+ _ [4]uint8
+ Flag uint64
+ Uid, Gid uint32
+ Pid, Ppid, Pgrp, Sid int32
+ Fname [16]uint8 // filename of executables
+ Args [80]uint8 // first part of program args
+}