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