xref: /dflybsd-src/sys/platform/pc64/x86_64/mp_flame.c (revision 2b3f93ea6d1f70880f3e87f3c2cbe0dc0bfc9332)
191dc43ddSMatthew Dillon /*
291dc43ddSMatthew Dillon  * Copyright (c) 2020 The DragonFly Project.  All rights reserved.
391dc43ddSMatthew Dillon  *
491dc43ddSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
591dc43ddSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
691dc43ddSMatthew Dillon  *
791dc43ddSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
891dc43ddSMatthew Dillon  * modification, are permitted provided that the following conditions
991dc43ddSMatthew Dillon  * are met:
1091dc43ddSMatthew Dillon  *
1191dc43ddSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
1291dc43ddSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
1391dc43ddSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
1491dc43ddSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
1591dc43ddSMatthew Dillon  *    the documentation and/or other materials provided with the
1691dc43ddSMatthew Dillon  *    distribution.
1791dc43ddSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
1891dc43ddSMatthew Dillon  *    contributors may be used to endorse or promote products derived
1991dc43ddSMatthew Dillon  *    from this software without specific, prior written permission.
2091dc43ddSMatthew Dillon  *
2191dc43ddSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2291dc43ddSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2391dc43ddSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2491dc43ddSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
2591dc43ddSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2691dc43ddSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2791dc43ddSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2891dc43ddSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2991dc43ddSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3091dc43ddSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3191dc43ddSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3291dc43ddSMatthew Dillon  * SUCH DAMAGE.
3391dc43ddSMatthew Dillon  */
3491dc43ddSMatthew Dillon #include "opt_cpu.h"
3591dc43ddSMatthew Dillon 
3691dc43ddSMatthew Dillon #include <sys/param.h>
3791dc43ddSMatthew Dillon #include <sys/systm.h>
3891dc43ddSMatthew Dillon #include <sys/kernel.h>
3991dc43ddSMatthew Dillon #include <sys/sysctl.h>
4091dc43ddSMatthew Dillon #include <sys/malloc.h>
41*2b3f93eaSMatthew Dillon #include <sys/caps.h>
4291dc43ddSMatthew Dillon #include <sys/flame_graph.h>
4391dc43ddSMatthew Dillon 
4491dc43ddSMatthew Dillon #include <sys/thread2.h>
4591dc43ddSMatthew Dillon 
4691dc43ddSMatthew Dillon #include <machine/atomic.h>
4791dc43ddSMatthew Dillon #include <machine/cpufunc.h>
4891dc43ddSMatthew Dillon #include <machine/cputypes.h>
4991dc43ddSMatthew Dillon #include <machine/psl.h>
5091dc43ddSMatthew Dillon #include <machine/segments.h>
5191dc43ddSMatthew Dillon #include <machine/tss.h>
5291dc43ddSMatthew Dillon #include <machine/specialreg.h>
5391dc43ddSMatthew Dillon #include <machine/globaldata.h>
5491dc43ddSMatthew Dillon 
5591dc43ddSMatthew Dillon #include <machine/md_var.h>		/* setidt() */
5691dc43ddSMatthew Dillon 
57db2ec6f8SSascha Wildner __read_frequently static struct flame_graph_pcpu *flame_graph_array;
5891dc43ddSMatthew Dillon __read_frequently static int flame_graph_enable;
5991dc43ddSMatthew Dillon 
6091dc43ddSMatthew Dillon SYSCTL_INT(_debug, OID_AUTO, flame_graph_enable, CTLFLAG_RW,
6191dc43ddSMatthew Dillon 	   &flame_graph_enable, 0, "Collect data for flame graphs");
6291dc43ddSMatthew Dillon SYSCTL_LONG(_debug, OID_AUTO, flame_graph_array, CTLFLAG_RD,
6391dc43ddSMatthew Dillon 	   &flame_graph_array, 0, "Collect data for flame graphs");
6491dc43ddSMatthew Dillon 
65db2ec6f8SSascha Wildner static MALLOC_DEFINE(M_FLAME, "flame_graphs", "Flame Graphs");
6691dc43ddSMatthew Dillon 
6791dc43ddSMatthew Dillon static void
hard_sniff_init(void * arg)6891dc43ddSMatthew Dillon hard_sniff_init(void *arg)
6991dc43ddSMatthew Dillon {
7091dc43ddSMatthew Dillon 	struct flame_graph_pcpu *fg;
7191dc43ddSMatthew Dillon 	struct flame_graph_entry *fge;
7291dc43ddSMatthew Dillon 	int n;
7391dc43ddSMatthew Dillon 
7491dc43ddSMatthew Dillon 	flame_graph_array = kmalloc(sizeof(*fg) * ncpus,
7591dc43ddSMatthew Dillon 				    M_FLAME, M_WAITOK | M_CACHEALIGN | M_ZERO);
7691dc43ddSMatthew Dillon 	for (n = 0; n < ncpus; ++n) {
7791dc43ddSMatthew Dillon 		fge = kmalloc(sizeof(*fge) * FLAME_GRAPH_NENTRIES,
7891dc43ddSMatthew Dillon 			      M_FLAME, M_WAITOK | M_CACHEALIGN | M_ZERO);
7991dc43ddSMatthew Dillon 
8091dc43ddSMatthew Dillon 		fg = &flame_graph_array[n];
8191dc43ddSMatthew Dillon 		fg->nentries = FLAME_GRAPH_NENTRIES;
8291dc43ddSMatthew Dillon 		fg->fge = fge;
8391dc43ddSMatthew Dillon 	}
8491dc43ddSMatthew Dillon }
8591dc43ddSMatthew Dillon 
8691dc43ddSMatthew Dillon SYSINIT(swi_vm_setup, SI_BOOT2_MACHDEP, SI_ORDER_ANY, hard_sniff_init, NULL);
8791dc43ddSMatthew Dillon 
8891dc43ddSMatthew Dillon /*
8991dc43ddSMatthew Dillon  * Xsniff vector calls into here
9091dc43ddSMatthew Dillon  *
9191dc43ddSMatthew Dillon  * WARNING! This code ignores critical sections!  The system can be
9291dc43ddSMatthew Dillon  *          in any state.  The only thing we safely have access to
9391dc43ddSMatthew Dillon  *          is the gd.
9491dc43ddSMatthew Dillon  */
9591dc43ddSMatthew Dillon void
hard_sniff(struct trapframe * tf)9691dc43ddSMatthew Dillon hard_sniff(struct trapframe *tf)
9791dc43ddSMatthew Dillon {
9891dc43ddSMatthew Dillon         globaldata_t gd = mycpu;
9991dc43ddSMatthew Dillon 	thread_t td;
10091dc43ddSMatthew Dillon 	char *top;
10191dc43ddSMatthew Dillon 	char *bot;
10291dc43ddSMatthew Dillon 	char *rbp;
10391dc43ddSMatthew Dillon 	char *rip;
10491dc43ddSMatthew Dillon 	struct flame_graph_pcpu *fg;
10591dc43ddSMatthew Dillon 	struct flame_graph_entry *fge;
10691dc43ddSMatthew Dillon 	int n;
10791dc43ddSMatthew Dillon 
10891dc43ddSMatthew Dillon 	/*
10991dc43ddSMatthew Dillon 	 * systat -pv 1 sampling
11091dc43ddSMatthew Dillon 	 */
11191dc43ddSMatthew Dillon         gd->gd_sample_pc = (void *)(intptr_t)tf->tf_rip;
11291dc43ddSMatthew Dillon         gd->gd_sample_sp = (void *)(intptr_t)tf->tf_rsp;
11391dc43ddSMatthew Dillon 
11491dc43ddSMatthew Dillon 	/*
11591dc43ddSMatthew Dillon 	 * Flame graph sampling, require %rbp (frame pointer) chaining.
11691dc43ddSMatthew Dillon 	 * Attempt to follow the chain and record what we believe are
11791dc43ddSMatthew Dillon 	 * %rip addresses.
11891dc43ddSMatthew Dillon 	 */
11991dc43ddSMatthew Dillon 	if (flame_graph_enable == 0)
12091dc43ddSMatthew Dillon 		return;
12191dc43ddSMatthew Dillon 	td = gd->gd_curthread;
12291dc43ddSMatthew Dillon 	if (td == NULL)
12391dc43ddSMatthew Dillon 		return;
12491dc43ddSMatthew Dillon 	bot = (char *)td->td_kstack + PAGE_SIZE;	/* skip guard */
12591dc43ddSMatthew Dillon 	top = (char *)td->td_kstack + td->td_kstack_size;
12691dc43ddSMatthew Dillon 	if (bot >= top)
12791dc43ddSMatthew Dillon 		return;
12891dc43ddSMatthew Dillon 	fg = &flame_graph_array[gd->gd_cpuid];
12991dc43ddSMatthew Dillon 	fge = &fg->fge[fg->windex % FLAME_GRAPH_NENTRIES];
13091dc43ddSMatthew Dillon 
13191dc43ddSMatthew Dillon 	rip = (char *)(intptr_t)tf->tf_rip;
13291dc43ddSMatthew Dillon 	fge->rips[0] = (intptr_t)rip;
13391dc43ddSMatthew Dillon 	rbp = (char *)(intptr_t)tf->tf_rbp;
13491dc43ddSMatthew Dillon 
13591dc43ddSMatthew Dillon 	for (n = 1; n < FLAME_GRAPH_FRAMES - 1; ++n) {
13691dc43ddSMatthew Dillon 		if (rbp < bot || rbp > top - 8 || ((intptr_t)rbp & 7))
13791dc43ddSMatthew Dillon 			break;
13891dc43ddSMatthew Dillon 		fge->rips[n] = (intptr_t)*(char **)(rbp + 8);
13991dc43ddSMatthew Dillon 		if (*(char **)rbp <= rbp)
14091dc43ddSMatthew Dillon 			break;
14191dc43ddSMatthew Dillon 		rbp = *(char **)rbp;
14291dc43ddSMatthew Dillon 	}
14391dc43ddSMatthew Dillon 	fge->rips[n] = 0;
14491dc43ddSMatthew Dillon 	cpu_sfence();
14591dc43ddSMatthew Dillon 	++fg->windex;
14691dc43ddSMatthew Dillon }
14791dc43ddSMatthew Dillon 
14891dc43ddSMatthew Dillon static int
sysctl_flame_graph_data(SYSCTL_HANDLER_ARGS)14991dc43ddSMatthew Dillon sysctl_flame_graph_data(SYSCTL_HANDLER_ARGS)
15091dc43ddSMatthew Dillon {
15191dc43ddSMatthew Dillon 	int error;
15291dc43ddSMatthew Dillon 	int n;
15391dc43ddSMatthew Dillon 	size_t ebytes;
15491dc43ddSMatthew Dillon 
155*2b3f93eaSMatthew Dillon 	error = caps_priv_check_self(SYSCAP_RESTRICTEDROOT);
15691dc43ddSMatthew Dillon 	if (error)
15791dc43ddSMatthew Dillon 		return error;
15891dc43ddSMatthew Dillon 	if (flame_graph_array == NULL)
15991dc43ddSMatthew Dillon 		return EOPNOTSUPP;
16091dc43ddSMatthew Dillon 
16191dc43ddSMatthew Dillon 	ebytes = sizeof(struct flame_graph_pcpu) +
16291dc43ddSMatthew Dillon 		 sizeof(struct flame_graph_entry);
16391dc43ddSMatthew Dillon 
16491dc43ddSMatthew Dillon 	for (n = 0; n < ncpus && error == 0; ++n) {
16591dc43ddSMatthew Dillon 		error = SYSCTL_OUT(req, &ebytes, sizeof(ebytes));
16691dc43ddSMatthew Dillon 		if (error == 0)
16791dc43ddSMatthew Dillon 			error = SYSCTL_OUT(req, flame_graph_array + n,
16891dc43ddSMatthew Dillon 					   sizeof(*flame_graph_array));
16991dc43ddSMatthew Dillon 		if (error == 0)
17091dc43ddSMatthew Dillon 			error = SYSCTL_OUT(req, flame_graph_array[n].fge,
17191dc43ddSMatthew Dillon 					   sizeof(*flame_graph_array->fge) *
17291dc43ddSMatthew Dillon 					   FLAME_GRAPH_NENTRIES);
17391dc43ddSMatthew Dillon 	}
17491dc43ddSMatthew Dillon 
17591dc43ddSMatthew Dillon 	return error;
17691dc43ddSMatthew Dillon }
17791dc43ddSMatthew Dillon 
17891dc43ddSMatthew Dillon SYSCTL_PROC(_debug, OID_AUTO, flame_graph_data,
17991dc43ddSMatthew Dillon 	    (CTLTYPE_OPAQUE|CTLFLAG_RD), 0, 0,
18091dc43ddSMatthew Dillon 	    sysctl_flame_graph_data, "S,flames", "Flame Graph Data");
18191dc43ddSMatthew Dillon 
18291dc43ddSMatthew Dillon static int
sysctl_flame_graph_sniff(SYSCTL_HANDLER_ARGS)18391dc43ddSMatthew Dillon sysctl_flame_graph_sniff(SYSCTL_HANDLER_ARGS)
18491dc43ddSMatthew Dillon {
18591dc43ddSMatthew Dillon 	int error;
18691dc43ddSMatthew Dillon 
187*2b3f93eaSMatthew Dillon 	error = caps_priv_check_self(SYSCAP_RESTRICTEDROOT);
18891dc43ddSMatthew Dillon 	if (error)
18991dc43ddSMatthew Dillon 		return error;
19091dc43ddSMatthew Dillon 	if (flame_graph_enable == 0)
19191dc43ddSMatthew Dillon 		return EINVAL;
19291dc43ddSMatthew Dillon 	if (req->newptr)
19391dc43ddSMatthew Dillon 		smp_sniff();
19491dc43ddSMatthew Dillon 	return(SYSCTL_OUT(req, &error, sizeof(int)));
19591dc43ddSMatthew Dillon }
19691dc43ddSMatthew Dillon 
19791dc43ddSMatthew Dillon SYSCTL_PROC(_debug, OID_AUTO, flame_graph_sniff,
19891dc43ddSMatthew Dillon 	    (CTLTYPE_UINT|CTLFLAG_RW), 0, 0,
19991dc43ddSMatthew Dillon 	    sysctl_flame_graph_sniff, "IU", "Flame Graph Poll");
200