Пример #1
0
// ReadValues fetches values of the variables that were passed to the Collector
// with AddVariable. The values of any new variables found are also fetched,
// e.g. the targets of pointers or the members of structs, until we reach the
// size limit or we run out of values to fetch.
// The results are output as a []*cd.Variable, which is the type we need to send
// to the Debuglet Controller after we trigger a breakpoint.
func (c *Collector) ReadValues() (out []*cd.Variable) {
	for i := 0; i < len(c.table); i++ {
		// Create a new cd.Variable for this value, and append it to the output.
		dcv := new(cd.Variable)
		out = append(out, dcv)
		if i == 0 {
			// The first element is unused.
			continue
		}
		switch x := c.table[i].(type) {
		case mapElement:
			key, value, err := c.prog.MapElement(x.Map, x.index)
			if err != nil {
				dcv.Status = statusMessage(err.Error(), true, refersToVariableValue)
				continue
			}
			// Add a member for the key.
			member := addMember(dcv, "key")
			if index, ok := c.add(key); !ok {
				// The table is full.
				member.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
				continue
			} else {
				member.VarTableIndex = int64(index)
			}
			// Add a member for the value.
			member = addMember(dcv, "value")
			if index, ok := c.add(value); !ok {
				// The table is full.
				member.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
			} else {
				member.VarTableIndex = int64(index)
			}
		case debug.Var:
			if v, err := c.prog.Value(x); err != nil {
				dcv.Status = statusMessage(err.Error(), true, refersToVariableValue)
			} else {
				c.FillValue(v, dcv)
			}
		}
	}
	return out
}
Пример #2
0
// FillValue copies a value into a cd.Variable.  Any variables referred to by
// that value, e.g. struct members and pointer targets, are added to the
// collector's queue, to be fetched later by ReadValues.
func (c *Collector) FillValue(v debug.Value, dcv *cd.Variable) {
	if c, ok := v.(debug.Channel); ok {
		// Convert to channel, which implements indexable.
		v = channel{c}
	}
	// Fill in dcv in a manner depending on the type of the value we got.
	switch val := v.(type) {
	case int8, int16, int32, int64, bool, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128:
		// For simple types, we just print the value to dcv.Value.
		dcv.Value = fmt.Sprint(val)
	case string:
		// Put double quotes around strings.
		dcv.Value = strconv.Quote(val)
	case debug.String:
		if uint64(len(val.String)) < val.Length {
			// This string value was truncated.
			dcv.Value = strconv.Quote(val.String + "...")
		} else {
			dcv.Value = strconv.Quote(val.String)
		}
	case debug.Struct:
		// For structs, we add an entry to dcv.Members for each field in the
		// struct.
		// Each member will contain the name of the field, and the index in the
		// output table which will contain the value of that field.
		for _, f := range val.Fields {
			member := addMember(dcv, f.Name)
			if index, ok := c.add(f.Var); !ok {
				// The table is full.
				member.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
			} else {
				member.VarTableIndex = int64(index)
			}
		}
	case debug.Map:
		dcv.Value = fmt.Sprintf("len = %d", val.Length)
		for i := uint64(0); i < val.Length; i++ {
			field := addMember(dcv, `⚫`)
			if i == maxMapLength {
				field.Name = "..."
				field.Status = statusMessage(messageTruncated, true, refersToVariableName)
				break
			}
			if index, ok := c.add(mapElement{val, i}); !ok {
				// The value table is full; add a member to contain the error message.
				field.Name = "..."
				field.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
				break
			} else {
				field.VarTableIndex = int64(index)
			}
		}
	case debug.Pointer:
		if val.Address == 0 {
			dcv.Value = "<nil>"
		} else if val.TypeID == 0 {
			// We don't know the type of the pointer, so just output the address as
			// the value.
			dcv.Value = fmt.Sprintf("0x%X", val.Address)
			dcv.Status = statusMessage(messageUnknownPointerType, false, refersToVariableName)
		} else {
			// Adds the pointed-to variable to the table, and links this value to
			// that table entry through VarTableIndex.
			dcv.Value = fmt.Sprintf("0x%X", val.Address)
			target := addMember(dcv, "")
			if index, ok := c.add(debug.Var(val)); !ok {
				target.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
			} else {
				target.VarTableIndex = int64(index)
			}
		}
	case indexable:
		// Arrays, slices and channels.
		dcv.Value = "len = " + fmt.Sprint(val.Len())
		for j := uint64(0); j < val.Len(); j++ {
			field := addMember(dcv, fmt.Sprint(`[`, j, `]`))
			if j == maxArrayLength {
				field.Name = "..."
				field.Status = statusMessage(messageTruncated, true, refersToVariableName)
				break
			}
			vr := val.Element(j)
			if index, ok := c.add(vr); !ok {
				// The value table is full; add a member to contain the error message.
				field.Name = "..."
				field.Status = statusMessage(messageNotCaptured, true, refersToVariableName)
				break
			} else {
				// Add a member with the index as the name.
				field.VarTableIndex = int64(index)
			}
		}
	default:
		dcv.Status = statusMessage(messageUnknownType, false, refersToVariableName)
	}
}
Пример #3
0
func addMember(v *cd.Variable, name string) *cd.Variable {
	v2 := &cd.Variable{Name: name}
	v.Members = append(v.Members, v2)
	return v2
}