1 /* 2 * Copyright (c) 2020 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "flame.h" 36 37 static void flame_disable(int sig); 38 static void flame_collect(int ncpus); 39 static void flame_doentry(int cpuid, uint32_t idx, 40 struct flame_graph_entry *fge); 41 42 static uint32_t *savewindex; 43 static struct save_ctx symctx; 44 45 void 46 flame_collect_loop(void) 47 { 48 int ncpus; 49 int enable; 50 int sniff = 1; 51 int n; 52 size_t ncpus_size = sizeof(ncpus); 53 54 if (sysctlbyname("hw.ncpu", &ncpus, &ncpus_size, NULL, 0) < 0) { 55 perror("hw.ncpu"); 56 exit(1); 57 } 58 savewindex = calloc(ncpus, sizeof(*savewindex)); 59 60 read_symbols("/boot/kernel/kernel"); 61 62 enable = 1; 63 signal(SIGINT, flame_disable); 64 if (sysctlbyname("debug.flame_graph_enable", 65 NULL, NULL, &enable, sizeof(enable)) < 0) { 66 perror("debug.flame_graph_enable"); 67 exit(1); 68 } 69 70 n = 0; 71 for (;;) { 72 sysctlbyname("debug.flame_graph_sniff", NULL, NULL, 73 &sniff, sizeof(sniff)); 74 usleep(10000); 75 ++n; 76 if ((n & 127) == 0) 77 flame_collect(ncpus); 78 } 79 flame_disable(0); 80 } 81 82 static void 83 flame_collect(int ncpus) 84 { 85 struct flame_graph_pcpu *fg; 86 struct flame_graph_entry *fge; 87 struct flame_graph_entry *scan; 88 void *buf; 89 void *ptr; 90 size_t bytes; 91 uint32_t rindex; 92 uint32_t windex; 93 uint32_t delta; 94 int n; 95 96 bytes = sizeof(size_t) + 97 sizeof(struct flame_graph_pcpu) + 98 sizeof(struct flame_graph_entry) * FLAME_GRAPH_NENTRIES; 99 bytes *= ncpus; 100 buf = malloc(bytes); 101 102 if (sysctlbyname("debug.flame_graph_data", buf, &bytes, NULL, 0) < 0) { 103 perror("debug.flame_graph_data"); 104 exit(1); 105 } 106 107 ptr = buf; 108 for (n = 0; n < ncpus; ++n) { 109 if (bytes < sizeof(size_t) + sizeof(*fg)) 110 break; 111 fg = (void *)((char *)ptr + sizeof(size_t)); 112 bytes -= sizeof(size_t) + sizeof(*fg); 113 ptr = (char *)ptr + sizeof(size_t) + sizeof(*fg); 114 115 if (bytes < sizeof(*fge) * fg->nentries) 116 break; 117 fge = (void *)(fg + 1); 118 ptr = (char *)ptr + sizeof(*fge) * fg->nentries; 119 bytes -= sizeof(*fge) * fg->nentries; 120 121 rindex = savewindex[n]; 122 windex = fg->windex; 123 124 /* 125 * Lost data? 126 */ 127 delta = windex - rindex; 128 if (delta >= fg->nentries) 129 rindex = windex - fg->nentries + 1; 130 131 /* 132 * Process entries 133 */ 134 while (rindex != windex) { 135 scan = &fge[rindex % fg->nentries]; 136 if (scan->rips[0]) 137 flame_doentry(n, rindex, scan); 138 ++rindex; 139 } 140 savewindex[n] = windex; 141 } 142 free(buf); 143 } 144 145 static void 146 flame_doentry(int cpuid, uint32_t idx, struct flame_graph_entry *fge) 147 { 148 int i; 149 150 for (i = 0; i < FLAME_GRAPH_FRAMES; ++i) { 151 if (fge->rips[i] == 0) 152 break; 153 } 154 155 printf("%03d/%03d ", cpuid, idx % 1000); 156 157 while (--i >= 0) { 158 printf(" "); 159 printf("%s()", 160 address_to_symbol((void *)fge->rips[i], &symctx)); 161 } 162 printf("\n"); 163 } 164 165 /* 166 * 167 */ 168 static void 169 flame_disable(int sig __unused) 170 { 171 int enable; 172 173 enable = 0; 174 if (sysctlbyname("debug.flame_graph_enable", 175 NULL, NULL, &enable, sizeof(enable)) < 0) { 176 perror("debug.flame_graph_enable"); 177 } 178 exit(0); 179 } 180