Skip to content

Commit 924f28a

Browse files
committed
pytest: add test hooks so we can test tracing.
Suggested-by: Christian Decker Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent c69a636 commit 924f28a

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

common/trace.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "config.h"
22
#include <assert.h>
3+
#include <ccan/err/err.h>
34
#include <ccan/htable/htable.h>
45
#include <ccan/str/hex/hex.h>
56
#include <ccan/tal/str/str.h>
@@ -8,6 +9,7 @@
89
#include <common/json_stream.h>
910
#include <common/memleak.h>
1011
#include <common/trace.h>
12+
#include <fcntl.h>
1113
#include <sodium/randombytes.h>
1214
#include <stdio.h>
1315
#include <unistd.h>
@@ -42,6 +44,7 @@
4244

4345
const char *trace_service_name = "lightningd";
4446
static bool disable_trace = false;
47+
static FILE *trace_to_file = NULL;
4548

4649
struct span_tag {
4750
char *name, *value;
@@ -151,11 +154,20 @@ static inline void trace_check_tree(void) {}
151154

152155
static void trace_init(void)
153156
{
157+
const char *dev_trace_file;
154158
if (active_spans)
155159
return;
156160
active_spans = calloc(MAX_ACTIVE_SPANS, sizeof(struct span));
157161

158162
current = NULL;
163+
dev_trace_file = getenv("CLN_DEV_TRACE_FILE");
164+
if (dev_trace_file) {
165+
const char *fname = tal_fmt(tmpctx, "%s.%u",
166+
dev_trace_file, (unsigned)getpid());
167+
trace_to_file = fopen(fname, "a+");
168+
if (!trace_to_file)
169+
err(1, "Opening CLN_DEV_TRACE_FILE %s", fname);
170+
}
159171
trace_inject_traceparent();
160172
}
161173

@@ -250,6 +262,10 @@ static void trace_emit(struct span *s)
250262

251263
tal_append_fmt(&res, "}, \"traceId\": \"%s\"}]", trace_id);
252264
DTRACE_PROBE2(lightningd, span_emit, span_id, res);
265+
if (trace_to_file) {
266+
fprintf(trace_to_file, "span_emit %s %s\n", span_id, res);
267+
fflush(trace_to_file);
268+
}
253269
tal_free(res);
254270
}
255271

@@ -301,6 +317,12 @@ void trace_span_start(const char *name, const void *key)
301317
current = s;
302318
trace_check_tree();
303319
DTRACE_PROBE1(lightningd, span_start, s->id);
320+
if (trace_to_file) {
321+
char span_id[HEX_SPAN_ID_SIZE];
322+
hex_encode(s->id, SPAN_ID_SIZE, span_id, HEX_SPAN_ID_SIZE);
323+
fprintf(trace_to_file, "span_start %s\n", span_id);
324+
fflush(trace_to_file);
325+
}
304326
}
305327

306328
void trace_span_remote(u8 trace_id[TRACE_ID_SIZE], u8 span_id[SPAN_ID_SIZE])
@@ -323,6 +345,12 @@ void trace_span_end(const void *key)
323345
struct timeabs now = time_now();
324346
s->end_time = (now.ts.tv_sec * 1000000) + now.ts.tv_nsec / 1000;
325347
DTRACE_PROBE1(lightningd, span_end, s->id);
348+
if (trace_to_file) {
349+
char span_id[HEX_SPAN_ID_SIZE];
350+
hex_encode(s->id, SPAN_ID_SIZE, span_id, HEX_SPAN_ID_SIZE);
351+
fprintf(trace_to_file, "span_end %s\n", span_id);
352+
fflush(trace_to_file);
353+
}
326354
trace_emit(s);
327355

328356
/* Reset the context span we are in. */
@@ -375,6 +403,12 @@ void trace_span_suspend_(const void *key, const char *lbl)
375403
span->suspended = true;
376404
current = NULL;
377405
DTRACE_PROBE1(lightningd, span_suspend, span->id);
406+
if (trace_to_file) {
407+
char span_id[HEX_SPAN_ID_SIZE];
408+
hex_encode(span->id, SPAN_ID_SIZE, span_id, HEX_SPAN_ID_SIZE);
409+
fprintf(trace_to_file, "span_suspend %s\n", span_id);
410+
fflush(trace_to_file);
411+
}
378412
}
379413

380414
static void destroy_trace_span(const void *key)
@@ -387,6 +421,8 @@ static void destroy_trace_span(const void *key)
387421
return;
388422

389423
/* Otherwise resume so we can terminate it */
424+
if (trace_to_file)
425+
fprintf(trace_to_file, "destroying span\n");
390426
trace_span_resume(key);
391427
trace_span_end(key);
392428
}
@@ -410,6 +446,12 @@ void trace_span_resume_(const void *key, const char *lbl)
410446
current->suspended = false;
411447
TRACE_DBG("Resuming span %s (%zu)\n", current->name, current->key);
412448
DTRACE_PROBE1(lightningd, span_resume, current->id);
449+
if (trace_to_file) {
450+
char span_id[HEX_SPAN_ID_SIZE];
451+
hex_encode(current->id, SPAN_ID_SIZE, span_id, HEX_SPAN_ID_SIZE);
452+
fprintf(trace_to_file, "span_resume %s\n", span_id);
453+
fflush(trace_to_file);
454+
}
413455
}
414456

415457
void trace_cleanup(void)

tests/test_misc.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
)
1414

1515
import copy
16+
import glob
1617
import json
1718
import os
1819
import pytest
@@ -4697,3 +4698,56 @@ def test_bolt12_invoice_decode(node_factory):
46974698

46984699
assert l1.rpc.decode(inv)['valid'] is True
46994700
subprocess.run(["devtools/bolt12-cli", "decode", inv], check=True)
4701+
4702+
4703+
@unittest.skipIf(env('HAVE_USDT') != '1', "Test requires tracing compiled in")
4704+
def test_tracing(node_factory):
4705+
l1 = node_factory.get_node(start=False)
4706+
trace_fnamebase = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "l1.trace")
4707+
l1.daemon.env["CLN_DEV_TRACE_FILE"] = trace_fnamebase
4708+
l1.start()
4709+
l1.stop()
4710+
4711+
traces = set()
4712+
suspended = set()
4713+
for fname in glob.glob(f"{trace_fnamebase}.*"):
4714+
for linenum, l in enumerate(open(fname, "rt").readlines(), 1):
4715+
# In case an assertion fails
4716+
print(f"Parsing {fname}:{linenum}")
4717+
parts = l.split(maxsplit=2)
4718+
cmd = parts[0]
4719+
spanid = parts[1]
4720+
if cmd == 'span_emit':
4721+
assert spanid in traces
4722+
assert spanid not in suspended
4723+
# Should be valid JSON
4724+
res = json.loads(parts[2])
4725+
4726+
# This is an array for some reason
4727+
assert len(res) == 1
4728+
res = res[0]
4729+
assert res['id'] == spanid
4730+
assert res['localEndpoint'] == {"serviceName": "lightningd"}
4731+
expected_keys = ['id', 'name', 'timestamp', 'duration', 'tags', 'traceId', 'localEndpoint']
4732+
if 'parentId' in res:
4733+
assert res['parentId'] in traces
4734+
expected_keys.append('parentId')
4735+
assert set(res.keys()) == set(expected_keys)
4736+
traces.remove(spanid)
4737+
elif cmd == 'span_end':
4738+
assert spanid in traces
4739+
elif cmd == 'span_start':
4740+
assert spanid not in traces
4741+
traces.add(spanid)
4742+
elif cmd == 'span_suspend':
4743+
assert spanid in traces
4744+
assert spanid not in suspended
4745+
suspended.add(spanid)
4746+
elif cmd == 'span_resume':
4747+
assert spanid in traces
4748+
suspended.remove(spanid)
4749+
else:
4750+
assert False, "Unknown trace line"
4751+
4752+
assert suspended == set()
4753+
assert traces == set()

0 commit comments

Comments
 (0)