xref: /dragonfly/usr.bin/flame_graph/collect.c (revision dda92f98)
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