New command 'types'
Lists all the types defined in the debugged program.
This commit is contained in:
parent
14a92352bc
commit
c8f4cee97d
@ -143,11 +143,7 @@ func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resol
|
|||||||
return nil, TypeNotFoundErr
|
return nil, TypeNotFoundErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeekToTypeNamed moves the reader to the type specified by the name.
|
func (reader *Reader) NextType() (*dwarf.Entry, error) {
|
||||||
// If the reader is set to a struct type the NextMemberVariable call
|
|
||||||
// can be used to walk all member data.
|
|
||||||
func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
|
||||||
// Walk the types to the base
|
|
||||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -155,9 +151,21 @@ func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
|||||||
|
|
||||||
switch entry.Tag {
|
switch entry.Tag {
|
||||||
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
|
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
|
||||||
//ok
|
return entry, nil
|
||||||
default:
|
}
|
||||||
continue
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SeekToTypeNamed moves the reader to the type specified by the name.
|
||||||
|
// If the reader is set to a struct type the NextMemberVariable call
|
||||||
|
// can be used to walk all member data.
|
||||||
|
func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
||||||
|
// Walk the types to the base
|
||||||
|
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
n, ok := entry.Val(dwarf.AttrName).(string)
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
||||||
|
19
proc/proc.go
19
proc/proc.go
@ -656,6 +656,25 @@ func (dbp *Process) Funcs() []gosym.Func {
|
|||||||
return dbp.goSymTable.Funcs
|
return dbp.goSymTable.Funcs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Types returns list of types present in the debugged program.
|
||||||
|
func (dbp *Process) Types() ([]string, error) {
|
||||||
|
reader := dbp.DwarfReader()
|
||||||
|
types := []string{}
|
||||||
|
seen := map[string]struct{}{}
|
||||||
|
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if n, ok := entry.Val(dwarf.AttrName).(string); ok {
|
||||||
|
if _, isseen := seen[n]; !isseen {
|
||||||
|
seen[n] = struct{}{}
|
||||||
|
types = append(types, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types, nil
|
||||||
|
}
|
||||||
|
|
||||||
// PCToLine converts an instruction address to a file/line/function.
|
// PCToLine converts an instruction address to a file/line/function.
|
||||||
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
||||||
return dbp.goSymTable.PCToLine(pc)
|
return dbp.goSymTable.PCToLine(pc)
|
||||||
|
@ -69,6 +69,8 @@ type Client interface {
|
|||||||
ListSources(filter string) ([]string, error)
|
ListSources(filter string) ([]string, error)
|
||||||
// ListFunctions lists all functions in the process matching filter.
|
// ListFunctions lists all functions in the process matching filter.
|
||||||
ListFunctions(filter string) ([]string, error)
|
ListFunctions(filter string) ([]string, error)
|
||||||
|
// ListTypes lists all types in the process matching filter.
|
||||||
|
ListTypes(filter string) ([]string, error)
|
||||||
// ListLocals lists all local variables in scope.
|
// ListLocals lists all local variables in scope.
|
||||||
ListLocalVariables(scope api.EvalScope) ([]api.Variable, error)
|
ListLocalVariables(scope api.EvalScope) ([]api.Variable, error)
|
||||||
// ListFunctionArgs lists all arguments to the current function.
|
// ListFunctionArgs lists all arguments to the current function.
|
||||||
|
@ -498,6 +498,30 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
|
|||||||
return regexFilterFuncs(filter, d.process.Funcs())
|
return regexFilterFuncs(filter, d.process.Funcs())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||||
|
d.processMutex.Lock()
|
||||||
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
|
regex, err := regexp.Compile(filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
types, err := d.process.Types()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := make([]string, 0, len(types))
|
||||||
|
for _, typ := range types {
|
||||||
|
if regex.Match([]byte(typ)) {
|
||||||
|
r = append(r, typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
|
func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
|
||||||
regex, err := regexp.Compile(filter)
|
regex, err := regexp.Compile(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -209,6 +209,12 @@ func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
|
|||||||
return funcs, err
|
return funcs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *RPCClient) ListTypes(filter string) ([]string, error) {
|
||||||
|
var types []string
|
||||||
|
err := c.call("ListTypes", filter, &types)
|
||||||
|
return types, err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) {
|
func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) {
|
||||||
var vars []api.Variable
|
var vars []api.Variable
|
||||||
err := c.call("ListPackageVars", filter, &vars)
|
err := c.call("ListPackageVars", filter, &vars)
|
||||||
|
@ -345,6 +345,15 @@ func (s *RPCServer) ListFunctions(filter string, funcs *[]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *RPCServer) ListTypes(filter string, types *[]string) error {
|
||||||
|
tps, err := s.debugger.Types(filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*types = tps
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error {
|
func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error {
|
||||||
gs, err := s.debugger.Goroutines()
|
gs, err := s.debugger.Goroutines()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -104,24 +104,24 @@ func TestRestart_breakpointPreservation(t *testing.T) {
|
|||||||
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Name: "firstbreakpoint", Tracepoint: true})
|
_, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Name: "firstbreakpoint", Tracepoint: true})
|
||||||
assertNoError(err, t, "CreateBreakpoint()")
|
assertNoError(err, t, "CreateBreakpoint()")
|
||||||
stateCh := c.Continue()
|
stateCh := c.Continue()
|
||||||
|
|
||||||
state := <- stateCh
|
state := <-stateCh
|
||||||
if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint {
|
if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint {
|
||||||
t.Fatalf("Wrong breakpoint: %#v\n", state.CurrentThread.Breakpoint)
|
t.Fatalf("Wrong breakpoint: %#v\n", state.CurrentThread.Breakpoint)
|
||||||
}
|
}
|
||||||
state = <- stateCh
|
state = <-stateCh
|
||||||
if !state.Exited {
|
if !state.Exited {
|
||||||
t.Fatal("Did not exit after first tracepoint")
|
t.Fatal("Did not exit after first tracepoint")
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log("Restart")
|
t.Log("Restart")
|
||||||
c.Restart()
|
c.Restart()
|
||||||
stateCh = c.Continue()
|
stateCh = c.Continue()
|
||||||
state = <- stateCh
|
state = <-stateCh
|
||||||
if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint {
|
if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint {
|
||||||
t.Fatalf("Wrong breakpoint (after restart): %#v\n", state.CurrentThread.Breakpoint)
|
t.Fatalf("Wrong breakpoint (after restart): %#v\n", state.CurrentThread.Breakpoint)
|
||||||
}
|
}
|
||||||
state = <- stateCh
|
state = <-stateCh
|
||||||
if !state.Exited {
|
if !state.Exited {
|
||||||
t.Fatal("Did not exit after first tracepoint (after restart)")
|
t.Fatal("Did not exit after first tracepoint (after restart)")
|
||||||
}
|
}
|
||||||
@ -1089,3 +1089,29 @@ func TestIssue419(t *testing.T) {
|
|||||||
assertNoError(state.Err, t, "Continue()")
|
assertNoError(state.Err, t, "Continue()")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTypesCommand(t *testing.T) {
|
||||||
|
withTestClient("testvariables2", t, func(c service.Client) {
|
||||||
|
state := <-c.Continue()
|
||||||
|
assertNoError(state.Err, t, "Continue()")
|
||||||
|
types, err := c.ListTypes("")
|
||||||
|
assertNoError(err, t, "ListTypes()")
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for i := range types {
|
||||||
|
if types[i] == "main.astruct" {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
t.Fatal("Type astruct not found in ListTypes output")
|
||||||
|
}
|
||||||
|
|
||||||
|
types, err = c.ListTypes("main.astruct")
|
||||||
|
assertNoError(err, t, "ListTypes(\"main.astruct\")")
|
||||||
|
if len(types) <= 0 {
|
||||||
|
t.Fatal("ListTypes(\"main.astruct\") did not return anything")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -75,6 +75,7 @@ func DebugCommands(client service.Client) *Commands {
|
|||||||
{aliases: []string{"set"}, cmdFn: g0f0(setVar), helpMsg: "Changes the value of a variable."},
|
{aliases: []string{"set"}, cmdFn: g0f0(setVar), helpMsg: "Changes the value of a variable."},
|
||||||
{aliases: []string{"sources"}, cmdFn: filterSortAndOutput(sources), helpMsg: "Print list of source files, optionally filtered by a regexp."},
|
{aliases: []string{"sources"}, cmdFn: filterSortAndOutput(sources), helpMsg: "Print list of source files, optionally filtered by a regexp."},
|
||||||
{aliases: []string{"funcs"}, cmdFn: filterSortAndOutput(funcs), helpMsg: "Print list of functions, optionally filtered by a regexp."},
|
{aliases: []string{"funcs"}, cmdFn: filterSortAndOutput(funcs), helpMsg: "Print list of functions, optionally filtered by a regexp."},
|
||||||
|
{aliases: []string{"types"}, cmdFn: filterSortAndOutput(types), helpMsg: "Print list of types, optionally filtered by a regexp."},
|
||||||
{aliases: []string{"args"}, cmdFn: filterSortAndOutput(g0f0filter(args)), helpMsg: "Print function arguments, optionally filtered by a regexp."},
|
{aliases: []string{"args"}, cmdFn: filterSortAndOutput(g0f0filter(args)), helpMsg: "Print function arguments, optionally filtered by a regexp."},
|
||||||
{aliases: []string{"locals"}, cmdFn: filterSortAndOutput(g0f0filter(locals)), helpMsg: "Print function locals, optionally filtered by a regexp."},
|
{aliases: []string{"locals"}, cmdFn: filterSortAndOutput(g0f0filter(locals)), helpMsg: "Print function locals, optionally filtered by a regexp."},
|
||||||
{aliases: []string{"vars"}, cmdFn: filterSortAndOutput(vars), helpMsg: "Print package variables, optionally filtered by a regexp."},
|
{aliases: []string{"vars"}, cmdFn: filterSortAndOutput(vars), helpMsg: "Print package variables, optionally filtered by a regexp."},
|
||||||
@ -703,6 +704,10 @@ func funcs(t *Term, filter string) ([]string, error) {
|
|||||||
return t.client.ListFunctions(filter)
|
return t.client.ListFunctions(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func types(t *Term, filter string) ([]string, error) {
|
||||||
|
return t.client.ListTypes(filter)
|
||||||
|
}
|
||||||
|
|
||||||
func args(t *Term, scope api.EvalScope, filter string) ([]string, error) {
|
func args(t *Term, scope api.EvalScope, filter string) ([]string, error) {
|
||||||
vars, err := t.client.ListFunctionArgs(scope)
|
vars, err := t.client.ListFunctionArgs(scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user