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