xref: /aosp_15_r20/external/mesa3d/src/util/perf/u_trace.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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    /* table of indirect data captured by u_trace
95     */
96    void *indirects;
97 
98    /* Array of u_trace_payload_buf referenced by traces[] elements.
99     */
100    struct u_vector payloads;
101 
102    /* Current payload buffer being written. */
103    struct u_trace_payload_buf *payload;
104 
105    struct util_queue_fence fence;
106 
107    bool has_indirect;
108    bool last; /* this chunk is last in batch */
109    bool eof;  /* this chunk is last in frame, unless frame_nr is set */
110    uint32_t frame_nr; /* frame idx from the driver */
111 
112    void *flush_data; /* assigned by u_trace_flush */
113 
114    /**
115     * Several chunks reference a single flush_data instance thus only
116     * one chunk should be designated to free the data.
117     */
118    bool free_flush_data;
119 };
120 
121 struct u_trace_printer {
122    void (*start)(struct u_trace_context *utctx);
123    void (*end)(struct u_trace_context *utctx);
124    void (*start_of_frame)(struct u_trace_context *utctx);
125    void (*end_of_frame)(struct u_trace_context *utctx);
126    void (*start_of_batch)(struct u_trace_context *utctx);
127    void (*end_of_batch)(struct u_trace_context *utctx);
128    void (*event)(struct u_trace_context *utctx,
129                  struct u_trace_chunk *chunk,
130                  const struct u_trace_event *evt,
131                  uint64_t ns,
132                  int32_t delta,
133                  const void *indirect);
134 };
135 
136 static void
print_txt_start(struct u_trace_context * utctx)137 print_txt_start(struct u_trace_context *utctx)
138 {
139 }
140 
141 static void
print_txt_end_of_frame(struct u_trace_context * utctx)142 print_txt_end_of_frame(struct u_trace_context *utctx)
143 {
144    fprintf(utctx->out, "END OF FRAME %u\n", utctx->frame_nr);
145 }
146 
147 static void
print_txt_start_of_batch(struct u_trace_context * utctx)148 print_txt_start_of_batch(struct u_trace_context *utctx)
149 {
150    fprintf(utctx->out, "+----- NS -----+ +-- Δ --+  +----- MSG -----\n");
151 }
152 
153 static void
print_txt_end_of_batch(struct u_trace_context * utctx)154 print_txt_end_of_batch(struct u_trace_context *utctx)
155 {
156    uint64_t elapsed = utctx->last_time_ns - utctx->first_time_ns;
157    fprintf(utctx->out, "ELAPSED: %" PRIu64 " ns\n", elapsed);
158 }
159 
160 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,const void * indirect)161 print_txt_event(struct u_trace_context *utctx,
162                 struct u_trace_chunk *chunk,
163                 const struct u_trace_event *evt,
164                 uint64_t ns,
165                 int32_t delta,
166                 const void *indirect)
167 {
168    if (evt->tp->print) {
169       fprintf(utctx->out, "%016" PRIu64 " %+9d: %s: ", ns, delta,
170               evt->tp->name);
171       evt->tp->print(utctx->out, evt->payload, indirect);
172    } else {
173       fprintf(utctx->out, "%016" PRIu64 " %+9d: %s\n", ns, delta,
174               evt->tp->name);
175    }
176 }
177 
178 static struct u_trace_printer txt_printer = {
179    .start = &print_txt_start,
180    .end = &print_txt_start,
181    .start_of_frame = &print_txt_start,
182    .end_of_frame = &print_txt_end_of_frame,
183    .start_of_batch = &print_txt_start_of_batch,
184    .end_of_batch = &print_txt_end_of_batch,
185    .event = &print_txt_event,
186 };
187 
188 static void
print_csv_start(struct u_trace_context * utctx)189 print_csv_start(struct u_trace_context *utctx)
190 {
191    fprintf(utctx->out, "frame,batch,time_ns,event,\n");
192 }
193 
194 static void
print_csv_end(struct u_trace_context * utctx)195 print_csv_end(struct u_trace_context *utctx)
196 {
197    fprintf(utctx->out, "\n");
198 }
199 
200 static void
print_csv_start_of_frame(struct u_trace_context * utctx)201 print_csv_start_of_frame(struct u_trace_context *utctx)
202 {
203 }
204 
205 static void
print_csv_end_of_frame(struct u_trace_context * utctx)206 print_csv_end_of_frame(struct u_trace_context *utctx)
207 {
208 }
209 
210 static void
print_csv_start_of_batch(struct u_trace_context * utctx)211 print_csv_start_of_batch(struct u_trace_context *utctx)
212 {
213 }
214 
215 static void
print_csv_end_of_batch(struct u_trace_context * utctx)216 print_csv_end_of_batch(struct u_trace_context *utctx)
217 {
218 }
219 
220 static void
print_csv_event(struct u_trace_context * utctx,struct u_trace_chunk * chunk,const struct u_trace_event * evt,uint64_t ns,int32_t delta,const void * indirect)221 print_csv_event(struct u_trace_context *utctx,
222                 struct u_trace_chunk *chunk,
223                 const struct u_trace_event *evt,
224                 uint64_t ns,
225                 int32_t delta,
226                 const void *indirect)
227 {
228    fprintf(utctx->out, "%u,%u,%"PRIu64",%s,\n",
229            utctx->frame_nr, utctx->batch_nr, ns, evt->tp->name);
230 }
231 
232 static struct u_trace_printer csv_printer = {
233    .start = print_csv_start,
234    .end = print_csv_end,
235    .start_of_frame = &print_csv_start_of_frame,
236    .end_of_frame = &print_csv_end_of_frame,
237    .start_of_batch = &print_csv_start_of_batch,
238    .end_of_batch = &print_csv_end_of_batch,
239    .event = &print_csv_event,
240 };
241 
242 static void
print_json_start(struct u_trace_context * utctx)243 print_json_start(struct u_trace_context *utctx)
244 {
245    fprintf(utctx->out, "[\n");
246 }
247 
248 static void
print_json_end(struct u_trace_context * utctx)249 print_json_end(struct u_trace_context *utctx)
250 {
251    fprintf(utctx->out, "\n]");
252 }
253 
254 static void
print_json_start_of_frame(struct u_trace_context * utctx)255 print_json_start_of_frame(struct u_trace_context *utctx)
256 {
257    if (utctx->frame_nr != 0)
258       fprintf(utctx->out, ",\n");
259    fprintf(utctx->out, "{\n\"frame\": %u,\n", utctx->frame_nr);
260    fprintf(utctx->out, "\"batches\": [\n");
261 }
262 
263 static void
print_json_end_of_frame(struct u_trace_context * utctx)264 print_json_end_of_frame(struct u_trace_context *utctx)
265 {
266    fprintf(utctx->out, "]\n}\n");
267    fflush(utctx->out);
268 }
269 
270 static void
print_json_start_of_batch(struct u_trace_context * utctx)271 print_json_start_of_batch(struct u_trace_context *utctx)
272 {
273    if (utctx->batch_nr != 0)
274       fprintf(utctx->out, ",\n");
275    fprintf(utctx->out, "{\n\"events\": [\n");
276 }
277 
278 static void
print_json_end_of_batch(struct u_trace_context * utctx)279 print_json_end_of_batch(struct u_trace_context *utctx)
280 {
281    uint64_t elapsed = utctx->last_time_ns - utctx->first_time_ns;
282    fprintf(utctx->out, "],\n");
283    fprintf(utctx->out, "\"duration_ns\": %" PRIu64 "\n", elapsed);
284    fprintf(utctx->out, "}\n");
285 }
286 
287 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,const void * indirect)288 print_json_event(struct u_trace_context *utctx,
289                  struct u_trace_chunk *chunk,
290                  const struct u_trace_event *evt,
291                  uint64_t ns,
292                  int32_t delta,
293                  const void *indirect)
294 {
295    if (utctx->event_nr != 0)
296       fprintf(utctx->out, ",\n");
297    fprintf(utctx->out, "{\n\"event\": \"%s\",\n", evt->tp->name);
298    fprintf(utctx->out, "\"time_ns\": \"%016" PRIu64 "\",\n", ns);
299    fprintf(utctx->out, "\"params\": {");
300    if (evt->tp->print)
301       evt->tp->print_json(utctx->out, evt->payload, indirect);
302    fprintf(utctx->out, "}\n}\n");
303 }
304 
305 static struct u_trace_printer json_printer = {
306    .start = print_json_start,
307    .end = print_json_end,
308    .start_of_frame = &print_json_start_of_frame,
309    .end_of_frame = &print_json_end_of_frame,
310    .start_of_batch = &print_json_start_of_batch,
311    .end_of_batch = &print_json_end_of_batch,
312    .event = &print_json_event,
313 };
314 
315 static struct u_trace_payload_buf *
u_trace_payload_buf_create(void)316 u_trace_payload_buf_create(void)
317 {
318    struct u_trace_payload_buf *payload =
319       malloc(sizeof(*payload) + PAYLOAD_BUFFER_SIZE);
320 
321    p_atomic_set(&payload->refcount, 1);
322 
323    payload->buf = (uint8_t *) (payload + 1);
324    payload->end = payload->buf + PAYLOAD_BUFFER_SIZE;
325    payload->next = payload->buf;
326 
327    return payload;
328 }
329 
330 static struct u_trace_payload_buf *
u_trace_payload_buf_ref(struct u_trace_payload_buf * payload)331 u_trace_payload_buf_ref(struct u_trace_payload_buf *payload)
332 {
333    p_atomic_inc(&payload->refcount);
334    return payload;
335 }
336 
337 static void
u_trace_payload_buf_unref(struct u_trace_payload_buf * payload)338 u_trace_payload_buf_unref(struct u_trace_payload_buf *payload)
339 {
340    if (p_atomic_dec_zero(&payload->refcount))
341       free(payload);
342 }
343 
344 static void
free_chunk(void * ptr)345 free_chunk(void *ptr)
346 {
347    struct u_trace_chunk *chunk = ptr;
348 
349    chunk->utctx->delete_buffer(chunk->utctx, chunk->timestamps);
350    if (chunk->indirects)
351       chunk->utctx->delete_buffer(chunk->utctx, chunk->indirects);
352 
353    /* Unref payloads attached to this chunk. */
354    struct u_trace_payload_buf **payload;
355    u_vector_foreach (payload, &chunk->payloads)
356       u_trace_payload_buf_unref(*payload);
357    u_vector_finish(&chunk->payloads);
358 
359    list_del(&chunk->node);
360    free(chunk);
361 }
362 
363 static void
free_chunks(struct list_head * chunks)364 free_chunks(struct list_head *chunks)
365 {
366    while (!list_is_empty(chunks)) {
367       struct u_trace_chunk *chunk =
368          list_first_entry(chunks, struct u_trace_chunk, node);
369       free_chunk(chunk);
370    }
371 }
372 
373 static struct u_trace_chunk *
get_chunk(struct u_trace * ut,size_t payload_size)374 get_chunk(struct u_trace *ut, size_t payload_size)
375 {
376    struct u_trace_chunk *chunk;
377 
378    assert(payload_size <= PAYLOAD_BUFFER_SIZE);
379 
380    /* do we currently have a non-full chunk to append msgs to? */
381    if (!list_is_empty(&ut->trace_chunks)) {
382       chunk = list_last_entry(&ut->trace_chunks, struct u_trace_chunk, node);
383       /* Can we store a new trace in the chunk? */
384       if (chunk->num_traces < TRACES_PER_CHUNK) {
385          /* If no payload required, nothing else to check. */
386          if (payload_size <= 0)
387             return chunk;
388 
389          /* If the payload buffer has space for the payload, we're good.
390           */
391          if (chunk->payload &&
392              (chunk->payload->end - chunk->payload->next) >= payload_size)
393             return chunk;
394 
395          /* If we don't have enough space in the payload buffer, can we
396           * allocate a new one?
397           */
398          struct u_trace_payload_buf **buf = u_vector_add(&chunk->payloads);
399          *buf = u_trace_payload_buf_create();
400          chunk->payload = *buf;
401          return chunk;
402       }
403       /* we need to expand to add another chunk to the batch, so
404        * the current one is no longer the last one of the batch:
405        */
406       chunk->last = false;
407    }
408 
409    /* .. if not, then create a new one: */
410    chunk = calloc(1, sizeof(*chunk));
411 
412    chunk->utctx = ut->utctx;
413    chunk->timestamps =
414       ut->utctx->create_buffer(ut->utctx,
415                                chunk->utctx->timestamp_size_bytes * TIMESTAMP_BUF_SIZE);
416    if (chunk->utctx->max_indirect_size_bytes &&
417        (chunk->utctx->enabled_traces & U_TRACE_TYPE_INDIRECTS)) {
418       chunk->indirects =
419          ut->utctx->create_buffer(ut->utctx,
420                                   chunk->utctx->max_indirect_size_bytes * TIMESTAMP_BUF_SIZE);
421    }
422    chunk->last = true;
423    u_vector_init(&chunk->payloads, 4, sizeof(struct u_trace_payload_buf *));
424    if (payload_size > 0) {
425       struct u_trace_payload_buf **buf = u_vector_add(&chunk->payloads);
426       *buf = u_trace_payload_buf_create();
427       chunk->payload = *buf;
428    }
429 
430    list_addtail(&chunk->node, &ut->trace_chunks);
431 
432    return chunk;
433 }
434 
435 static const struct debug_named_value config_control[] = {
436    { "print", U_TRACE_TYPE_PRINT, "Enable print" },
437    { "print_csv", U_TRACE_TYPE_PRINT_CSV, "Enable print in CSV" },
438    { "print_json", U_TRACE_TYPE_PRINT_JSON, "Enable print in JSON" },
439 #ifdef HAVE_PERFETTO
440    { "perfetto", U_TRACE_TYPE_PERFETTO_ENV, "Enable perfetto" },
441 #endif
442    { "markers", U_TRACE_TYPE_MARKERS, "Enable marker trace" },
443    { "indirects", U_TRACE_TYPE_INDIRECTS, "Enable indirect data capture" },
444    DEBUG_NAMED_VALUE_END
445 };
446 
447 DEBUG_GET_ONCE_OPTION(trace_file, "MESA_GPU_TRACEFILE", NULL)
448 
449 static void
trace_file_fini(void)450 trace_file_fini(void)
451 {
452    fclose(u_trace_state.trace_file);
453    u_trace_state.trace_file = NULL;
454 }
455 
456 static void
u_trace_state_init_once(void)457 u_trace_state_init_once(void)
458 {
459    u_trace_state.enabled_traces =
460       debug_get_flags_option("MESA_GPU_TRACES", config_control, 0);
461    const char *tracefile_name = debug_get_option_trace_file();
462    if (tracefile_name && __normal_user()) {
463       u_trace_state.trace_file = fopen(tracefile_name, "w");
464       if (u_trace_state.trace_file != NULL) {
465          atexit(trace_file_fini);
466       }
467    }
468    if (!u_trace_state.trace_file) {
469       u_trace_state.trace_file = stdout;
470    }
471 }
472 
473 void
u_trace_state_init(void)474 u_trace_state_init(void)
475 {
476    util_call_once(&u_trace_state.once, u_trace_state_init_once);
477 }
478 
479 bool
u_trace_is_enabled(enum u_trace_type type)480 u_trace_is_enabled(enum u_trace_type type)
481 {
482    /* Active is only tracked in a given u_trace context, so if you're asking
483     * us if U_TRACE_TYPE_PERFETTO (_ENV | _ACTIVE) is enabled, then just check
484     * _ENV ("perfetto tracing is desired, but perfetto might not be running").
485     */
486    type &= ~U_TRACE_TYPE_PERFETTO_ACTIVE;
487 
488    return (u_trace_state.enabled_traces & type) == type;
489 }
490 
491 static void
queue_init(struct u_trace_context * utctx)492 queue_init(struct u_trace_context *utctx)
493 {
494    if (utctx->queue.jobs)
495       return;
496 
497    bool ret = util_queue_init(
498       &utctx->queue, "traceq", 256, 1,
499       UTIL_QUEUE_INIT_USE_MINIMUM_PRIORITY | UTIL_QUEUE_INIT_RESIZE_IF_FULL,
500       NULL);
501    assert(ret);
502 
503    if (!ret)
504       utctx->out = NULL;
505 }
506 
507 void
u_trace_context_init(struct u_trace_context * utctx,void * pctx,uint32_t timestamp_size_bytes,uint32_t max_indirect_size_bytes,u_trace_create_buffer create_buffer,u_trace_delete_buffer delete_buffer,u_trace_record_ts record_timestamp,u_trace_read_ts read_timestamp,u_trace_capture_data capture_data,u_trace_get_data get_data,u_trace_delete_flush_data delete_flush_data)508 u_trace_context_init(struct u_trace_context *utctx,
509                      void *pctx,
510                      uint32_t timestamp_size_bytes,
511                      uint32_t max_indirect_size_bytes,
512                      u_trace_create_buffer create_buffer,
513                      u_trace_delete_buffer delete_buffer,
514                      u_trace_record_ts record_timestamp,
515                      u_trace_read_ts read_timestamp,
516                      u_trace_capture_data capture_data,
517                      u_trace_get_data get_data,
518                      u_trace_delete_flush_data delete_flush_data)
519 {
520    u_trace_state_init();
521 
522    utctx->enabled_traces = u_trace_state.enabled_traces;
523    utctx->pctx = pctx;
524    utctx->create_buffer = create_buffer;
525    utctx->delete_buffer = delete_buffer;
526    utctx->record_timestamp = record_timestamp;
527    utctx->capture_data = capture_data;
528    utctx->get_data = get_data;
529    utctx->read_timestamp = read_timestamp;
530    utctx->delete_flush_data = delete_flush_data;
531    utctx->timestamp_size_bytes = timestamp_size_bytes;
532    utctx->max_indirect_size_bytes = max_indirect_size_bytes;
533 
534    utctx->last_time_ns = 0;
535    utctx->first_time_ns = 0;
536    utctx->frame_nr = 0;
537    utctx->batch_nr = 0;
538    utctx->event_nr = 0;
539    utctx->start_of_frame = true;
540 
541    utctx->dummy_indirect_data = calloc(1, max_indirect_size_bytes);
542 
543    list_inithead(&utctx->flushed_trace_chunks);
544 
545    if (utctx->enabled_traces & U_TRACE_TYPE_PRINT) {
546       utctx->out = u_trace_state.trace_file;
547 
548       if (utctx->enabled_traces & U_TRACE_TYPE_JSON) {
549          utctx->out_printer = &json_printer;
550       } else if (utctx->enabled_traces & U_TRACE_TYPE_CSV) {
551          utctx->out_printer = &csv_printer;
552       } else {
553          utctx->out_printer = &txt_printer;
554       }
555    } else {
556       utctx->out = NULL;
557       utctx->out_printer = NULL;
558    }
559 
560 #ifdef HAVE_PERFETTO
561    simple_mtx_lock(&ctx_list_mutex);
562    list_add(&utctx->node, &ctx_list);
563    if (_u_trace_perfetto_count > 0)
564       utctx->enabled_traces |= U_TRACE_TYPE_PERFETTO_ACTIVE;
565 
566    queue_init(utctx);
567 
568    simple_mtx_unlock(&ctx_list_mutex);
569 #else
570    queue_init(utctx);
571 #endif
572 
573    if (!(p_atomic_read_relaxed(&utctx->enabled_traces) &
574          U_TRACE_TYPE_REQUIRE_QUEUING))
575       return;
576 
577    if (utctx->out) {
578       utctx->out_printer->start(utctx);
579    }
580 }
581 
582 void
u_trace_context_fini(struct u_trace_context * utctx)583 u_trace_context_fini(struct u_trace_context *utctx)
584 {
585 #ifdef HAVE_PERFETTO
586    simple_mtx_lock(&ctx_list_mutex);
587    list_del(&utctx->node);
588    simple_mtx_unlock(&ctx_list_mutex);
589 #endif
590 
591    if (utctx->out) {
592       if (utctx->batch_nr > 0) {
593          utctx->out_printer->end_of_frame(utctx);
594       }
595 
596       utctx->out_printer->end(utctx);
597       fflush(utctx->out);
598    }
599 
600    free (utctx->dummy_indirect_data);
601 
602    if (!utctx->queue.jobs)
603       return;
604    util_queue_finish(&utctx->queue);
605    util_queue_destroy(&utctx->queue);
606    free_chunks(&utctx->flushed_trace_chunks);
607 }
608 
609 #ifdef HAVE_PERFETTO
610 void
u_trace_perfetto_start(void)611 u_trace_perfetto_start(void)
612 {
613    simple_mtx_lock(&ctx_list_mutex);
614 
615    list_for_each_entry (struct u_trace_context, utctx, &ctx_list, node) {
616       queue_init(utctx);
617       p_atomic_set(&utctx->enabled_traces,
618                    utctx->enabled_traces | U_TRACE_TYPE_PERFETTO_ACTIVE);
619    }
620 
621    _u_trace_perfetto_count++;
622 
623    simple_mtx_unlock(&ctx_list_mutex);
624 }
625 
626 void
u_trace_perfetto_stop(void)627 u_trace_perfetto_stop(void)
628 {
629    simple_mtx_lock(&ctx_list_mutex);
630 
631    assert(_u_trace_perfetto_count > 0);
632    _u_trace_perfetto_count--;
633    if (_u_trace_perfetto_count == 0) {
634       list_for_each_entry (struct u_trace_context, utctx, &ctx_list, node) {
635          p_atomic_set(&utctx->enabled_traces,
636                       utctx->enabled_traces & ~U_TRACE_TYPE_PERFETTO_ACTIVE);
637       }
638    }
639 
640    simple_mtx_unlock(&ctx_list_mutex);
641 }
642 #endif
643 
644 static void
process_chunk(void * job,void * gdata,int thread_index)645 process_chunk(void *job, void *gdata, int thread_index)
646 {
647    struct u_trace_chunk *chunk = job;
648    struct u_trace_context *utctx = chunk->utctx;
649 
650    if (chunk->frame_nr != U_TRACE_FRAME_UNKNOWN &&
651        chunk->frame_nr != utctx->frame_nr) {
652       if (utctx->out) {
653          utctx->out_printer->end_of_frame(utctx);
654       }
655       utctx->frame_nr = chunk->frame_nr;
656       utctx->start_of_frame = true;
657    }
658 
659    if (utctx->start_of_frame) {
660       utctx->start_of_frame = false;
661       utctx->batch_nr = 0;
662       if (utctx->out) {
663          utctx->out_printer->start_of_frame(utctx);
664       }
665    }
666 
667    /* For first chunk of batch, accumulated times will be zerod: */
668    if (!utctx->last_time_ns) {
669       utctx->event_nr = 0;
670       if (utctx->out) {
671          utctx->out_printer->start_of_batch(utctx);
672       }
673    }
674 
675    for (unsigned idx = 0; idx < chunk->num_traces; idx++) {
676       const struct u_trace_event *evt = &chunk->traces[idx];
677 
678       if (!evt->tp)
679          continue;
680 
681       uint64_t ns = utctx->read_timestamp(utctx,
682                                           chunk->timestamps,
683                                           utctx->timestamp_size_bytes * idx,
684                                           chunk->flush_data);
685       int32_t delta;
686 
687       if (!utctx->first_time_ns)
688          utctx->first_time_ns = ns;
689 
690       if (ns != U_TRACE_NO_TIMESTAMP) {
691          delta = utctx->last_time_ns ? ns - utctx->last_time_ns : 0;
692          utctx->last_time_ns = ns;
693       } else {
694          /* we skipped recording the timestamp, so it should be
695           * the same as last msg:
696           */
697          ns = utctx->last_time_ns;
698          delta = 0;
699       }
700 
701       const void *indirect_data = NULL;
702       if (evt->tp->indirect_sz > 0) {
703          if (utctx->enabled_traces & U_TRACE_TYPE_INDIRECTS) {
704             indirect_data = utctx->get_data(utctx, chunk->indirects,
705                                             utctx->max_indirect_size_bytes * idx,
706                                             evt->tp->indirect_sz);
707          } else {
708             indirect_data = utctx->dummy_indirect_data;
709          }
710       }
711 
712       if (utctx->out) {
713          utctx->out_printer->event(utctx, chunk, evt, ns, delta, indirect_data);
714       }
715 #ifdef HAVE_PERFETTO
716       if (evt->tp->perfetto &&
717           (p_atomic_read_relaxed(&utctx->enabled_traces) &
718            U_TRACE_TYPE_PERFETTO_ACTIVE)) {
719          evt->tp->perfetto(utctx->pctx, ns, evt->tp->tp_idx, chunk->flush_data,
720                            evt->payload, indirect_data);
721       }
722 #endif
723 
724       utctx->event_nr++;
725    }
726 
727    if (chunk->last) {
728       if (utctx->out) {
729          utctx->out_printer->end_of_batch(utctx);
730       }
731 
732       utctx->batch_nr++;
733       utctx->last_time_ns = 0;
734       utctx->first_time_ns = 0;
735    }
736 
737    if (chunk->eof) {
738       if (utctx->out) {
739          utctx->out_printer->end_of_frame(utctx);
740       }
741       utctx->frame_nr++;
742       utctx->start_of_frame = true;
743    }
744 
745    if (chunk->free_flush_data && utctx->delete_flush_data) {
746       utctx->delete_flush_data(utctx, chunk->flush_data);
747    }
748 }
749 
750 static void
cleanup_chunk(void * job,void * gdata,int thread_index)751 cleanup_chunk(void *job, void *gdata, int thread_index)
752 {
753    free_chunk(job);
754 }
755 
756 void
u_trace_context_process(struct u_trace_context * utctx,bool eof)757 u_trace_context_process(struct u_trace_context *utctx, bool eof)
758 {
759    struct list_head *chunks = &utctx->flushed_trace_chunks;
760 
761    if (list_is_empty(chunks))
762       return;
763 
764    struct u_trace_chunk *last_chunk =
765       list_last_entry(chunks, struct u_trace_chunk, node);
766    last_chunk->eof = eof;
767 
768    while (!list_is_empty(chunks)) {
769       struct u_trace_chunk *chunk =
770          list_first_entry(chunks, struct u_trace_chunk, node);
771 
772       /* remove from list before enqueuing, because chunk is freed
773        * once it is processed by the queue:
774        */
775       list_delinit(&chunk->node);
776 
777       util_queue_add_job(&utctx->queue, chunk, &chunk->fence, process_chunk,
778                          cleanup_chunk, TIMESTAMP_BUF_SIZE);
779    }
780 }
781 
782 void
u_trace_init(struct u_trace * ut,struct u_trace_context * utctx)783 u_trace_init(struct u_trace *ut, struct u_trace_context *utctx)
784 {
785    ut->utctx = utctx;
786    ut->num_traces = 0;
787    list_inithead(&ut->trace_chunks);
788 }
789 
790 void
u_trace_fini(struct u_trace * ut)791 u_trace_fini(struct u_trace *ut)
792 {
793    /* Normally the list of trace-chunks would be empty, if they
794     * have been flushed to the trace-context.
795     */
796    free_chunks(&ut->trace_chunks);
797    ut->num_traces = 0;
798 }
799 
800 bool
u_trace_has_points(struct u_trace * ut)801 u_trace_has_points(struct u_trace *ut)
802 {
803    return !list_is_empty(&ut->trace_chunks);
804 }
805 
806 struct u_trace_iterator
u_trace_begin_iterator(struct u_trace * ut)807 u_trace_begin_iterator(struct u_trace *ut)
808 {
809    if (list_is_empty(&ut->trace_chunks))
810       return (struct u_trace_iterator) { ut, NULL, 0 };
811 
812    struct u_trace_chunk *first_chunk =
813       list_first_entry(&ut->trace_chunks, struct u_trace_chunk, node);
814 
815    return (struct u_trace_iterator) { ut, first_chunk, 0 };
816 }
817 
818 struct u_trace_iterator
u_trace_end_iterator(struct u_trace * ut)819 u_trace_end_iterator(struct u_trace *ut)
820 {
821    if (list_is_empty(&ut->trace_chunks))
822       return (struct u_trace_iterator) { ut, NULL, 0 };
823 
824    struct u_trace_chunk *last_chunk =
825       list_last_entry(&ut->trace_chunks, struct u_trace_chunk, node);
826 
827    return (struct u_trace_iterator) { ut, last_chunk,
828                                       last_chunk->num_traces };
829 }
830 
831 /* If an iterator was created when there were no chunks and there are now
832  * chunks, "sanitize" it to include the first chunk.
833  */
834 static struct u_trace_iterator
sanitize_iterator(struct u_trace_iterator iter)835 sanitize_iterator(struct u_trace_iterator iter)
836 {
837    if (iter.ut && !iter.chunk && !list_is_empty(&iter.ut->trace_chunks)) {
838       iter.chunk =
839          list_first_entry(&iter.ut->trace_chunks, struct u_trace_chunk, node);
840    }
841 
842    return iter;
843 }
844 
845 bool
u_trace_iterator_equal(struct u_trace_iterator a,struct u_trace_iterator b)846 u_trace_iterator_equal(struct u_trace_iterator a, struct u_trace_iterator b)
847 {
848    a = sanitize_iterator(a);
849    b = sanitize_iterator(b);
850    return a.ut == b.ut && a.chunk == b.chunk && a.event_idx == b.event_idx;
851 }
852 
853 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_buffer copy_buffer)854 u_trace_clone_append(struct u_trace_iterator begin_it,
855                      struct u_trace_iterator end_it,
856                      struct u_trace *into,
857                      void *cmdstream,
858                      u_trace_copy_buffer copy_buffer)
859 {
860    begin_it = sanitize_iterator(begin_it);
861    end_it = sanitize_iterator(end_it);
862 
863    struct u_trace_chunk *from_chunk = begin_it.chunk;
864    uint32_t from_idx = begin_it.event_idx;
865 
866    while (from_chunk != end_it.chunk || from_idx != end_it.event_idx) {
867       struct u_trace_chunk *to_chunk = get_chunk(into, 0 /* payload_size */);
868 
869       unsigned to_copy = MIN2(TRACES_PER_CHUNK - to_chunk->num_traces,
870                               from_chunk->num_traces - from_idx);
871       if (from_chunk == end_it.chunk)
872          to_copy = MIN2(to_copy, end_it.event_idx - from_idx);
873 
874       copy_buffer(begin_it.ut->utctx, cmdstream,
875                   from_chunk->timestamps,
876                   begin_it.ut->utctx->timestamp_size_bytes * from_idx,
877                   to_chunk->timestamps,
878                   begin_it.ut->utctx->timestamp_size_bytes * to_chunk->num_traces,
879                   begin_it.ut->utctx->timestamp_size_bytes * to_copy);
880 
881       if (from_chunk->has_indirect) {
882          copy_buffer(begin_it.ut->utctx, cmdstream,
883                      from_chunk->indirects,
884                      begin_it.ut->utctx->max_indirect_size_bytes * from_idx,
885                      to_chunk->indirects,
886                      begin_it.ut->utctx->max_indirect_size_bytes * to_chunk->num_traces,
887                      begin_it.ut->utctx->max_indirect_size_bytes * to_copy);
888       }
889 
890       memcpy(&to_chunk->traces[to_chunk->num_traces],
891              &from_chunk->traces[from_idx],
892              to_copy * sizeof(struct u_trace_event));
893 
894       /* Take a refcount on payloads from from_chunk if needed. */
895       if (begin_it.ut != into) {
896          struct u_trace_payload_buf **in_payload;
897          u_vector_foreach (in_payload, &from_chunk->payloads) {
898             struct u_trace_payload_buf **out_payload =
899                u_vector_add(&to_chunk->payloads);
900 
901             *out_payload = u_trace_payload_buf_ref(*in_payload);
902          }
903       }
904 
905       into->num_traces += to_copy;
906       to_chunk->num_traces += to_copy;
907       from_idx += to_copy;
908 
909       assert(from_idx <= from_chunk->num_traces);
910       if (from_idx == from_chunk->num_traces) {
911          if (from_chunk == end_it.chunk)
912             break;
913 
914          from_idx = 0;
915          from_chunk =
916             list_entry(from_chunk->node.next, struct u_trace_chunk, node);
917       }
918    }
919 }
920 
921 void
u_trace_disable_event_range(struct u_trace_iterator begin_it,struct u_trace_iterator end_it)922 u_trace_disable_event_range(struct u_trace_iterator begin_it,
923                             struct u_trace_iterator end_it)
924 {
925    begin_it = sanitize_iterator(begin_it);
926    end_it = sanitize_iterator(end_it);
927 
928    struct u_trace_chunk *current_chunk = begin_it.chunk;
929    uint32_t start_idx = begin_it.event_idx;
930 
931    while (current_chunk != end_it.chunk) {
932       memset(&current_chunk->traces[start_idx], 0,
933              (current_chunk->num_traces - start_idx) *
934                 sizeof(struct u_trace_event));
935       start_idx = 0;
936       current_chunk =
937          list_entry(current_chunk->node.next, struct u_trace_chunk, node);
938    }
939 
940    memset(&current_chunk->traces[start_idx], 0,
941           (end_it.event_idx - start_idx) * sizeof(struct u_trace_event));
942 }
943 
944 /**
945  * Append a trace event, returning pointer to buffer of tp->payload_sz
946  * to be filled in with trace payload.  Called by generated tracepoint
947  * functions.
948  */
949 void *
u_trace_appendv(struct u_trace * ut,void * cs,const struct u_tracepoint * tp,unsigned variable_sz,unsigned n_indirects,const struct u_trace_address * addresses,const uint8_t * indirect_sizes_B)950 u_trace_appendv(struct u_trace *ut,
951                 void *cs,
952                 const struct u_tracepoint *tp,
953                 unsigned variable_sz,
954                 unsigned n_indirects,
955                 const struct u_trace_address *addresses,
956                 const uint8_t *indirect_sizes_B)
957 {
958    assert(tp->payload_sz == ALIGN_NPOT(tp->payload_sz, 8));
959 
960    unsigned payload_sz = ALIGN_NPOT(tp->payload_sz + variable_sz, 8);
961    struct u_trace_chunk *chunk = get_chunk(ut, payload_sz);
962    unsigned tp_idx = chunk->num_traces++;
963 
964    /* sub-allocate storage for trace payload: */
965    void *payload = NULL;
966    if (payload_sz > 0) {
967       payload = chunk->payload->next;
968       chunk->payload->next += payload_sz;
969    }
970 
971    /* record a timestamp for the trace: */
972    ut->utctx->record_timestamp(ut, cs, chunk->timestamps,
973                                ut->utctx->timestamp_size_bytes * tp_idx,
974                                tp->flags);
975 
976    if (ut->utctx->enabled_traces & U_TRACE_TYPE_INDIRECTS) {
977       for (unsigned i = 0; i < n_indirects; i++) {
978          ut->utctx->capture_data(ut, cs, chunk->indirects,
979                                  ut->utctx->max_indirect_size_bytes * tp_idx,
980                                  addresses[i].bo, addresses[i].offset,
981                                  indirect_sizes_B[i]);
982       }
983       chunk->has_indirect |= n_indirects > 0;
984    }
985 
986    chunk->traces[tp_idx] = (struct u_trace_event) {
987       .tp = tp,
988       .payload = payload,
989    };
990    ut->num_traces++;
991 
992    return payload;
993 }
994 
995 void
u_trace_flush(struct u_trace * ut,void * flush_data,uint32_t frame_nr,bool free_data)996 u_trace_flush(struct u_trace *ut,
997               void *flush_data,
998               uint32_t frame_nr,
999               bool free_data)
1000 {
1001    list_for_each_entry (struct u_trace_chunk, chunk, &ut->trace_chunks,
1002                         node) {
1003       chunk->flush_data = flush_data;
1004       chunk->free_flush_data = false;
1005       chunk->frame_nr = frame_nr;
1006    }
1007 
1008    if (free_data && !list_is_empty(&ut->trace_chunks)) {
1009       struct u_trace_chunk *last_chunk =
1010          list_last_entry(&ut->trace_chunks, struct u_trace_chunk, node);
1011       last_chunk->free_flush_data = true;
1012    }
1013 
1014    /* transfer batch's log chunks to context: */
1015    list_splicetail(&ut->trace_chunks, &ut->utctx->flushed_trace_chunks);
1016    list_inithead(&ut->trace_chunks);
1017    ut->num_traces = 0;
1018 }
1019