Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 1 | package vtrace_test |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 7 | iipc "veyron.io/veyron/veyron/runtimes/google/ipc" |
| 8 | "veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager" |
| 9 | tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming" |
| 10 | truntime "veyron.io/veyron/veyron/runtimes/google/testing/mocks/runtime" |
| 11 | ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace" |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 12 | |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 13 | "veyron.io/veyron/veyron2/context" |
| 14 | "veyron.io/veyron/veyron2/ipc" |
| 15 | "veyron.io/veyron/veyron2/ipc/stream" |
| 16 | "veyron.io/veyron/veyron2/naming" |
| 17 | "veyron.io/veyron/veyron2/security" |
| 18 | "veyron.io/veyron/veyron2/vlog" |
| 19 | "veyron.io/veyron/veyron2/vtrace" |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 20 | ) |
| 21 | |
| 22 | // We need a special way to create contexts for tests. We |
| 23 | // can't create a real runtime in the runtime implementation |
| 24 | // so we use a fake one that panics if used. The runtime |
| 25 | // implementation should not ever use the Runtime from a context. |
| 26 | func testContext() context.T { |
| 27 | return iipc.InternalNewContext(&truntime.PanicRuntime{}) |
| 28 | } |
| 29 | |
| 30 | func TestNewFromContext(t *testing.T) { |
| 31 | c0 := testContext() |
| 32 | c1, s1 := ivtrace.WithNewSpan(c0, "s1") |
| 33 | c2, s2 := ivtrace.WithNewSpan(c1, "s2") |
| 34 | c3, s3 := ivtrace.WithNewSpan(c2, "s3") |
| 35 | expected := map[context.T]vtrace.Span{ |
| 36 | c0: nil, |
| 37 | c1: s1, |
| 38 | c2: s2, |
| 39 | c3: s3, |
| 40 | } |
| 41 | for ctx, expectedSpan := range expected { |
| 42 | if s := ivtrace.FromContext(ctx); s != expectedSpan { |
| 43 | t.Errorf("Wrong span for ctx %v. Got %v, want %v", c0, s, expectedSpan) |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | type fakeAuthorizer int |
| 49 | |
| 50 | func (fakeAuthorizer) Authorize(security.Context) error { |
| 51 | return nil |
| 52 | } |
| 53 | |
| 54 | type testServer struct { |
| 55 | sm stream.Manager |
| 56 | ns naming.Namespace |
| 57 | name string |
| 58 | child string |
| 59 | stop func() error |
| 60 | forceCollect bool |
| 61 | } |
| 62 | |
Todd Wang | 1fe7cdd | 2014-11-12 12:51:49 -0800 | [diff] [blame] | 63 | func (c *testServer) Run(ctx ipc.ServerContext) error { |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 64 | if c.forceCollect { |
| 65 | ivtrace.FromContext(ctx).Trace().ForceCollect() |
| 66 | } |
| 67 | |
| 68 | client, err := iipc.InternalNewClient(c.sm, c.ns) |
| 69 | if err != nil { |
| 70 | vlog.Error(err) |
| 71 | return err |
| 72 | } |
| 73 | |
| 74 | ivtrace.FromContext(ctx).Annotate(c.name + "-begin") |
| 75 | |
| 76 | if c.child != "" { |
| 77 | var call ipc.Call |
| 78 | if call, err = client.StartCall(ctx, c.child, "Run", []interface{}{}); err != nil { |
| 79 | vlog.Error(err) |
| 80 | return err |
| 81 | } |
| 82 | var outerr error |
| 83 | if err = call.Finish(&outerr); err != nil { |
| 84 | vlog.Error(err) |
| 85 | return err |
| 86 | } |
| 87 | if outerr != nil { |
| 88 | vlog.Error(outerr) |
| 89 | return outerr |
| 90 | } |
| 91 | } |
| 92 | ivtrace.FromContext(ctx).Annotate(c.name + "-end") |
| 93 | |
| 94 | return nil |
| 95 | } |
| 96 | |
| 97 | func makeTestServer(ns naming.Namespace, name, child string, forceCollect bool) (*testServer, error) { |
| 98 | sm := manager.InternalNew(naming.FixedRoutingID(0x111111111)) |
| 99 | ctx := testContext() |
Matt Rosencrantz | 3e76f28 | 2014-11-10 09:38:57 -0800 | [diff] [blame] | 100 | s, err := iipc.InternalNewServer(ctx, sm, ns, nil) |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 101 | if err != nil { |
| 102 | return nil, err |
| 103 | } |
Cosmos Nicolaou | f8d4c2b | 2014-10-23 22:36:38 -0700 | [diff] [blame] | 104 | if _, err := s.Listen(ipc.ListenSpec{Protocol: "tcp", Address: "127.0.0.1:0"}); err != nil { |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 105 | return nil, err |
| 106 | } |
| 107 | |
| 108 | c := &testServer{ |
| 109 | sm: sm, |
| 110 | ns: ns, |
| 111 | name: name, |
| 112 | child: child, |
| 113 | stop: s.Stop, |
| 114 | forceCollect: forceCollect, |
| 115 | } |
| 116 | |
Cosmos Nicolaou | 92dba58 | 2014-11-05 17:24:10 -0800 | [diff] [blame] | 117 | if err := s.Serve(name, c, fakeAuthorizer(0)); err != nil { |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 118 | return nil, err |
| 119 | } |
| 120 | |
| 121 | return c, nil |
| 122 | } |
| 123 | |
| 124 | func summary(span *vtrace.SpanRecord) string { |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 125 | summary := span.Name |
| 126 | if len(span.Annotations) > 0 { |
| 127 | msgs := []string{} |
| 128 | for _, annotation := range span.Annotations { |
| 129 | msgs = append(msgs, annotation.Message) |
| 130 | } |
| 131 | summary += ": " + strings.Join(msgs, ", ") |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 132 | } |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 133 | return summary |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 134 | } |
| 135 | |
| 136 | func expectSequence(t *testing.T, trace vtrace.TraceRecord, expectedSpans []string) { |
| 137 | if got, want := len(trace.Spans), len(expectedSpans); got != want { |
| 138 | t.Errorf("Found %d spans, want %d", got, want) |
| 139 | } |
| 140 | |
| 141 | spans := map[string]*vtrace.SpanRecord{} |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 142 | summaries := []string{} |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 143 | for i := range trace.Spans { |
| 144 | span := &trace.Spans[i] |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 145 | |
| 146 | // All spans should have a start. |
| 147 | if span.Start == 0 { |
| 148 | t.Errorf("span missing start: %#v", span) |
| 149 | } |
| 150 | // All spans except the root should have an end. |
| 151 | if span.Name != "" && span.End == 0 { |
| 152 | t.Errorf("span missing end: %#v", span) |
| 153 | if span.Start >= span.End { |
| 154 | t.Errorf("span end should be after start: %#v", span) |
| 155 | } |
| 156 | } |
| 157 | |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 158 | summary := summary(span) |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 159 | summaries = append(summaries, summary) |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 160 | spans[summary] = span |
| 161 | } |
| 162 | |
| 163 | for i := range expectedSpans { |
| 164 | child, ok := spans[expectedSpans[i]] |
| 165 | if !ok { |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 166 | t.Errorf("expected span %s not found in %#v", expectedSpans[i], summaries) |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 167 | continue |
| 168 | } |
| 169 | if i == 0 { |
| 170 | continue |
| 171 | } |
| 172 | parent, ok := spans[expectedSpans[i-1]] |
| 173 | if !ok { |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 174 | t.Errorf("expected span %s not found in %#v", expectedSpans[i-1], summaries) |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 175 | continue |
| 176 | } |
| 177 | if child.Parent != parent.ID { |
| 178 | t.Errorf("%v should be a child of %v, but it's not.", child, parent) |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | func runCallChain(t *testing.T, ctx context.T, force1, force2 bool) { |
| 184 | sm := manager.InternalNew(naming.FixedRoutingID(0x555555555)) |
| 185 | ns := tnaming.NewSimpleNamespace() |
| 186 | |
| 187 | client, err := iipc.InternalNewClient(sm, ns) |
| 188 | if err != nil { |
| 189 | t.Error(err) |
| 190 | } |
| 191 | |
| 192 | c1, err := makeTestServer(ns, "c1", "c2", force1) |
| 193 | if err != nil { |
| 194 | t.Fatal("Can't start server:", err) |
| 195 | } |
| 196 | defer c1.stop() |
| 197 | |
| 198 | c2, err := makeTestServer(ns, "c2", "", force2) |
| 199 | if err != nil { |
| 200 | t.Fatal("Can't start server:", err) |
| 201 | } |
| 202 | defer c2.stop() |
| 203 | |
| 204 | call, err := client.StartCall(ctx, "c1", "Run", []interface{}{}) |
| 205 | if err != nil { |
| 206 | t.Fatal("can't call: ", err) |
| 207 | } |
| 208 | var outerr error |
| 209 | if err = call.Finish(&outerr); err != nil { |
| 210 | t.Error(err) |
| 211 | } |
| 212 | if outerr != nil { |
| 213 | t.Error(outerr) |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | // TestCancellationPropagation tests that cancellation propogates along an |
| 218 | // RPC call chain without user intervention. |
| 219 | func TestTraceAcrossRPCs(t *testing.T) { |
Mike Burrows | b28d7e1 | 2014-10-14 11:09:01 -0700 | [diff] [blame] | 220 | ctx, span := ivtrace.WithNewSpan(testContext(), "") |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 221 | span.Trace().ForceCollect() |
| 222 | span.Annotate("c0-begin") |
| 223 | |
| 224 | runCallChain(t, ctx, false, false) |
| 225 | |
| 226 | span.Annotate("c0-end") |
| 227 | |
| 228 | expectedSpans := []string{ |
Matt Rosencrantz | 1fa3277 | 2014-10-28 11:31:46 -0700 | [diff] [blame] | 229 | ": c0-begin, c0-end", |
Matt Rosencrantz | 3197d6c | 2014-11-06 09:53:22 -0800 | [diff] [blame] | 230 | "<client>\"c1\".Run", |
| 231 | "\"\".Run: c1-begin, c1-end", |
| 232 | "<client>\"c2\".Run", |
| 233 | "\"\".Run: c2-begin, c2-end", |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 234 | } |
| 235 | expectSequence(t, span.Trace().Record(), expectedSpans) |
| 236 | } |
| 237 | |
| 238 | // TestCancellationPropagationLateForce tests that cancellation propogates along an |
| 239 | // RPC call chain when tracing is initiated by someone deep in the call chain. |
| 240 | func TestTraceAcrossRPCsLateForce(t *testing.T) { |
Mike Burrows | b28d7e1 | 2014-10-14 11:09:01 -0700 | [diff] [blame] | 241 | ctx, span := ivtrace.WithNewSpan(testContext(), "") |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 242 | span.Annotate("c0-begin") |
| 243 | |
| 244 | runCallChain(t, ctx, false, true) |
| 245 | |
| 246 | span.Annotate("c0-end") |
| 247 | |
| 248 | expectedSpans := []string{ |
Mike Burrows | b28d7e1 | 2014-10-14 11:09:01 -0700 | [diff] [blame] | 249 | ": c0-end", |
Matt Rosencrantz | 3197d6c | 2014-11-06 09:53:22 -0800 | [diff] [blame] | 250 | "<client>\"c1\".Run", |
| 251 | "\"\".Run: c1-end", |
| 252 | "<client>\"c2\".Run", |
| 253 | "\"\".Run: c2-begin, c2-end", |
Matt Rosencrantz | 9fe6082 | 2014-09-12 10:09:53 -0700 | [diff] [blame] | 254 | } |
| 255 | expectSequence(t, span.Trace().Record(), expectedSpans) |
| 256 | } |