1/*
2 * Copyright (C) 2019 Alyssa Rosenzweig
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 */
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <stdbool.h>
28#include <panfrost-job.h>
29#include "pan_trace.h"
30#include "util/list.h"
31
32/* The pandecode utility is capable of parsing a command stream trace and
33 * disassembling any referenced shaders. Traces themselves are glorified memory
34 * dumps, a directory consisting of .bin's for each memory segment, and a
35 * simple plain-text description of the interesting kernel activity.
36 * Historically, these dumps have been produced via panwrap, an LD_PRELOAD shim
37 * sitting between the driver and the kernel. However, for modern Panfrost, we
38 * can just produce the dumps ourselves, which is rather less fragile. This
39 * file (pantrace) implements this functionality. */
40
41static FILE *pan_control_log;
42static const char *pan_control_base;
43
44/* Represent the abstraction for a single mmap chunk */
45
46static unsigned pantrace_memory_count = 0;
47
48struct pantrace_memory {
49        struct list_head node;
50
51        mali_ptr gpu;
52        void *cpu;
53        size_t sz;
54        char *full_filename;
55};
56
57static struct pantrace_memory mmaps;
58
59void
60pantrace_initialize(const char *base)
61{
62        /* Open the control.log */
63        char fn[128];
64        snprintf(fn, 128, "%s/control.log", base);
65        pan_control_log = fopen(fn, "w+");
66        assert(pan_control_log);
67
68        /* Save the base for later */
69        pan_control_base = base;
70
71        /* Initialize the mmap list */
72        list_inithead(&mmaps.node);
73}
74
75static bool
76pantrace_is_initialized(void)
77{
78        return pan_control_log && pan_control_base;
79}
80
81/* Traces a submitted job with a given job chain, core requirements, and
82 * platform */
83
84void
85pantrace_submit_job(mali_ptr jc, unsigned core_req, unsigned is_bifrost)
86{
87        if (!pantrace_is_initialized())
88                return;
89
90        fprintf(pan_control_log, "JS %" PRIx64 " %x %x\n",
91                        jc, core_req, is_bifrost);
92        fflush(pan_control_log);
93}
94
95/* Dumps a given mapped memory buffer with the given label. If no label
96 * is given (label == NULL), one is created */
97
98void
99pantrace_mmap(mali_ptr gpu, void *cpu, size_t sz, char *label)
100{
101        if (!pantrace_is_initialized())
102                return;
103
104        char *filename = NULL;
105        char *full_filename = NULL;
106
107        /* Create a filename based on the label or count */
108
109        if (label) {
110                asprintf(&filename, "%s.bin", label);
111        } else {
112                asprintf(&filename, "memory_%d.bin", pantrace_memory_count++);
113        }
114
115        /* Emit an mmap for it */
116        fprintf(pan_control_log, "MMAP %" PRIx64 " %s\n", gpu, filename);
117        fflush(pan_control_log);
118
119        /* Dump the memory itself */
120        asprintf(&full_filename, "%s/%s", pan_control_base, filename);
121        free(filename);
122
123        struct pantrace_memory *mem = malloc(sizeof(*mem));
124        list_inithead(&mem->node);
125        mem->gpu = gpu;
126        mem->cpu = cpu;
127        mem->sz = sz;
128        mem->full_filename = full_filename;
129        list_add(&mem->node, &mmaps.node);
130}
131
132/* Dump all memory at once, once everything has been written */
133
134void
135pantrace_dump_memory(void)
136{
137        if (!pantrace_is_initialized())
138                return;
139
140        list_for_each_entry(struct pantrace_memory, pos, &mmaps.node, node) {
141                /* Save the mapping */
142                FILE *fp = fopen(pos->full_filename, "wb");
143                fwrite(pos->cpu, 1, pos->sz, fp);
144                fclose(fp);
145        }
146}
147