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