1 /*
2  * Copyright © 2020 Google, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "u_trace.h"
25 
26 #include <inttypes.h>
27 
28 #include "util/list.h"
29 #include "util/u_call_once.h"
30 #include "util/u_debug.h"
31 #include "util/u_vector.h"
32 
33 #define __NEEDS_TRACE_PRIV
34 #include "u_trace_priv.h"
35 
36 #define PAYLOAD_BUFFER_SIZE 0x100
37 #define TIMESTAMP_BUF_SIZE 0x1000
38 #define TRACES_PER_CHUNK (TIMESTAMP_BUF_SIZE / sizeof(uint64_t))
39 
40 struct u_trace_state {
41    util_once_flag once;
42    FILE *trace_file;
43    enum u_trace_type enabled_traces;
44 };
45 static struct u_trace_state u_trace_state = { .once = UTIL_ONCE_FLAG_INIT };
46 
47 #ifdef HAVE_PERFETTO
48 /**
49  * Global list of contexts, so we can defer starting the queue until
50  * perfetto tracing is started.
51  */
52 static struct list_head ctx_list = { &ctx_list, &ctx_list };
53 
54 static simple_mtx_t ctx_list_mutex = SIMPLE_MTX_INITIALIZER;
55 /* The amount of Perfetto tracers connected */
56 int _u_trace_perfetto_count;
57 #endif
58 
59 struct u_trace_payload_buf {
60    uint32_t refcount;
61 
62    uint8_t *buf;
63    uint8_t *next;
64    uint8_t *end;
65 };
66 
67 struct u_trace_event {
68    const struct u_tracepoint *tp;
69    const void *payload;
70 };
71 
72 /**
73  * A "chunk" of trace-events and corresponding timestamp buffer.  As
74  * trace events are emitted, additional trace chucks will be allocated
75  * as needed.  When u_trace_flush() is called, they are transferred
76  * from the u_trace to the u_trace_context queue.
77  */
78 struct u_trace_chunk {
79    struct list_head node;
80 
81    struct u_trace_context *utctx;
82 
83    /* The number of traces this chunk contains so far: */
84    unsigned num_traces;
85 
86    /* table of trace events: */
87    struct u_trace_event traces[TRACES_PER_CHUNK];
88 
89    /* table of driver recorded 64b timestamps, index matches index
90     * into traces table
91     */
92    void *timestamps;
93 
94    /* Array of u_trace_payload_buf referenced by traces[] elements.
95     */
96    struct u_vector payloads;
97 
98    /* Current payload buffer being written. */
99    struct u_trace_payload_buf *payload;
100 
101    struct util_queue_fence fence;
102 
103    bool last; /* this chunk is last in batch */
104    bool eof;  /* this chunk is last in frame */
105 
106    void *flush_data; /* assigned by u_trace_flush */
107 
108    /**
109     * Several chunks reference a single flush_data instance thus only
110     * one chunk should be designated to free the data.
111     */
112    bool free_flush_data;
113 };
114 
115 struct u_trace_printer {
116    void (*start)(struct u_trace_context *utctx);
117    void (*end)(struct u_trace_context *utctx);
118    void (*start_of_frame)(struct u_trace_context *utctx);
119    void (*end_of_frame)(struct u_trace_context *utctx);
120    void (*start_of_batch)(struct u_trace_context *utctx);
121    void (*end_of_batch)(struct u_trace_context *utctx);
122    void (*event)(struct u_trace_context *utctx,
123                  struct u_trace_chunk *chunk,
124                  const struct u_trace_event *evt,
125                  uint64_t ns,
126                  int32_t delta);
127 };
128 
129 static void
print_txt_start(struct u_trace_context * utctx)130 print_txt_start(struct u_trace_context *utctx)
131 {
132 }
133 
134 static void
print_txt_end_of_frame(struct u_trace_context * utctx)135 print_txt_end_of_frame(struct u_trace_context *utctx)
136 {
137    fprintf(utctx->out, "END OF FRAME %u\n", utctx->frame_nr);
138 }
139 
140 static void
print_txt_start_of_batch(struct u_trace_context * utctx)141 print_txt_start_of_batch(struct u_trace_context *utctx)
142 {
143    fprintf(utctx->out, "+----- NS -----+ +-- Δ --+  +----- MSG -----\n");
144 }
145 
146 static void
print_txt_end_of_batch(struct u_trace_context * utctx)147 print_txt_end_of_batch(struct u_trace_context *utctx)
148 {
149    uint64_t elapsed = utctx->last_time_ns - utctx->first_time_ns;
150    fprintf(utctx->out, "ELAPSED: %" PRIu64 " ns\n", elapsed);
151 }
152 
153 static void
print_txt_event(struct u_trace_context * utctx,struct u_trace_chunk * chunk,const struct u_trace_event * evt,uint64_t ns,int32_t delta)154 print_txt_event(struct u_trace_context *utctx,
155                 struct u_trace_chunk *chunk,
156                 const struct u_trace_event *evt,
157                 uint64_t ns,
158                 int32_t delta)
159 {
160    if (evt->tp->print) {
161       fprintf(utctx->out, "%016" PRIu64 " %+9d: %s: ", ns, delta,
162               evt->tp->name);
163       evt->tp->print(utctx->out, evt->payload);
164    } else {
165       fprintf(utctx->out, "%016" PRIu64 " %+9d: %s\n", ns, delta,
166               evt->tp->name);
167    }
168 }
169 
170 static struct u_trace_printer txt_printer = {
171    .start = &print_txt_start,
172    .end = &print_txt_start,
173    .start_of_frame = &print_txt_start,
174    .end_of_frame = &print_txt_end_of_frame,
175    .start_of_batch = &print_txt_start_of_batch,
176    .end_of_batch = &print_txt_end_of_batch,
177    .event = &print_txt_event,
178 };
179 
180 static void
print_json_start(struct u_trace_context * utctx)181 print_json_start(struct u_trace_context *utctx)
182 {
183    fprintf(utctx->out, "[\n");
184 }
185 
186 static void
print_json_end(struct u_trace_context * utctx)187 print_json_end(struct u_trace_context *utctx)
188 {
189    fprintf(utctx->out, "\n]");
190 }
191 
192 static void
print_json_start_of_frame(struct u_trace_context * utctx)193 print_json_start_of_frame(struct u_trace_context *utctx)
194 {
195    if (utctx->frame_nr != 0)
196       fprintf(utctx->out, ",\n");
197    fprintf(utctx->out, "{\n\"frame\": %u,\n", utctx->frame_nr);
198    fprintf(utctx->out, "\"batches\": [\n");
199 }
200 
201 static void
print_json_end_of_frame(struct u_trace_context * utctx)202 print_json_end_of_frame(struct u_trace_context *utctx)
203 {
204    fprintf(utctx->out, "]\n}\n");
205    fflush(utctx->out);
206 }
207 
208 static void
print_json_start_of_batch(struct u_trace_context * utctx)209 print_json_start_of_batch(struct u_trace_context *utctx)
210 {
211    if (utctx->batch_nr != 0)
212       fprintf(utctx->out, ",\n");
213    fprintf(utctx->out, "{\n\"events\": [\n");
214 }
215 
216 static void
print_json_end_of_batch(struct u_trace_context * utctx)217 print_json_end_of_batch(struct u_trace_context *utctx)
218 {
219    uint64_t elapsed = utctx->last_time_ns - utctx->first_time_ns;
220    fprintf(utctx->out, "],\n");
221    fprintf(utctx->out, "\"duration_ns\": %" PRIu64 "\n", elapsed);
222    fprintf(utctx->out, "}\n");
223 }
224 
225 static void
print_json_event(struct u_trace_context * utctx,struct u_trace_chunk * chunk,const struct u_trace_event * evt,uint64_t ns,int32_t delta)226 print_json_event(struct u_trace_context *utctx,
227                  struct u_trace_chunk *chunk,
228                  const struct u_trace_event *evt,
229                  uint64_t ns,
230                  int32_t delta)
231 {
232    if (utctx->event_nr != 0)
233       fprintf(utctx->out, ",\n");
234    fprintf(utctx->out, "{\n\"event\": \"%s\",\n", evt->tp->name);
235    fprintf(utctx->out, "\"time_ns\": \"%016" PRIu64 "\",\n", ns);
236    fprintf(utctx->out, "\"params\": {");
237    if (evt->tp->print)
238       evt->tp->print_json(utctx->out, evt->payload);
239    fprintf(utctx->out, "}\n}\n");
240 }
241 
242 static struct u_trace_printer json_printer = {
243    .start = print_json_start,
244    .end = print_json_end,
245    .start_of_frame = &print_json_start_of_frame,
246    .end_of_frame = &print_json_end_of_frame,
247    .start_of_batch = &print_json_start_of_batch,
248    .end_of_batch = &print_json_end_of_batch,
249    .event = &print_json_event,
250 };
251 
252 static struct u_trace_payload_buf *
u_trace_payload_buf_create(void)253 u_trace_payload_buf_create(void)
254 {
255    struct u_trace_payload_buf *payload =
256       malloc(sizeof(*payload) + PAYLOAD_BUFFER_SIZE);
257 
258    p_atomic_set(&payload->refcount, 1);
259 
260    payload->buf = (uint8_t *) (payload + 1);
261    payload->end = payload->buf + PAYLOAD_BUFFER_SIZE;
262    payload->next = payload->buf;
263 
264    return payload;
265 }
266 
267 static struct u_trace_payload_buf *
u_trace_payload_buf_ref(struct u_trace_payload_buf * payload)268 u_trace_payload_buf_ref(struct u_trace_payload_buf *payload)
269 {
270    p_atomic_inc(&payload->refcount);
271    return payload;
272 }
273 
274 static void
u_trace_payload_buf_unref(struct u_trace_payload_buf * payload)275 u_trace_payload_buf_unref(struct u_trace_payload_buf *payload)
276 {
277    if (p_atomic_dec_zero(&payload->refcount))
278       free(payload);
279 }
280 
281 static void
free_chunk(void * ptr)282 free_chunk(void *ptr)
283 {
284    struct u_trace_chunk *chunk = ptr;
285 
286    chunk->utctx->delete_timestamp_buffer(chunk->utctx, chunk->timestamps);
287 
288    /* Unref payloads attached to this chunk. */
289    struct u_trace_payload_buf **payload;
290    u_vector_foreach (payload, &chunk->payloads)
291       u_trace_payload_buf_unref(*payload);
292    u_vector_finish(&chunk->payloads);
293 
294    list_del(&chunk->node);
295    free(chunk);
296 }
297 
298 static void
free_chunks(struct list_head * chunks)299 free_chunks(struct list_head *chunks)
300 {
301    while (!list_is_empty(chunks)) {
302       struct u_trace_chunk *chunk =
303          list_first_entry(chunks, struct u_trace_chunk, node);
304       free_chunk(chunk);
305    }
306 }
307 
308 static struct u_trace_chunk *
get_chunk(struct u_trace * ut,size_t payload_size)309 get_chunk(struct u_trace *ut, size_t payload_size)
310 {
311    struct u_trace_chunk *chunk;
312 
313    assert(payload_size <= PAYLOAD_BUFFER_SIZE);
314 
315    /* do we currently have a non-full chunk to append msgs to? */
316    if (!list_is_empty(&ut->trace_chunks)) {
317       chunk = list_last_entry(&ut->trace_chunks, struct u_trace_chunk, node);
318       /* Can we store a new trace in the chunk? */
319       if (chunk->num_traces < TRACES_PER_CHUNK) {
320          /* If no payload required, nothing else to check. */
321          if (payload_size <= 0)
322             return chunk;
323 
324          /* If the payload buffer has space for the payload, we're good.
325           */
326          if (chunk->payload &&
327              (chunk->payload->end - chunk->payload->next) >= payload_size)
328             return chunk;
329 
330          /* If we don't have enough space in the payload buffer, can we
331           * allocate a new one?
332           */
333          struct u_trace_payload_buf **buf = u_vector_add(&chunk->payloads);
334          *buf = u_trace_payload_buf_create();
335          chunk->payload = *buf;
336          return chunk;
337       }
338       /* we need to expand to add another chunk to the batch, so
339        * the current one is no longer the last one of the batch:
340        */
341       chunk->last = false;
342    }
343 
344    /* .. if not, then create a new one: */
345    chunk = calloc(1, sizeof(*chunk));
346 
347    chunk->utctx = ut->utctx;
348    chunk->timestamps =
349       ut->utctx->create_timestamp_buffer(ut->utctx, TIMESTAMP_BUF_SIZE);
350    chunk->last = true;
351    u_vector_init(&chunk->payloads, 4, sizeof(struct u_trace_payload_buf *));
352    if (payload_size > 0) {
353       struct u_trace_payload_buf **buf = u_vector_add(&chunk->payloads);
354       *buf = u_trace_payload_buf_create();
355       chunk->payload = *buf;
356    }
357 
358    list_addtail(&chunk->node, &ut->trace_chunks);
359 
360    return chunk;
361 }
362 
363 static const struct debug_named_value config_control[] = {
364    { "print", U_TRACE_TYPE_PRINT, "Enable print" },
365    { "print_json", U_TRACE_TYPE_PRINT_JSON, "Enable print in JSON" },
366 #ifdef HAVE_PERFETTO
367    { "perfetto", U_TRACE_TYPE_PERFETTO_ENV, "Enable perfetto" },
368 #endif
369    { "markers", U_TRACE_TYPE_MARKERS, "Enable marker trace" },
370    DEBUG_NAMED_VALUE_END
371 };
372 
373 DEBUG_GET_ONCE_OPTION(trace_file, "MESA_GPU_TRACEFILE", NULL)
374 
375 static void
trace_file_fini(void)376 trace_file_fini(void)
377 {
378    fclose(u_trace_state.trace_file);
379    u_trace_state.trace_file = NULL;
380 }
381 
382 static void
u_trace_state_init_once(void)383 u_trace_state_init_once(void)
384 {
385    u_trace_state.enabled_traces =
386       debug_get_flags_option("MESA_GPU_TRACES", config_control, 0);
387    const char *tracefile_name = debug_get_option_trace_file();
388    if (tracefile_name && !__check_suid()) {
389       u_trace_state.trace_file = fopen(tracefile_name, "w");
390       if (u_trace_state.trace_file != NULL) {
391          atexit(trace_file_fini);
392       }
393    }
394    if (!u_trace_state.trace_file) {
395       u_trace_state.trace_file = stdout;
396    }
397 }
398 
399 void
u_trace_state_init(void)400 u_trace_state_init(void)
401 {
402    util_call_once(&u_trace_state.once, u_trace_state_init_once);
403 }
404 
405 bool
u_trace_is_enabled(enum u_trace_type type)406 u_trace_is_enabled(enum u_trace_type type)
407 {
408    /* Active is only tracked in a given u_trace context, so if you're asking
409     * us if U_TRACE_TYPE_PERFETTO (_ENV | _ACTIVE) is enabled, then just check
410     * _ENV ("perfetto tracing is desired, but perfetto might not be running").
411     */
412    type &= ~U_TRACE_TYPE_PERFETTO_ACTIVE;
413 
414    return (u_trace_state.enabled_traces & type) == type;
415 }
416 
417 static void
queue_init(struct u_trace_context * utctx)418 queue_init(struct u_trace_context *utctx)
419 {
420    if (utctx->queue.jobs)
421       return;
422 
423    bool ret = util_queue_init(
424       &utctx->queue, "traceq", 256, 1,
425       UTIL_QUEUE_INIT_USE_MINIMUM_PRIORITY | UTIL_QUEUE_INIT_RESIZE_IF_FULL,
426       NULL);
427    assert(ret);
428 
429    if (!ret)
430       utctx->out = NULL;
431 }
432 
433 void
u_trace_context_init(struct u_trace_context * utctx,void * pctx,u_trace_create_ts_buffer create_timestamp_buffer,u_trace_delete_ts_buffer delete_timestamp_buffer,u_trace_record_ts record_timestamp,u_trace_read_ts read_timestamp,u_trace_delete_flush_data delete_flush_data)434 u_trace_context_init(struct u_trace_context *utctx,
435                      void *pctx,
436                      u_trace_create_ts_buffer create_timestamp_buffer,
437                      u_trace_delete_ts_buffer delete_timestamp_buffer,
438                      u_trace_record_ts record_timestamp,
439                      u_trace_read_ts read_timestamp,
440                      u_trace_delete_flush_data delete_flush_data)
441 {
442    u_trace_state_init();
443 
444    utctx->enabled_traces = u_trace_state.enabled_traces;
445    utctx->pctx = pctx;
446    utctx->create_timestamp_buffer = create_timestamp_buffer;
447    utctx->delete_timestamp_buffer = delete_timestamp_buffer;
448    utctx->record_timestamp = record_timestamp;
449    utctx->read_timestamp = read_timestamp;
450    utctx->delete_flush_data = delete_flush_data;
451 
452    utctx->last_time_ns = 0;
453    utctx->first_time_ns = 0;
454    utctx->frame_nr = 0;
455    utctx->batch_nr = 0;
456    utctx->event_nr = 0;
457    utctx->start_of_frame = true;
458 
459    list_inithead(&utctx->flushed_trace_chunks);
460 
461    if (utctx->enabled_traces & U_TRACE_TYPE_PRINT) {
462       utctx->out = u_trace_state.trace_file;
463 
464       if (utctx->enabled_traces & U_TRACE_TYPE_JSON) {
465          utctx->out_printer = &json_printer;
466       } else {
467          utctx->out_printer = &txt_printer;
468       }
469    } else {
470       utctx->out = NULL;
471       utctx->out_printer = NULL;
472    }
473 
474 #ifdef HAVE_PERFETTO
475    simple_mtx_lock(&ctx_list_mutex);
476    list_add(&utctx->node, &ctx_list);
477    if (_u_trace_perfetto_count > 0)
478       utctx->enabled_traces |= U_TRACE_TYPE_PERFETTO_ACTIVE;
479 
480    queue_init(utctx);
481 
482    simple_mtx_unlock(&ctx_list_mutex);
483 #else
484    queue_init(utctx);
485 #endif
486 
487    if (!(p_atomic_read_relaxed(&utctx->enabled_traces) &
488          U_TRACE_TYPE_REQUIRE_QUEUING))
489       return;
490 
491    if (utctx->out) {
492       utctx->out_printer->start(utctx);
493    }
494 }
495 
496 void
u_trace_context_fini(struct u_trace_context * utctx)497 u_trace_context_fini(struct u_trace_context *utctx)
498 {
499 #ifdef HAVE_PERFETTO
500    simple_mtx_lock(&ctx_list_mutex);
501    list_del(&utctx->node);
502    simple_mtx_unlock(&ctx_list_mutex);
503 #endif
504 
505    if (utctx->out) {
506       utctx->out_printer->end(utctx);
507       fflush(utctx->out);
508    }
509 
510    if (!utctx->queue.jobs)
511       return;
512    util_queue_finish(&utctx->queue);
513    util_queue_destroy(&utctx->queue);
514    free_chunks(&utctx->flushed_trace_chunks);
515 }
516 
517 #ifdef HAVE_PERFETTO
518 void
u_trace_perfetto_start(void)519 u_trace_perfetto_start(void)
520 {
521    simple_mtx_lock(&ctx_list_mutex);
522 
523    list_for_each_entry (struct u_trace_context, utctx, &ctx_list, node) {
524       queue_init(utctx);
525       p_atomic_set(&utctx->enabled_traces,
526                    utctx->enabled_traces | U_TRACE_TYPE_PERFETTO_ACTIVE);
527    }
528 
529    _u_trace_perfetto_count++;
530 
531    simple_mtx_unlock(&ctx_list_mutex);
532 }
533 
534 void
u_trace_perfetto_stop(void)535 u_trace_perfetto_stop(void)
536 {
537    simple_mtx_lock(&ctx_list_mutex);
538 
539    assert(_u_trace_perfetto_count > 0);
540    _u_trace_perfetto_count--;
541    if (_u_trace_perfetto_count == 0) {
542       list_for_each_entry (struct u_trace_context, utctx, &ctx_list, node) {
543          p_atomic_set(&utctx->enabled_traces,
544                       utctx->enabled_traces & ~U_TRACE_TYPE_PERFETTO_ACTIVE);
545       }
546    }
547 
548    simple_mtx_unlock(&ctx_list_mutex);
549 }
550 #endif
551 
552 static void
process_chunk(void * job,void * gdata,int thread_index)553 process_chunk(void *job, void *gdata, int thread_index)
554 {
555    struct u_trace_chunk *chunk = job;
556    struct u_trace_context *utctx = chunk->utctx;
557 
558    if (utctx->start_of_frame) {
559       utctx->start_of_frame = false;
560       utctx->batch_nr = 0;
561       if (utctx->out) {
562          utctx->out_printer->start_of_frame(utctx);
563       }
564    }
565 
566    /* For first chunk of batch, accumulated times will be zerod: */
567    if (!utctx->last_time_ns) {
568       utctx->event_nr = 0;
569       if (utctx->out) {
570          utctx->out_printer->start_of_batch(utctx);
571       }
572    }
573 
574    for (unsigned idx = 0; idx < chunk->num_traces; idx++) {
575       const struct u_trace_event *evt = &chunk->traces[idx];
576 
577       if (!evt->tp)
578          continue;
579 
580       uint64_t ns = utctx->read_timestamp(utctx, chunk->timestamps, idx,
581                                           chunk->flush_data);
582       int32_t delta;
583 
584       if (!utctx->first_time_ns)
585          utctx->first_time_ns = ns;
586 
587       if (ns != U_TRACE_NO_TIMESTAMP) {
588          delta = utctx->last_time_ns ? ns - utctx->last_time_ns : 0;
589          utctx->last_time_ns = ns;
590       } else {
591          /* we skipped recording the timestamp, so it should be
592           * the same as last msg:
593           */
594          ns = utctx->last_time_ns;
595          delta = 0;
596       }
597 
598       if (utctx->out) {
599          utctx->out_printer->event(utctx, chunk, evt, ns, delta);
600       }
601 #ifdef HAVE_PERFETTO
602       if (evt->tp->perfetto &&
603           (p_atomic_read_relaxed(&utctx->enabled_traces) &
604            U_TRACE_TYPE_PERFETTO_ACTIVE)) {
605          evt->tp->perfetto(utctx->pctx, ns, chunk->flush_data, evt->payload);
606       }
607 #endif
608 
609       utctx->event_nr++;
610    }
611 
612    if (chunk->last) {
613       if (utctx->out) {
614          utctx->out_printer->end_of_batch(utctx);
615       }
616 
617       utctx->batch_nr++;
618       utctx->last_time_ns = 0;
619       utctx->first_time_ns = 0;
620    }
621 
622    if (chunk->eof) {
623       if (utctx->out) {
624          utctx->out_printer->end_of_frame(utctx);
625       }
626       utctx->frame_nr++;
627       utctx->start_of_frame = true;
628    }
629 
630    if (chunk->free_flush_data && utctx->delete_flush_data) {
631       utctx->delete_flush_data(utctx, chunk->flush_data);
632    }
633 }
634 
635 static void
cleanup_chunk(void * job,void * gdata,int thread_index)636 cleanup_chunk(void *job, void *gdata, int thread_index)
637 {
638    free_chunk(job);
639 }
640 
641 void
u_trace_context_process(struct u_trace_context * utctx,bool eof)642 u_trace_context_process(struct u_trace_context *utctx, bool eof)
643 {
644    struct list_head *chunks = &utctx->flushed_trace_chunks;
645 
646    if (list_is_empty(chunks))
647       return;
648 
649    struct u_trace_chunk *last_chunk =
650       list_last_entry(chunks, struct u_trace_chunk, node);
651    last_chunk->eof = eof;
652 
653    while (!list_is_empty(chunks)) {
654       struct u_trace_chunk *chunk =
655          list_first_entry(chunks, struct u_trace_chunk, node);
656 
657       /* remove from list before enqueuing, because chunk is freed
658        * once it is processed by the queue:
659        */
660       list_delinit(&chunk->node);
661 
662       util_queue_add_job(&utctx->queue, chunk, &chunk->fence, process_chunk,
663                          cleanup_chunk, TIMESTAMP_BUF_SIZE);
664    }
665 }
666 
667 void
u_trace_init(struct u_trace * ut,struct u_trace_context * utctx)668 u_trace_init(struct u_trace *ut, struct u_trace_context *utctx)
669 {
670    ut->utctx = utctx;
671    list_inithead(&ut->trace_chunks);
672 }
673 
674 void
u_trace_fini(struct u_trace * ut)675 u_trace_fini(struct u_trace *ut)
676 {
677    /* Normally the list of trace-chunks would be empty, if they
678     * have been flushed to the trace-context.
679     */
680    free_chunks(&ut->trace_chunks);
681 }
682 
683 bool
u_trace_has_points(struct u_trace * ut)684 u_trace_has_points(struct u_trace *ut)
685 {
686    return !list_is_empty(&ut->trace_chunks);
687 }
688 
689 struct u_trace_iterator
u_trace_begin_iterator(struct u_trace * ut)690 u_trace_begin_iterator(struct u_trace *ut)
691 {
692    if (list_is_empty(&ut->trace_chunks))
693       return (struct u_trace_iterator) { ut, NULL, 0 };
694 
695    struct u_trace_chunk *first_chunk =
696       list_first_entry(&ut->trace_chunks, struct u_trace_chunk, node);
697 
698    return (struct u_trace_iterator) { ut, first_chunk, 0 };
699 }
700 
701 struct u_trace_iterator
u_trace_end_iterator(struct u_trace * ut)702 u_trace_end_iterator(struct u_trace *ut)
703 {
704    if (list_is_empty(&ut->trace_chunks))
705       return (struct u_trace_iterator) { ut, NULL, 0 };
706 
707    struct u_trace_chunk *last_chunk =
708       list_last_entry(&ut->trace_chunks, struct u_trace_chunk, node);
709 
710    return (struct u_trace_iterator) { ut, last_chunk,
711                                       last_chunk->num_traces };
712 }
713 
714 /* If an iterator was created when there were no chunks and there are now
715  * chunks, "sanitize" it to include the first chunk.
716  */
717 static struct u_trace_iterator
sanitize_iterator(struct u_trace_iterator iter)718 sanitize_iterator(struct u_trace_iterator iter)
719 {
720    if (iter.ut && !iter.chunk && !list_is_empty(&iter.ut->trace_chunks)) {
721       iter.chunk =
722          list_first_entry(&iter.ut->trace_chunks, struct u_trace_chunk, node);
723    }
724 
725    return iter;
726 }
727 
728 bool
u_trace_iterator_equal(struct u_trace_iterator a,struct u_trace_iterator b)729 u_trace_iterator_equal(struct u_trace_iterator a, struct u_trace_iterator b)
730 {
731    a = sanitize_iterator(a);
732    b = sanitize_iterator(b);
733    return a.ut == b.ut && a.chunk == b.chunk && a.event_idx == b.event_idx;
734 }
735 
736 void
u_trace_clone_append(struct u_trace_iterator begin_it,struct u_trace_iterator end_it,struct u_trace * into,void * cmdstream,u_trace_copy_ts_buffer copy_ts_buffer)737 u_trace_clone_append(struct u_trace_iterator begin_it,
738                      struct u_trace_iterator end_it,
739                      struct u_trace *into,
740                      void *cmdstream,
741                      u_trace_copy_ts_buffer copy_ts_buffer)
742 {
743    begin_it = sanitize_iterator(begin_it);
744    end_it = sanitize_iterator(end_it);
745 
746    struct u_trace_chunk *from_chunk = begin_it.chunk;
747    uint32_t from_idx = begin_it.event_idx;
748 
749    while (from_chunk != end_it.chunk || from_idx != end_it.event_idx) {
750       struct u_trace_chunk *to_chunk = get_chunk(into, 0 /* payload_size */);
751 
752       unsigned to_copy = MIN2(TRACES_PER_CHUNK - to_chunk->num_traces,
753                               from_chunk->num_traces - from_idx);
754       if (from_chunk == end_it.chunk)
755          to_copy = MIN2(to_copy, end_it.event_idx - from_idx);
756 
757       copy_ts_buffer(begin_it.ut->utctx, cmdstream, from_chunk->timestamps,
758                      from_idx, to_chunk->timestamps, to_chunk->num_traces,
759                      to_copy);
760 
761       memcpy(&to_chunk->traces[to_chunk->num_traces],
762              &from_chunk->traces[from_idx],
763              to_copy * sizeof(struct u_trace_event));
764 
765       /* Take a refcount on payloads from from_chunk if needed. */
766       if (begin_it.ut != into) {
767          struct u_trace_payload_buf **in_payload;
768          u_vector_foreach (in_payload, &from_chunk->payloads) {
769             struct u_trace_payload_buf **out_payload =
770                u_vector_add(&to_chunk->payloads);
771 
772             *out_payload = u_trace_payload_buf_ref(*in_payload);
773          }
774       }
775 
776       to_chunk->num_traces += to_copy;
777       from_idx += to_copy;
778 
779       assert(from_idx <= from_chunk->num_traces);
780       if (from_idx == from_chunk->num_traces) {
781          if (from_chunk == end_it.chunk)
782             break;
783 
784          from_idx = 0;
785          from_chunk =
786             list_entry(from_chunk->node.next, struct u_trace_chunk, node);
787       }
788    }
789 }
790 
791 void
u_trace_disable_event_range(struct u_trace_iterator begin_it,struct u_trace_iterator end_it)792 u_trace_disable_event_range(struct u_trace_iterator begin_it,
793                             struct u_trace_iterator end_it)
794 {
795    begin_it = sanitize_iterator(begin_it);
796    end_it = sanitize_iterator(end_it);
797 
798    struct u_trace_chunk *current_chunk = begin_it.chunk;
799    uint32_t start_idx = begin_it.event_idx;
800 
801    while (current_chunk != end_it.chunk) {
802       memset(&current_chunk->traces[start_idx], 0,
803              (current_chunk->num_traces - start_idx) *
804                 sizeof(struct u_trace_event));
805       start_idx = 0;
806       current_chunk =
807          list_entry(current_chunk->node.next, struct u_trace_chunk, node);
808    }
809 
810    memset(&current_chunk->traces[start_idx], 0,
811           (end_it.event_idx - start_idx) * sizeof(struct u_trace_event));
812 }
813 
814 /**
815  * Append a trace event, returning pointer to buffer of tp->payload_sz
816  * to be filled in with trace payload.  Called by generated tracepoint
817  * functions.
818  */
819 void *
u_trace_appendv(struct u_trace * ut,void * cs,const struct u_tracepoint * tp,unsigned variable_sz)820 u_trace_appendv(struct u_trace *ut,
821                 void *cs,
822                 const struct u_tracepoint *tp,
823                 unsigned variable_sz)
824 {
825    assert(tp->payload_sz == ALIGN_NPOT(tp->payload_sz, 8));
826 
827    unsigned payload_sz = ALIGN_NPOT(tp->payload_sz + variable_sz, 8);
828    struct u_trace_chunk *chunk = get_chunk(ut, payload_sz);
829    unsigned tp_idx = chunk->num_traces++;
830 
831    /* sub-allocate storage for trace payload: */
832    void *payload = NULL;
833    if (payload_sz > 0) {
834       payload = chunk->payload->next;
835       chunk->payload->next += payload_sz;
836    }
837 
838    /* record a timestamp for the trace: */
839    ut->utctx->record_timestamp(ut, cs, chunk->timestamps, tp_idx,
840                                tp->end_of_pipe);
841 
842    chunk->traces[tp_idx] = (struct u_trace_event) {
843       .tp = tp,
844       .payload = payload,
845    };
846 
847    return payload;
848 }
849 
850 void
u_trace_flush(struct u_trace * ut,void * flush_data,bool free_data)851 u_trace_flush(struct u_trace *ut, void *flush_data, bool free_data)
852 {
853    list_for_each_entry (struct u_trace_chunk, chunk, &ut->trace_chunks,
854                         node) {
855       chunk->flush_data = flush_data;
856       chunk->free_flush_data = false;
857    }
858 
859    if (free_data && !list_is_empty(&ut->trace_chunks)) {
860       struct u_trace_chunk *last_chunk =
861          list_last_entry(&ut->trace_chunks, struct u_trace_chunk, node);
862       last_chunk->free_flush_data = true;
863    }
864 
865    /* transfer batch's log chunks to context: */
866    list_splicetail(&ut->trace_chunks, &ut->utctx->flushed_trace_chunks);
867    list_inithead(&ut->trace_chunks);
868 }
869