New command 'types'

Lists all the types defined in the debugged program.
This commit is contained in:
aarzilli 2016-02-22 16:08:45 +01:00 committed by Derek Parker
parent 14a92352bc
commit c8f4cee97d
8 changed files with 113 additions and 14 deletions

@ -143,11 +143,7 @@ func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resol
return nil, TypeNotFoundErr
}
// 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
func (reader *Reader) NextType() (*dwarf.Entry, error) {
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
@ -155,9 +151,21 @@ func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
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:
//ok
default:
continue
return entry, nil
}
}
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)

@ -656,6 +656,25 @@ func (dbp *Process) Funcs() []gosym.Func {
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.
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
return dbp.goSymTable.PCToLine(pc)

@ -69,6 +69,8 @@ type Client interface {
ListSources(filter string) ([]string, error)
// ListFunctions lists all functions in the process matching filter.
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.
ListLocalVariables(scope api.EvalScope) ([]api.Variable, error)
// 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())
}
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) {
regex, err := regexp.Compile(filter)
if err != nil {

@ -209,6 +209,12 @@ func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
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) {
var vars []api.Variable
err := c.call("ListPackageVars", filter, &vars)

@ -345,6 +345,15 @@ func (s *RPCServer) ListFunctions(filter string, funcs *[]string) error {
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 {
gs, err := s.debugger.Goroutines()
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})
assertNoError(err, t, "CreateBreakpoint()")
stateCh := c.Continue()
state := <- stateCh
state := <-stateCh
if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint {
t.Fatalf("Wrong breakpoint: %#v\n", state.CurrentThread.Breakpoint)
}
state = <- stateCh
state = <-stateCh
if !state.Exited {
t.Fatal("Did not exit after first tracepoint")
}
t.Log("Restart")
c.Restart()
stateCh = c.Continue()
state = <- stateCh
state = <-stateCh
if state.CurrentThread.Breakpoint.Name != "firstbreakpoint" || !state.CurrentThread.Breakpoint.Tracepoint {
t.Fatalf("Wrong breakpoint (after restart): %#v\n", state.CurrentThread.Breakpoint)
}
state = <- stateCh
state = <-stateCh
if !state.Exited {
t.Fatal("Did not exit after first tracepoint (after restart)")
}
@ -1089,3 +1089,29 @@ func TestIssue419(t *testing.T) {
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{"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{"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{"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."},
@ -703,6 +704,10 @@ func funcs(t *Term, filter string) ([]string, error) {
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) {
vars, err := t.client.ListFunctionArgs(scope)
if err != nil {