// Experiment is the name of the experiment this event is a part of.
Experiment string
- // ArgNames is the names of the event's arguments in order.
- // This may refer to a globally shared slice. Copy before mutating.
- ArgNames []string
+ // Args lists the names of the event's arguments in order.
+ Args []string
- // Args contains the event's arguments.
- Args []uint64
+ // argValues contains the raw integer arguments which are interpreted
+ // by ArgValue using table.
+ table *evTable
+ argValues []uint64
+}
+
+// ArgValue returns a typed Value for the i'th argument in the experimental event.
+func (e ExperimentalEvent) ArgValue(i int) Value {
+ if i < 0 || i >= len(e.Args) {
+ panic(fmt.Sprintf("experimental event argument index %d out of bounds [0, %d)", i, len(e.Args)))
+ }
+ if strings.HasSuffix(e.Args[i], "string") {
+ s := e.table.strings.mustGet(stringID(e.argValues[i]))
+ return stringValue(s)
+ }
+ return uint64Value(e.argValues[i])
}
// ExperimentalBatch represents a packet of unparsed data along with metadata about that packet.
switch e.base.typ {
case tracev2.EvProcsChange:
m.Name = "/sched/gomaxprocs:threads"
- m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]}
+ m.Value = uint64Value(e.base.args[0])
case tracev2.EvHeapAlloc:
m.Name = "/memory/classes/heap/objects:bytes"
- m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]}
+ m.Value = uint64Value(e.base.args[0])
case tracev2.EvHeapGoal:
m.Name = "/gc/heap/goal:bytes"
- m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]}
+ m.Value = uint64Value(e.base.args[0])
default:
panic(fmt.Sprintf("internal error: unexpected wire-format event type for Metric kind: %d", e.base.typ))
}
return []RangeAttribute{
{
Name: "bytes swept",
- Value: Value{kind: ValueUint64, scalar: e.base.args[0]},
+ Value: uint64Value(e.base.args[0]),
},
{
Name: "bytes reclaimed",
- Value: Value{kind: ValueUint64, scalar: e.base.args[1]},
+ Value: uint64Value(e.base.args[1]),
},
}
}
return ExperimentalEvent{
Name: spec.Name,
Experiment: tracev2.Experiments()[spec.Experiment],
- ArgNames: argNames,
- Args: e.base.args[:len(argNames)],
+ Args: argNames,
+ table: e.table,
+ argValues: e.base.args[:len(argNames)],
}
}
switch kind := e.Kind(); kind {
case EventMetric:
m := e.Metric()
- fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, valueAsString(m.Value))
+ fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, m.Value)
case EventLabel:
l := e.Label()
fmt.Fprintf(&sb, " Label=%q Resource=%s", l.Label, l.Resource)
if i != 0 {
fmt.Fprintf(&sb, " ")
}
- fmt.Fprintf(&sb, "%q=%s", attr.Name, valueAsString(attr.Value))
+ fmt.Fprintf(&sb, "%q=%s", attr.Name, attr.Value)
}
fmt.Fprintf(&sb, "]")
}
}
case EventExperimental:
r := e.Experimental()
- fmt.Fprintf(&sb, " Name=%s ArgNames=%v Args=%v", r.Name, r.ArgNames, r.Args)
+ fmt.Fprintf(&sb, " Name=%s Args=[", r.Name)
+ for i, arg := range r.Args {
+ if i != 0 {
+ fmt.Fprintf(&sb, ", ")
+ }
+ fmt.Fprintf(&sb, "%s=%s", arg, r.ArgValue(i).String())
+ }
+ fmt.Fprintf(&sb, "]")
}
if stk := e.Stack(); stk != NoStack {
fmt.Fprintln(&sb)
package trace
-import "fmt"
+import (
+ "fmt"
+ "unsafe"
+)
// Value is a dynamically-typed value obtained from a trace.
type Value struct {
- kind ValueKind
- scalar uint64
+ kind ValueKind
+ pointer unsafe.Pointer
+ scalar uint64
}
// ValueKind is the type of a dynamically-typed value from a trace.
const (
ValueBad ValueKind = iota
ValueUint64
+ ValueString
)
// Kind returns the ValueKind of the value.
return v.kind
}
-// Uint64 returns the uint64 value for a MetricSampleUint64.
+// ToUint64 returns the uint64 value for a ValueUint64.
//
-// Panics if this metric sample's Kind is not MetricSampleUint64.
-func (v Value) Uint64() uint64 {
+// Panics if this Value's Kind is not ValueUint64.
+func (v Value) ToUint64() uint64 {
if v.kind != ValueUint64 {
- panic("Uint64 called on Value of a different Kind")
+ panic("ToUint64 called on Value of a different Kind")
}
return v.scalar
}
-// valueAsString produces a debug string value.
+// ToString returns the uint64 value for a ValueString.
//
-// This isn't just Value.String because we may want to use that to store
-// string values in the future.
-func valueAsString(v Value) string {
+// Panics if this Value's Kind is not ValueString.
+func (v Value) ToString() string {
+ if v.kind != ValueString {
+ panic("ToString called on Value of a different Kind")
+ }
+ return unsafe.String((*byte)(v.pointer), int(v.scalar))
+}
+
+func uint64Value(x uint64) Value {
+ return Value{kind: ValueUint64, scalar: x}
+}
+
+func stringValue(s string) Value {
+ return Value{kind: ValueString, scalar: uint64(len(s)), pointer: unsafe.Pointer(unsafe.StringData(s))}
+}
+
+// String returns the string representation of the value.
+func (v Value) String() string {
switch v.Kind() {
case ValueUint64:
- return fmt.Sprintf("Uint64(%d)", v.scalar)
+ return fmt.Sprintf("Value{Uint64(%d)}", v.ToUint64())
+ case ValueString:
+ return fmt.Sprintf("Value{String(%s)}", v.ToString())
}
- return "Bad"
+ return "Value{Bad}"
}