1*387f9dfdSAndroid Build Coastguard Worker // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2*387f9dfdSAndroid Build Coastguard Worker // Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
3*387f9dfdSAndroid Build Coastguard Worker //
4*387f9dfdSAndroid Build Coastguard Worker // Based on memleak(8) from BCC by Sasha Goldshtein and others.
5*387f9dfdSAndroid Build Coastguard Worker // 1-Mar-2023 JP Kobryn Created this.
6*387f9dfdSAndroid Build Coastguard Worker #include <argp.h>
7*387f9dfdSAndroid Build Coastguard Worker #include <errno.h>
8*387f9dfdSAndroid Build Coastguard Worker #include <signal.h>
9*387f9dfdSAndroid Build Coastguard Worker #include <stdbool.h>
10*387f9dfdSAndroid Build Coastguard Worker #include <stdio.h>
11*387f9dfdSAndroid Build Coastguard Worker #include <stdint.h>
12*387f9dfdSAndroid Build Coastguard Worker #include <stdlib.h>
13*387f9dfdSAndroid Build Coastguard Worker #include <string.h>
14*387f9dfdSAndroid Build Coastguard Worker #include <sys/eventfd.h>
15*387f9dfdSAndroid Build Coastguard Worker #include <sys/types.h>
16*387f9dfdSAndroid Build Coastguard Worker #include <sys/wait.h>
17*387f9dfdSAndroid Build Coastguard Worker #include <time.h>
18*387f9dfdSAndroid Build Coastguard Worker #include <unistd.h>
19*387f9dfdSAndroid Build Coastguard Worker
20*387f9dfdSAndroid Build Coastguard Worker #include <bpf/libbpf.h>
21*387f9dfdSAndroid Build Coastguard Worker #include <bpf/bpf.h>
22*387f9dfdSAndroid Build Coastguard Worker
23*387f9dfdSAndroid Build Coastguard Worker #include "memleak.h"
24*387f9dfdSAndroid Build Coastguard Worker #include "memleak.skel.h"
25*387f9dfdSAndroid Build Coastguard Worker #include "trace_helpers.h"
26*387f9dfdSAndroid Build Coastguard Worker
27*387f9dfdSAndroid Build Coastguard Worker #ifdef USE_BLAZESYM
28*387f9dfdSAndroid Build Coastguard Worker #include "blazesym.h"
29*387f9dfdSAndroid Build Coastguard Worker #endif
30*387f9dfdSAndroid Build Coastguard Worker
31*387f9dfdSAndroid Build Coastguard Worker static struct env {
32*387f9dfdSAndroid Build Coastguard Worker int interval;
33*387f9dfdSAndroid Build Coastguard Worker int nr_intervals;
34*387f9dfdSAndroid Build Coastguard Worker pid_t pid;
35*387f9dfdSAndroid Build Coastguard Worker bool trace_all;
36*387f9dfdSAndroid Build Coastguard Worker bool show_allocs;
37*387f9dfdSAndroid Build Coastguard Worker bool combined_only;
38*387f9dfdSAndroid Build Coastguard Worker int min_age_ns;
39*387f9dfdSAndroid Build Coastguard Worker uint64_t sample_rate;
40*387f9dfdSAndroid Build Coastguard Worker int top_stacks;
41*387f9dfdSAndroid Build Coastguard Worker size_t min_size;
42*387f9dfdSAndroid Build Coastguard Worker size_t max_size;
43*387f9dfdSAndroid Build Coastguard Worker char object[32];
44*387f9dfdSAndroid Build Coastguard Worker
45*387f9dfdSAndroid Build Coastguard Worker bool wa_missing_free;
46*387f9dfdSAndroid Build Coastguard Worker bool percpu;
47*387f9dfdSAndroid Build Coastguard Worker int perf_max_stack_depth;
48*387f9dfdSAndroid Build Coastguard Worker int stack_map_max_entries;
49*387f9dfdSAndroid Build Coastguard Worker long page_size;
50*387f9dfdSAndroid Build Coastguard Worker bool kernel_trace;
51*387f9dfdSAndroid Build Coastguard Worker bool verbose;
52*387f9dfdSAndroid Build Coastguard Worker char command[32];
53*387f9dfdSAndroid Build Coastguard Worker } env = {
54*387f9dfdSAndroid Build Coastguard Worker .interval = 5, // posarg 1
55*387f9dfdSAndroid Build Coastguard Worker .nr_intervals = -1, // posarg 2
56*387f9dfdSAndroid Build Coastguard Worker .pid = -1, // -p --pid
57*387f9dfdSAndroid Build Coastguard Worker .trace_all = false, // -t --trace
58*387f9dfdSAndroid Build Coastguard Worker .show_allocs = false, // -a --show-allocs
59*387f9dfdSAndroid Build Coastguard Worker .combined_only = false, // --combined-only
60*387f9dfdSAndroid Build Coastguard Worker .min_age_ns = 500, // -o --older (arg * 1e6)
61*387f9dfdSAndroid Build Coastguard Worker .wa_missing_free = false, // --wa-missing-free
62*387f9dfdSAndroid Build Coastguard Worker .sample_rate = 1, // -s --sample-rate
63*387f9dfdSAndroid Build Coastguard Worker .top_stacks = 10, // -T --top
64*387f9dfdSAndroid Build Coastguard Worker .min_size = 0, // -z --min-size
65*387f9dfdSAndroid Build Coastguard Worker .max_size = -1, // -Z --max-size
66*387f9dfdSAndroid Build Coastguard Worker .object = {0}, // -O --obj
67*387f9dfdSAndroid Build Coastguard Worker .percpu = false, // --percpu
68*387f9dfdSAndroid Build Coastguard Worker .perf_max_stack_depth = 127,
69*387f9dfdSAndroid Build Coastguard Worker .stack_map_max_entries = 10240,
70*387f9dfdSAndroid Build Coastguard Worker .page_size = 1,
71*387f9dfdSAndroid Build Coastguard Worker .kernel_trace = true,
72*387f9dfdSAndroid Build Coastguard Worker .verbose = false,
73*387f9dfdSAndroid Build Coastguard Worker .command = {0}, // -c --command
74*387f9dfdSAndroid Build Coastguard Worker };
75*387f9dfdSAndroid Build Coastguard Worker
76*387f9dfdSAndroid Build Coastguard Worker struct allocation_node {
77*387f9dfdSAndroid Build Coastguard Worker uint64_t address;
78*387f9dfdSAndroid Build Coastguard Worker size_t size;
79*387f9dfdSAndroid Build Coastguard Worker struct allocation_node* next;
80*387f9dfdSAndroid Build Coastguard Worker };
81*387f9dfdSAndroid Build Coastguard Worker
82*387f9dfdSAndroid Build Coastguard Worker struct allocation {
83*387f9dfdSAndroid Build Coastguard Worker uint64_t stack_id;
84*387f9dfdSAndroid Build Coastguard Worker size_t size;
85*387f9dfdSAndroid Build Coastguard Worker size_t count;
86*387f9dfdSAndroid Build Coastguard Worker struct allocation_node* allocations;
87*387f9dfdSAndroid Build Coastguard Worker };
88*387f9dfdSAndroid Build Coastguard Worker
89*387f9dfdSAndroid Build Coastguard Worker #define __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe) \
90*387f9dfdSAndroid Build Coastguard Worker do { \
91*387f9dfdSAndroid Build Coastguard Worker LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts, \
92*387f9dfdSAndroid Build Coastguard Worker .func_name = #sym_name, \
93*387f9dfdSAndroid Build Coastguard Worker .retprobe = is_retprobe); \
94*387f9dfdSAndroid Build Coastguard Worker skel->links.prog_name = bpf_program__attach_uprobe_opts( \
95*387f9dfdSAndroid Build Coastguard Worker skel->progs.prog_name, \
96*387f9dfdSAndroid Build Coastguard Worker env.pid, \
97*387f9dfdSAndroid Build Coastguard Worker env.object, \
98*387f9dfdSAndroid Build Coastguard Worker 0, \
99*387f9dfdSAndroid Build Coastguard Worker &uprobe_opts); \
100*387f9dfdSAndroid Build Coastguard Worker } while (false)
101*387f9dfdSAndroid Build Coastguard Worker
102*387f9dfdSAndroid Build Coastguard Worker #define __CHECK_PROGRAM(skel, prog_name) \
103*387f9dfdSAndroid Build Coastguard Worker do { \
104*387f9dfdSAndroid Build Coastguard Worker if (!skel->links.prog_name) { \
105*387f9dfdSAndroid Build Coastguard Worker perror("no program attached for " #prog_name); \
106*387f9dfdSAndroid Build Coastguard Worker return -errno; \
107*387f9dfdSAndroid Build Coastguard Worker } \
108*387f9dfdSAndroid Build Coastguard Worker } while (false)
109*387f9dfdSAndroid Build Coastguard Worker
110*387f9dfdSAndroid Build Coastguard Worker #define __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, is_retprobe) \
111*387f9dfdSAndroid Build Coastguard Worker do { \
112*387f9dfdSAndroid Build Coastguard Worker __ATTACH_UPROBE(skel, sym_name, prog_name, is_retprobe); \
113*387f9dfdSAndroid Build Coastguard Worker __CHECK_PROGRAM(skel, prog_name); \
114*387f9dfdSAndroid Build Coastguard Worker } while (false)
115*387f9dfdSAndroid Build Coastguard Worker
116*387f9dfdSAndroid Build Coastguard Worker #define ATTACH_UPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, false)
117*387f9dfdSAndroid Build Coastguard Worker #define ATTACH_URETPROBE(skel, sym_name, prog_name) __ATTACH_UPROBE(skel, sym_name, prog_name, true)
118*387f9dfdSAndroid Build Coastguard Worker
119*387f9dfdSAndroid Build Coastguard Worker #define ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, false)
120*387f9dfdSAndroid Build Coastguard Worker #define ATTACH_URETPROBE_CHECKED(skel, sym_name, prog_name) __ATTACH_UPROBE_CHECKED(skel, sym_name, prog_name, true)
121*387f9dfdSAndroid Build Coastguard Worker
122*387f9dfdSAndroid Build Coastguard Worker static void sig_handler(int signo);
123*387f9dfdSAndroid Build Coastguard Worker
124*387f9dfdSAndroid Build Coastguard Worker static long argp_parse_long(int key, const char *arg, struct argp_state *state);
125*387f9dfdSAndroid Build Coastguard Worker static error_t argp_parse_arg(int key, char *arg, struct argp_state *state);
126*387f9dfdSAndroid Build Coastguard Worker
127*387f9dfdSAndroid Build Coastguard Worker static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args);
128*387f9dfdSAndroid Build Coastguard Worker
129*387f9dfdSAndroid Build Coastguard Worker static int event_init(int *fd);
130*387f9dfdSAndroid Build Coastguard Worker static int event_wait(int fd, uint64_t expected_event);
131*387f9dfdSAndroid Build Coastguard Worker static int event_notify(int fd, uint64_t event);
132*387f9dfdSAndroid Build Coastguard Worker
133*387f9dfdSAndroid Build Coastguard Worker static pid_t fork_sync_exec(const char *command, int fd);
134*387f9dfdSAndroid Build Coastguard Worker
135*387f9dfdSAndroid Build Coastguard Worker #ifdef USE_BLAZESYM
136*387f9dfdSAndroid Build Coastguard Worker static void print_stack_frame_by_blazesym(size_t frame, uint64_t addr, const blazesym_csym *sym);
137*387f9dfdSAndroid Build Coastguard Worker static void print_stack_frames_by_blazesym();
138*387f9dfdSAndroid Build Coastguard Worker #else
139*387f9dfdSAndroid Build Coastguard Worker static void print_stack_frames_by_ksyms();
140*387f9dfdSAndroid Build Coastguard Worker static void print_stack_frames_by_syms_cache();
141*387f9dfdSAndroid Build Coastguard Worker #endif
142*387f9dfdSAndroid Build Coastguard Worker static int print_stack_frames(struct allocation *allocs, size_t nr_allocs, int stack_traces_fd);
143*387f9dfdSAndroid Build Coastguard Worker
144*387f9dfdSAndroid Build Coastguard Worker static int alloc_size_compare(const void *a, const void *b);
145*387f9dfdSAndroid Build Coastguard Worker
146*387f9dfdSAndroid Build Coastguard Worker static int print_outstanding_allocs(int allocs_fd, int stack_traces_fd);
147*387f9dfdSAndroid Build Coastguard Worker static int print_outstanding_combined_allocs(int combined_allocs_fd, int stack_traces_fd);
148*387f9dfdSAndroid Build Coastguard Worker
149*387f9dfdSAndroid Build Coastguard Worker static bool has_kernel_node_tracepoints();
150*387f9dfdSAndroid Build Coastguard Worker static void disable_kernel_node_tracepoints(struct memleak_bpf *skel);
151*387f9dfdSAndroid Build Coastguard Worker static void disable_kernel_percpu_tracepoints(struct memleak_bpf *skel);
152*387f9dfdSAndroid Build Coastguard Worker static void disable_kernel_tracepoints(struct memleak_bpf *skel);
153*387f9dfdSAndroid Build Coastguard Worker
154*387f9dfdSAndroid Build Coastguard Worker static int attach_uprobes(struct memleak_bpf *skel);
155*387f9dfdSAndroid Build Coastguard Worker
156*387f9dfdSAndroid Build Coastguard Worker const char *argp_program_version = "memleak 0.1";
157*387f9dfdSAndroid Build Coastguard Worker const char *argp_program_bug_address =
158*387f9dfdSAndroid Build Coastguard Worker "https://github.com/iovisor/bcc/tree/master/libbpf-tools";
159*387f9dfdSAndroid Build Coastguard Worker
160*387f9dfdSAndroid Build Coastguard Worker const char argp_args_doc[] =
161*387f9dfdSAndroid Build Coastguard Worker "Trace outstanding memory allocations\n"
162*387f9dfdSAndroid Build Coastguard Worker "\n"
163*387f9dfdSAndroid Build Coastguard Worker "USAGE: memleak [-h] [-c COMMAND] [-p PID] [-t] [-n] [-a] [-o AGE_MS] [-C] [-F] [-s SAMPLE_RATE] [-T TOP_STACKS] [-z MIN_SIZE] [-Z MAX_SIZE] [-O OBJECT] [-P] [INTERVAL] [INTERVALS]\n"
164*387f9dfdSAndroid Build Coastguard Worker "\n"
165*387f9dfdSAndroid Build Coastguard Worker "EXAMPLES:\n"
166*387f9dfdSAndroid Build Coastguard Worker "./memleak -p $(pidof allocs)\n"
167*387f9dfdSAndroid Build Coastguard Worker " Trace allocations and display a summary of 'leaked' (outstanding)\n"
168*387f9dfdSAndroid Build Coastguard Worker " allocations every 5 seconds\n"
169*387f9dfdSAndroid Build Coastguard Worker "./memleak -p $(pidof allocs) -t\n"
170*387f9dfdSAndroid Build Coastguard Worker " Trace allocations and display each individual allocator function call\n"
171*387f9dfdSAndroid Build Coastguard Worker "./memleak -ap $(pidof allocs) 10\n"
172*387f9dfdSAndroid Build Coastguard Worker " Trace allocations and display allocated addresses, sizes, and stacks\n"
173*387f9dfdSAndroid Build Coastguard Worker " every 10 seconds for outstanding allocations\n"
174*387f9dfdSAndroid Build Coastguard Worker "./memleak -c './allocs'\n"
175*387f9dfdSAndroid Build Coastguard Worker " Run the specified command and trace its allocations\n"
176*387f9dfdSAndroid Build Coastguard Worker "./memleak\n"
177*387f9dfdSAndroid Build Coastguard Worker " Trace allocations in kernel mode and display a summary of outstanding\n"
178*387f9dfdSAndroid Build Coastguard Worker " allocations every 5 seconds\n"
179*387f9dfdSAndroid Build Coastguard Worker "./memleak -o 60000\n"
180*387f9dfdSAndroid Build Coastguard Worker " Trace allocations in kernel mode and display a summary of outstanding\n"
181*387f9dfdSAndroid Build Coastguard Worker " allocations that are at least one minute (60 seconds) old\n"
182*387f9dfdSAndroid Build Coastguard Worker "./memleak -s 5\n"
183*387f9dfdSAndroid Build Coastguard Worker " Trace roughly every 5th allocation, to reduce overhead\n"
184*387f9dfdSAndroid Build Coastguard Worker "";
185*387f9dfdSAndroid Build Coastguard Worker
186*387f9dfdSAndroid Build Coastguard Worker static const struct argp_option argp_options[] = {
187*387f9dfdSAndroid Build Coastguard Worker // name/longopt:str, key/shortopt:int, arg:str, flags:int, doc:str
188*387f9dfdSAndroid Build Coastguard Worker {"pid", 'p', "PID", 0, "process ID to trace. if not specified, trace kernel allocs"},
189*387f9dfdSAndroid Build Coastguard Worker {"trace", 't', 0, 0, "print trace messages for each alloc/free call" },
190*387f9dfdSAndroid Build Coastguard Worker {"show-allocs", 'a', 0, 0, "show allocation addresses and sizes as well as call stacks"},
191*387f9dfdSAndroid Build Coastguard Worker {"older", 'o', "AGE_MS", 0, "prune allocations younger than this age in milliseconds"},
192*387f9dfdSAndroid Build Coastguard Worker {"command", 'c', "COMMAND", 0, "execute and trace the specified command"},
193*387f9dfdSAndroid Build Coastguard Worker {"combined-only", 'C', 0, 0, "show combined allocation statistics only"},
194*387f9dfdSAndroid Build Coastguard Worker {"wa-missing-free", 'F', 0, 0, "workaround to alleviate misjudgments when free is missing"},
195*387f9dfdSAndroid Build Coastguard Worker {"sample-rate", 's', "SAMPLE_RATE", 0, "sample every N-th allocation to decrease the overhead"},
196*387f9dfdSAndroid Build Coastguard Worker {"top", 'T', "TOP_STACKS", 0, "display only this many top allocating stacks (by size)"},
197*387f9dfdSAndroid Build Coastguard Worker {"min-size", 'z', "MIN_SIZE", 0, "capture only allocations larger than this size"},
198*387f9dfdSAndroid Build Coastguard Worker {"max-size", 'Z', "MAX_SIZE", 0, "capture only allocations smaller than this size"},
199*387f9dfdSAndroid Build Coastguard Worker {"obj", 'O', "OBJECT", 0, "attach to allocator functions in the specified object"},
200*387f9dfdSAndroid Build Coastguard Worker {"percpu", 'P', NULL, 0, "trace percpu allocations"},
201*387f9dfdSAndroid Build Coastguard Worker {},
202*387f9dfdSAndroid Build Coastguard Worker };
203*387f9dfdSAndroid Build Coastguard Worker
204*387f9dfdSAndroid Build Coastguard Worker static volatile sig_atomic_t exiting;
205*387f9dfdSAndroid Build Coastguard Worker static volatile sig_atomic_t child_exited;
206*387f9dfdSAndroid Build Coastguard Worker
207*387f9dfdSAndroid Build Coastguard Worker static struct sigaction sig_action = {
208*387f9dfdSAndroid Build Coastguard Worker .sa_handler = sig_handler
209*387f9dfdSAndroid Build Coastguard Worker };
210*387f9dfdSAndroid Build Coastguard Worker
211*387f9dfdSAndroid Build Coastguard Worker static int child_exec_event_fd = -1;
212*387f9dfdSAndroid Build Coastguard Worker
213*387f9dfdSAndroid Build Coastguard Worker #ifdef USE_BLAZESYM
214*387f9dfdSAndroid Build Coastguard Worker static blazesym *symbolizer;
215*387f9dfdSAndroid Build Coastguard Worker static sym_src_cfg src_cfg;
216*387f9dfdSAndroid Build Coastguard Worker #else
217*387f9dfdSAndroid Build Coastguard Worker struct syms_cache *syms_cache;
218*387f9dfdSAndroid Build Coastguard Worker struct ksyms *ksyms;
219*387f9dfdSAndroid Build Coastguard Worker #endif
220*387f9dfdSAndroid Build Coastguard Worker static void (*print_stack_frames_func)();
221*387f9dfdSAndroid Build Coastguard Worker
222*387f9dfdSAndroid Build Coastguard Worker static uint64_t *stack;
223*387f9dfdSAndroid Build Coastguard Worker
224*387f9dfdSAndroid Build Coastguard Worker static struct allocation *allocs;
225*387f9dfdSAndroid Build Coastguard Worker
226*387f9dfdSAndroid Build Coastguard Worker static const char default_object[] = "libc.so.6";
227*387f9dfdSAndroid Build Coastguard Worker
main(int argc,char * argv[])228*387f9dfdSAndroid Build Coastguard Worker int main(int argc, char *argv[])
229*387f9dfdSAndroid Build Coastguard Worker {
230*387f9dfdSAndroid Build Coastguard Worker int ret = 0;
231*387f9dfdSAndroid Build Coastguard Worker struct memleak_bpf *skel = NULL;
232*387f9dfdSAndroid Build Coastguard Worker
233*387f9dfdSAndroid Build Coastguard Worker static const struct argp argp = {
234*387f9dfdSAndroid Build Coastguard Worker .options = argp_options,
235*387f9dfdSAndroid Build Coastguard Worker .parser = argp_parse_arg,
236*387f9dfdSAndroid Build Coastguard Worker .doc = argp_args_doc,
237*387f9dfdSAndroid Build Coastguard Worker };
238*387f9dfdSAndroid Build Coastguard Worker
239*387f9dfdSAndroid Build Coastguard Worker // parse command line args to env settings
240*387f9dfdSAndroid Build Coastguard Worker if (argp_parse(&argp, argc, argv, 0, NULL, NULL)) {
241*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to parse args\n");
242*387f9dfdSAndroid Build Coastguard Worker
243*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
244*387f9dfdSAndroid Build Coastguard Worker }
245*387f9dfdSAndroid Build Coastguard Worker
246*387f9dfdSAndroid Build Coastguard Worker // install signal handler
247*387f9dfdSAndroid Build Coastguard Worker if (sigaction(SIGINT, &sig_action, NULL) || sigaction(SIGCHLD, &sig_action, NULL)) {
248*387f9dfdSAndroid Build Coastguard Worker perror("failed to set up signal handling");
249*387f9dfdSAndroid Build Coastguard Worker ret = -errno;
250*387f9dfdSAndroid Build Coastguard Worker
251*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
252*387f9dfdSAndroid Build Coastguard Worker }
253*387f9dfdSAndroid Build Coastguard Worker
254*387f9dfdSAndroid Build Coastguard Worker // post-processing and validation of env settings
255*387f9dfdSAndroid Build Coastguard Worker if (env.min_size > env.max_size) {
256*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "min size (-z) can't be greater than max_size (-Z)\n");
257*387f9dfdSAndroid Build Coastguard Worker return 1;
258*387f9dfdSAndroid Build Coastguard Worker }
259*387f9dfdSAndroid Build Coastguard Worker
260*387f9dfdSAndroid Build Coastguard Worker if (!strlen(env.object)) {
261*387f9dfdSAndroid Build Coastguard Worker printf("using default object: %s\n", default_object);
262*387f9dfdSAndroid Build Coastguard Worker strncpy(env.object, default_object, sizeof(env.object) - 1);
263*387f9dfdSAndroid Build Coastguard Worker }
264*387f9dfdSAndroid Build Coastguard Worker
265*387f9dfdSAndroid Build Coastguard Worker env.page_size = sysconf(_SC_PAGE_SIZE);
266*387f9dfdSAndroid Build Coastguard Worker printf("using page size: %ld\n", env.page_size);
267*387f9dfdSAndroid Build Coastguard Worker
268*387f9dfdSAndroid Build Coastguard Worker env.kernel_trace = env.pid < 0 && !strlen(env.command);
269*387f9dfdSAndroid Build Coastguard Worker printf("tracing kernel: %s\n", env.kernel_trace ? "true" : "false");
270*387f9dfdSAndroid Build Coastguard Worker
271*387f9dfdSAndroid Build Coastguard Worker // if specific userspace program was specified,
272*387f9dfdSAndroid Build Coastguard Worker // create the child process and use an eventfd to synchronize the call to exec()
273*387f9dfdSAndroid Build Coastguard Worker if (strlen(env.command)) {
274*387f9dfdSAndroid Build Coastguard Worker if (env.pid >= 0) {
275*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "cannot specify both command and pid\n");
276*387f9dfdSAndroid Build Coastguard Worker ret = 1;
277*387f9dfdSAndroid Build Coastguard Worker
278*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
279*387f9dfdSAndroid Build Coastguard Worker }
280*387f9dfdSAndroid Build Coastguard Worker
281*387f9dfdSAndroid Build Coastguard Worker if (event_init(&child_exec_event_fd)) {
282*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to init child event\n");
283*387f9dfdSAndroid Build Coastguard Worker
284*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
285*387f9dfdSAndroid Build Coastguard Worker }
286*387f9dfdSAndroid Build Coastguard Worker
287*387f9dfdSAndroid Build Coastguard Worker const pid_t child_pid = fork_sync_exec(env.command, child_exec_event_fd);
288*387f9dfdSAndroid Build Coastguard Worker if (child_pid < 0) {
289*387f9dfdSAndroid Build Coastguard Worker perror("failed to spawn child process");
290*387f9dfdSAndroid Build Coastguard Worker ret = -errno;
291*387f9dfdSAndroid Build Coastguard Worker
292*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
293*387f9dfdSAndroid Build Coastguard Worker }
294*387f9dfdSAndroid Build Coastguard Worker
295*387f9dfdSAndroid Build Coastguard Worker env.pid = child_pid;
296*387f9dfdSAndroid Build Coastguard Worker }
297*387f9dfdSAndroid Build Coastguard Worker
298*387f9dfdSAndroid Build Coastguard Worker // allocate space for storing a stack trace
299*387f9dfdSAndroid Build Coastguard Worker stack = calloc(env.perf_max_stack_depth, sizeof(*stack));
300*387f9dfdSAndroid Build Coastguard Worker if (!stack) {
301*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to allocate stack array\n");
302*387f9dfdSAndroid Build Coastguard Worker ret = -ENOMEM;
303*387f9dfdSAndroid Build Coastguard Worker
304*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
305*387f9dfdSAndroid Build Coastguard Worker }
306*387f9dfdSAndroid Build Coastguard Worker
307*387f9dfdSAndroid Build Coastguard Worker #ifdef USE_BLAZESYM
308*387f9dfdSAndroid Build Coastguard Worker if (env.pid < 0) {
309*387f9dfdSAndroid Build Coastguard Worker src_cfg.src_type = SRC_T_KERNEL;
310*387f9dfdSAndroid Build Coastguard Worker src_cfg.params.kernel.kallsyms = NULL;
311*387f9dfdSAndroid Build Coastguard Worker src_cfg.params.kernel.kernel_image = NULL;
312*387f9dfdSAndroid Build Coastguard Worker } else {
313*387f9dfdSAndroid Build Coastguard Worker src_cfg.src_type = SRC_T_PROCESS;
314*387f9dfdSAndroid Build Coastguard Worker src_cfg.params.process.pid = env.pid;
315*387f9dfdSAndroid Build Coastguard Worker }
316*387f9dfdSAndroid Build Coastguard Worker #endif
317*387f9dfdSAndroid Build Coastguard Worker
318*387f9dfdSAndroid Build Coastguard Worker // allocate space for storing "allocation" structs
319*387f9dfdSAndroid Build Coastguard Worker if (env.combined_only)
320*387f9dfdSAndroid Build Coastguard Worker allocs = calloc(COMBINED_ALLOCS_MAX_ENTRIES, sizeof(*allocs));
321*387f9dfdSAndroid Build Coastguard Worker else
322*387f9dfdSAndroid Build Coastguard Worker allocs = calloc(ALLOCS_MAX_ENTRIES, sizeof(*allocs));
323*387f9dfdSAndroid Build Coastguard Worker
324*387f9dfdSAndroid Build Coastguard Worker if (!allocs) {
325*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to allocate array\n");
326*387f9dfdSAndroid Build Coastguard Worker ret = -ENOMEM;
327*387f9dfdSAndroid Build Coastguard Worker
328*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
329*387f9dfdSAndroid Build Coastguard Worker }
330*387f9dfdSAndroid Build Coastguard Worker
331*387f9dfdSAndroid Build Coastguard Worker libbpf_set_print(libbpf_print_fn);
332*387f9dfdSAndroid Build Coastguard Worker
333*387f9dfdSAndroid Build Coastguard Worker skel = memleak_bpf__open();
334*387f9dfdSAndroid Build Coastguard Worker if (!skel) {
335*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to open bpf object\n");
336*387f9dfdSAndroid Build Coastguard Worker ret = 1;
337*387f9dfdSAndroid Build Coastguard Worker
338*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
339*387f9dfdSAndroid Build Coastguard Worker }
340*387f9dfdSAndroid Build Coastguard Worker
341*387f9dfdSAndroid Build Coastguard Worker skel->rodata->min_size = env.min_size;
342*387f9dfdSAndroid Build Coastguard Worker skel->rodata->max_size = env.max_size;
343*387f9dfdSAndroid Build Coastguard Worker skel->rodata->page_size = env.page_size;
344*387f9dfdSAndroid Build Coastguard Worker skel->rodata->sample_rate = env.sample_rate;
345*387f9dfdSAndroid Build Coastguard Worker skel->rodata->trace_all = env.trace_all;
346*387f9dfdSAndroid Build Coastguard Worker skel->rodata->stack_flags = env.kernel_trace ? 0 : BPF_F_USER_STACK;
347*387f9dfdSAndroid Build Coastguard Worker skel->rodata->wa_missing_free = env.wa_missing_free;
348*387f9dfdSAndroid Build Coastguard Worker
349*387f9dfdSAndroid Build Coastguard Worker bpf_map__set_value_size(skel->maps.stack_traces,
350*387f9dfdSAndroid Build Coastguard Worker env.perf_max_stack_depth * sizeof(unsigned long));
351*387f9dfdSAndroid Build Coastguard Worker bpf_map__set_max_entries(skel->maps.stack_traces, env.stack_map_max_entries);
352*387f9dfdSAndroid Build Coastguard Worker
353*387f9dfdSAndroid Build Coastguard Worker // disable kernel tracepoints based on settings or availability
354*387f9dfdSAndroid Build Coastguard Worker if (env.kernel_trace) {
355*387f9dfdSAndroid Build Coastguard Worker if (!has_kernel_node_tracepoints())
356*387f9dfdSAndroid Build Coastguard Worker disable_kernel_node_tracepoints(skel);
357*387f9dfdSAndroid Build Coastguard Worker
358*387f9dfdSAndroid Build Coastguard Worker if (!env.percpu)
359*387f9dfdSAndroid Build Coastguard Worker disable_kernel_percpu_tracepoints(skel);
360*387f9dfdSAndroid Build Coastguard Worker } else {
361*387f9dfdSAndroid Build Coastguard Worker disable_kernel_tracepoints(skel);
362*387f9dfdSAndroid Build Coastguard Worker }
363*387f9dfdSAndroid Build Coastguard Worker
364*387f9dfdSAndroid Build Coastguard Worker ret = memleak_bpf__load(skel);
365*387f9dfdSAndroid Build Coastguard Worker if (ret) {
366*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to load bpf object\n");
367*387f9dfdSAndroid Build Coastguard Worker
368*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
369*387f9dfdSAndroid Build Coastguard Worker }
370*387f9dfdSAndroid Build Coastguard Worker
371*387f9dfdSAndroid Build Coastguard Worker const int allocs_fd = bpf_map__fd(skel->maps.allocs);
372*387f9dfdSAndroid Build Coastguard Worker const int combined_allocs_fd = bpf_map__fd(skel->maps.combined_allocs);
373*387f9dfdSAndroid Build Coastguard Worker const int stack_traces_fd = bpf_map__fd(skel->maps.stack_traces);
374*387f9dfdSAndroid Build Coastguard Worker
375*387f9dfdSAndroid Build Coastguard Worker // if userspace oriented, attach upbrobes
376*387f9dfdSAndroid Build Coastguard Worker if (!env.kernel_trace) {
377*387f9dfdSAndroid Build Coastguard Worker ret = attach_uprobes(skel);
378*387f9dfdSAndroid Build Coastguard Worker if (ret) {
379*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to attach uprobes\n");
380*387f9dfdSAndroid Build Coastguard Worker
381*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
382*387f9dfdSAndroid Build Coastguard Worker }
383*387f9dfdSAndroid Build Coastguard Worker }
384*387f9dfdSAndroid Build Coastguard Worker
385*387f9dfdSAndroid Build Coastguard Worker ret = memleak_bpf__attach(skel);
386*387f9dfdSAndroid Build Coastguard Worker if (ret) {
387*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to attach bpf program(s)\n");
388*387f9dfdSAndroid Build Coastguard Worker
389*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
390*387f9dfdSAndroid Build Coastguard Worker }
391*387f9dfdSAndroid Build Coastguard Worker
392*387f9dfdSAndroid Build Coastguard Worker // if running a specific userspace program,
393*387f9dfdSAndroid Build Coastguard Worker // notify the child process that it can exec its program
394*387f9dfdSAndroid Build Coastguard Worker if (strlen(env.command)) {
395*387f9dfdSAndroid Build Coastguard Worker ret = event_notify(child_exec_event_fd, 1);
396*387f9dfdSAndroid Build Coastguard Worker if (ret) {
397*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to notify child to perform exec\n");
398*387f9dfdSAndroid Build Coastguard Worker
399*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
400*387f9dfdSAndroid Build Coastguard Worker }
401*387f9dfdSAndroid Build Coastguard Worker }
402*387f9dfdSAndroid Build Coastguard Worker
403*387f9dfdSAndroid Build Coastguard Worker #ifdef USE_BLAZESYM
404*387f9dfdSAndroid Build Coastguard Worker symbolizer = blazesym_new();
405*387f9dfdSAndroid Build Coastguard Worker if (!symbolizer) {
406*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "Failed to load blazesym\n");
407*387f9dfdSAndroid Build Coastguard Worker ret = -ENOMEM;
408*387f9dfdSAndroid Build Coastguard Worker
409*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
410*387f9dfdSAndroid Build Coastguard Worker }
411*387f9dfdSAndroid Build Coastguard Worker print_stack_frames_func = print_stack_frames_by_blazesym;
412*387f9dfdSAndroid Build Coastguard Worker #else
413*387f9dfdSAndroid Build Coastguard Worker if (env.kernel_trace) {
414*387f9dfdSAndroid Build Coastguard Worker ksyms = ksyms__load();
415*387f9dfdSAndroid Build Coastguard Worker if (!ksyms) {
416*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "Failed to load ksyms\n");
417*387f9dfdSAndroid Build Coastguard Worker ret = -ENOMEM;
418*387f9dfdSAndroid Build Coastguard Worker
419*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
420*387f9dfdSAndroid Build Coastguard Worker }
421*387f9dfdSAndroid Build Coastguard Worker print_stack_frames_func = print_stack_frames_by_ksyms;
422*387f9dfdSAndroid Build Coastguard Worker } else {
423*387f9dfdSAndroid Build Coastguard Worker syms_cache = syms_cache__new(0);
424*387f9dfdSAndroid Build Coastguard Worker if (!syms_cache) {
425*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "Failed to create syms_cache\n");
426*387f9dfdSAndroid Build Coastguard Worker ret = -ENOMEM;
427*387f9dfdSAndroid Build Coastguard Worker
428*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
429*387f9dfdSAndroid Build Coastguard Worker }
430*387f9dfdSAndroid Build Coastguard Worker print_stack_frames_func = print_stack_frames_by_syms_cache;
431*387f9dfdSAndroid Build Coastguard Worker }
432*387f9dfdSAndroid Build Coastguard Worker #endif
433*387f9dfdSAndroid Build Coastguard Worker
434*387f9dfdSAndroid Build Coastguard Worker printf("Tracing outstanding memory allocs... Hit Ctrl-C to end\n");
435*387f9dfdSAndroid Build Coastguard Worker
436*387f9dfdSAndroid Build Coastguard Worker // main loop
437*387f9dfdSAndroid Build Coastguard Worker while (!exiting && env.nr_intervals) {
438*387f9dfdSAndroid Build Coastguard Worker env.nr_intervals--;
439*387f9dfdSAndroid Build Coastguard Worker
440*387f9dfdSAndroid Build Coastguard Worker sleep(env.interval);
441*387f9dfdSAndroid Build Coastguard Worker
442*387f9dfdSAndroid Build Coastguard Worker if (env.combined_only)
443*387f9dfdSAndroid Build Coastguard Worker print_outstanding_combined_allocs(combined_allocs_fd, stack_traces_fd);
444*387f9dfdSAndroid Build Coastguard Worker else
445*387f9dfdSAndroid Build Coastguard Worker print_outstanding_allocs(allocs_fd, stack_traces_fd);
446*387f9dfdSAndroid Build Coastguard Worker }
447*387f9dfdSAndroid Build Coastguard Worker
448*387f9dfdSAndroid Build Coastguard Worker // after loop ends, check for child process and cleanup accordingly
449*387f9dfdSAndroid Build Coastguard Worker if (env.pid > 0 && strlen(env.command)) {
450*387f9dfdSAndroid Build Coastguard Worker if (!child_exited) {
451*387f9dfdSAndroid Build Coastguard Worker if (kill(env.pid, SIGTERM)) {
452*387f9dfdSAndroid Build Coastguard Worker perror("failed to signal child process");
453*387f9dfdSAndroid Build Coastguard Worker ret = -errno;
454*387f9dfdSAndroid Build Coastguard Worker
455*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
456*387f9dfdSAndroid Build Coastguard Worker }
457*387f9dfdSAndroid Build Coastguard Worker printf("signaled child process\n");
458*387f9dfdSAndroid Build Coastguard Worker }
459*387f9dfdSAndroid Build Coastguard Worker
460*387f9dfdSAndroid Build Coastguard Worker if (waitpid(env.pid, NULL, 0) < 0) {
461*387f9dfdSAndroid Build Coastguard Worker perror("failed to reap child process");
462*387f9dfdSAndroid Build Coastguard Worker ret = -errno;
463*387f9dfdSAndroid Build Coastguard Worker
464*387f9dfdSAndroid Build Coastguard Worker goto cleanup;
465*387f9dfdSAndroid Build Coastguard Worker }
466*387f9dfdSAndroid Build Coastguard Worker printf("reaped child process\n");
467*387f9dfdSAndroid Build Coastguard Worker }
468*387f9dfdSAndroid Build Coastguard Worker
469*387f9dfdSAndroid Build Coastguard Worker cleanup:
470*387f9dfdSAndroid Build Coastguard Worker #ifdef USE_BLAZESYM
471*387f9dfdSAndroid Build Coastguard Worker blazesym_free(symbolizer);
472*387f9dfdSAndroid Build Coastguard Worker #else
473*387f9dfdSAndroid Build Coastguard Worker if (syms_cache)
474*387f9dfdSAndroid Build Coastguard Worker syms_cache__free(syms_cache);
475*387f9dfdSAndroid Build Coastguard Worker if (ksyms)
476*387f9dfdSAndroid Build Coastguard Worker ksyms__free(ksyms);
477*387f9dfdSAndroid Build Coastguard Worker #endif
478*387f9dfdSAndroid Build Coastguard Worker memleak_bpf__destroy(skel);
479*387f9dfdSAndroid Build Coastguard Worker
480*387f9dfdSAndroid Build Coastguard Worker free(allocs);
481*387f9dfdSAndroid Build Coastguard Worker free(stack);
482*387f9dfdSAndroid Build Coastguard Worker
483*387f9dfdSAndroid Build Coastguard Worker printf("done\n");
484*387f9dfdSAndroid Build Coastguard Worker
485*387f9dfdSAndroid Build Coastguard Worker return ret;
486*387f9dfdSAndroid Build Coastguard Worker }
487*387f9dfdSAndroid Build Coastguard Worker
argp_parse_long(int key,const char * arg,struct argp_state * state)488*387f9dfdSAndroid Build Coastguard Worker long argp_parse_long(int key, const char *arg, struct argp_state *state)
489*387f9dfdSAndroid Build Coastguard Worker {
490*387f9dfdSAndroid Build Coastguard Worker errno = 0;
491*387f9dfdSAndroid Build Coastguard Worker const long temp = strtol(arg, NULL, 10);
492*387f9dfdSAndroid Build Coastguard Worker if (errno || temp <= 0) {
493*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "error arg:%c %s\n", (char)key, arg);
494*387f9dfdSAndroid Build Coastguard Worker argp_usage(state);
495*387f9dfdSAndroid Build Coastguard Worker }
496*387f9dfdSAndroid Build Coastguard Worker
497*387f9dfdSAndroid Build Coastguard Worker return temp;
498*387f9dfdSAndroid Build Coastguard Worker }
499*387f9dfdSAndroid Build Coastguard Worker
argp_parse_arg(int key,char * arg,struct argp_state * state)500*387f9dfdSAndroid Build Coastguard Worker error_t argp_parse_arg(int key, char *arg, struct argp_state *state)
501*387f9dfdSAndroid Build Coastguard Worker {
502*387f9dfdSAndroid Build Coastguard Worker static int pos_args = 0;
503*387f9dfdSAndroid Build Coastguard Worker
504*387f9dfdSAndroid Build Coastguard Worker switch (key) {
505*387f9dfdSAndroid Build Coastguard Worker case 'p':
506*387f9dfdSAndroid Build Coastguard Worker env.pid = atoi(arg);
507*387f9dfdSAndroid Build Coastguard Worker break;
508*387f9dfdSAndroid Build Coastguard Worker case 't':
509*387f9dfdSAndroid Build Coastguard Worker env.trace_all = true;
510*387f9dfdSAndroid Build Coastguard Worker break;
511*387f9dfdSAndroid Build Coastguard Worker case 'a':
512*387f9dfdSAndroid Build Coastguard Worker env.show_allocs = true;
513*387f9dfdSAndroid Build Coastguard Worker break;
514*387f9dfdSAndroid Build Coastguard Worker case 'o':
515*387f9dfdSAndroid Build Coastguard Worker env.min_age_ns = 1e6 * atoi(arg);
516*387f9dfdSAndroid Build Coastguard Worker break;
517*387f9dfdSAndroid Build Coastguard Worker case 'c':
518*387f9dfdSAndroid Build Coastguard Worker strncpy(env.command, arg, sizeof(env.command) - 1);
519*387f9dfdSAndroid Build Coastguard Worker break;
520*387f9dfdSAndroid Build Coastguard Worker case 'C':
521*387f9dfdSAndroid Build Coastguard Worker env.combined_only = true;
522*387f9dfdSAndroid Build Coastguard Worker break;
523*387f9dfdSAndroid Build Coastguard Worker case 'F':
524*387f9dfdSAndroid Build Coastguard Worker env.wa_missing_free = true;
525*387f9dfdSAndroid Build Coastguard Worker break;
526*387f9dfdSAndroid Build Coastguard Worker case 's':
527*387f9dfdSAndroid Build Coastguard Worker env.sample_rate = argp_parse_long(key, arg, state);
528*387f9dfdSAndroid Build Coastguard Worker break;
529*387f9dfdSAndroid Build Coastguard Worker case 'T':
530*387f9dfdSAndroid Build Coastguard Worker env.top_stacks = atoi(arg);
531*387f9dfdSAndroid Build Coastguard Worker break;
532*387f9dfdSAndroid Build Coastguard Worker case 'z':
533*387f9dfdSAndroid Build Coastguard Worker env.min_size = argp_parse_long(key, arg, state);
534*387f9dfdSAndroid Build Coastguard Worker break;
535*387f9dfdSAndroid Build Coastguard Worker case 'Z':
536*387f9dfdSAndroid Build Coastguard Worker env.max_size = argp_parse_long(key, arg, state);
537*387f9dfdSAndroid Build Coastguard Worker break;
538*387f9dfdSAndroid Build Coastguard Worker case 'O':
539*387f9dfdSAndroid Build Coastguard Worker strncpy(env.object, arg, sizeof(env.object) - 1);
540*387f9dfdSAndroid Build Coastguard Worker break;
541*387f9dfdSAndroid Build Coastguard Worker case 'P':
542*387f9dfdSAndroid Build Coastguard Worker env.percpu = true;
543*387f9dfdSAndroid Build Coastguard Worker break;
544*387f9dfdSAndroid Build Coastguard Worker case ARGP_KEY_ARG:
545*387f9dfdSAndroid Build Coastguard Worker pos_args++;
546*387f9dfdSAndroid Build Coastguard Worker
547*387f9dfdSAndroid Build Coastguard Worker if (pos_args == 1) {
548*387f9dfdSAndroid Build Coastguard Worker env.interval = argp_parse_long(key, arg, state);
549*387f9dfdSAndroid Build Coastguard Worker }
550*387f9dfdSAndroid Build Coastguard Worker else if (pos_args == 2) {
551*387f9dfdSAndroid Build Coastguard Worker env.nr_intervals = argp_parse_long(key, arg, state);
552*387f9dfdSAndroid Build Coastguard Worker } else {
553*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "Unrecognized positional argument: %s\n", arg);
554*387f9dfdSAndroid Build Coastguard Worker argp_usage(state);
555*387f9dfdSAndroid Build Coastguard Worker }
556*387f9dfdSAndroid Build Coastguard Worker
557*387f9dfdSAndroid Build Coastguard Worker break;
558*387f9dfdSAndroid Build Coastguard Worker default:
559*387f9dfdSAndroid Build Coastguard Worker return ARGP_ERR_UNKNOWN;
560*387f9dfdSAndroid Build Coastguard Worker }
561*387f9dfdSAndroid Build Coastguard Worker
562*387f9dfdSAndroid Build Coastguard Worker return 0;
563*387f9dfdSAndroid Build Coastguard Worker }
564*387f9dfdSAndroid Build Coastguard Worker
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)565*387f9dfdSAndroid Build Coastguard Worker int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
566*387f9dfdSAndroid Build Coastguard Worker {
567*387f9dfdSAndroid Build Coastguard Worker if (level == LIBBPF_DEBUG && !env.verbose)
568*387f9dfdSAndroid Build Coastguard Worker return 0;
569*387f9dfdSAndroid Build Coastguard Worker
570*387f9dfdSAndroid Build Coastguard Worker return vfprintf(stderr, format, args);
571*387f9dfdSAndroid Build Coastguard Worker }
572*387f9dfdSAndroid Build Coastguard Worker
sig_handler(int signo)573*387f9dfdSAndroid Build Coastguard Worker void sig_handler(int signo)
574*387f9dfdSAndroid Build Coastguard Worker {
575*387f9dfdSAndroid Build Coastguard Worker if (signo == SIGCHLD)
576*387f9dfdSAndroid Build Coastguard Worker child_exited = 1;
577*387f9dfdSAndroid Build Coastguard Worker
578*387f9dfdSAndroid Build Coastguard Worker exiting = 1;
579*387f9dfdSAndroid Build Coastguard Worker }
580*387f9dfdSAndroid Build Coastguard Worker
event_init(int * fd)581*387f9dfdSAndroid Build Coastguard Worker int event_init(int *fd)
582*387f9dfdSAndroid Build Coastguard Worker {
583*387f9dfdSAndroid Build Coastguard Worker if (!fd) {
584*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "pointer to fd is null\n");
585*387f9dfdSAndroid Build Coastguard Worker
586*387f9dfdSAndroid Build Coastguard Worker return 1;
587*387f9dfdSAndroid Build Coastguard Worker }
588*387f9dfdSAndroid Build Coastguard Worker
589*387f9dfdSAndroid Build Coastguard Worker const int tmp_fd = eventfd(0, EFD_CLOEXEC);
590*387f9dfdSAndroid Build Coastguard Worker if (tmp_fd < 0) {
591*387f9dfdSAndroid Build Coastguard Worker perror("failed to create event fd");
592*387f9dfdSAndroid Build Coastguard Worker
593*387f9dfdSAndroid Build Coastguard Worker return -errno;
594*387f9dfdSAndroid Build Coastguard Worker }
595*387f9dfdSAndroid Build Coastguard Worker
596*387f9dfdSAndroid Build Coastguard Worker *fd = tmp_fd;
597*387f9dfdSAndroid Build Coastguard Worker
598*387f9dfdSAndroid Build Coastguard Worker return 0;
599*387f9dfdSAndroid Build Coastguard Worker }
600*387f9dfdSAndroid Build Coastguard Worker
event_wait(int fd,uint64_t expected_event)601*387f9dfdSAndroid Build Coastguard Worker int event_wait(int fd, uint64_t expected_event)
602*387f9dfdSAndroid Build Coastguard Worker {
603*387f9dfdSAndroid Build Coastguard Worker uint64_t event = 0;
604*387f9dfdSAndroid Build Coastguard Worker const ssize_t bytes = read(fd, &event, sizeof(event));
605*387f9dfdSAndroid Build Coastguard Worker if (bytes < 0) {
606*387f9dfdSAndroid Build Coastguard Worker perror("failed to read from fd");
607*387f9dfdSAndroid Build Coastguard Worker
608*387f9dfdSAndroid Build Coastguard Worker return -errno;
609*387f9dfdSAndroid Build Coastguard Worker } else if (bytes != sizeof(event)) {
610*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "read unexpected size\n");
611*387f9dfdSAndroid Build Coastguard Worker
612*387f9dfdSAndroid Build Coastguard Worker return 1;
613*387f9dfdSAndroid Build Coastguard Worker }
614*387f9dfdSAndroid Build Coastguard Worker
615*387f9dfdSAndroid Build Coastguard Worker if (event != expected_event) {
616*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "read event %lu, expected %lu\n", event, expected_event);
617*387f9dfdSAndroid Build Coastguard Worker
618*387f9dfdSAndroid Build Coastguard Worker return 1;
619*387f9dfdSAndroid Build Coastguard Worker }
620*387f9dfdSAndroid Build Coastguard Worker
621*387f9dfdSAndroid Build Coastguard Worker return 0;
622*387f9dfdSAndroid Build Coastguard Worker }
623*387f9dfdSAndroid Build Coastguard Worker
event_notify(int fd,uint64_t event)624*387f9dfdSAndroid Build Coastguard Worker int event_notify(int fd, uint64_t event)
625*387f9dfdSAndroid Build Coastguard Worker {
626*387f9dfdSAndroid Build Coastguard Worker const ssize_t bytes = write(fd, &event, sizeof(event));
627*387f9dfdSAndroid Build Coastguard Worker if (bytes < 0) {
628*387f9dfdSAndroid Build Coastguard Worker perror("failed to write to fd");
629*387f9dfdSAndroid Build Coastguard Worker
630*387f9dfdSAndroid Build Coastguard Worker return -errno;
631*387f9dfdSAndroid Build Coastguard Worker } else if (bytes != sizeof(event)) {
632*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "attempted to write %zu bytes, wrote %zd bytes\n", sizeof(event), bytes);
633*387f9dfdSAndroid Build Coastguard Worker
634*387f9dfdSAndroid Build Coastguard Worker return 1;
635*387f9dfdSAndroid Build Coastguard Worker }
636*387f9dfdSAndroid Build Coastguard Worker
637*387f9dfdSAndroid Build Coastguard Worker return 0;
638*387f9dfdSAndroid Build Coastguard Worker }
639*387f9dfdSAndroid Build Coastguard Worker
fork_sync_exec(const char * command,int fd)640*387f9dfdSAndroid Build Coastguard Worker pid_t fork_sync_exec(const char *command, int fd)
641*387f9dfdSAndroid Build Coastguard Worker {
642*387f9dfdSAndroid Build Coastguard Worker const pid_t pid = fork();
643*387f9dfdSAndroid Build Coastguard Worker
644*387f9dfdSAndroid Build Coastguard Worker switch (pid) {
645*387f9dfdSAndroid Build Coastguard Worker case -1:
646*387f9dfdSAndroid Build Coastguard Worker perror("failed to create child process");
647*387f9dfdSAndroid Build Coastguard Worker break;
648*387f9dfdSAndroid Build Coastguard Worker case 0: {
649*387f9dfdSAndroid Build Coastguard Worker const uint64_t event = 1;
650*387f9dfdSAndroid Build Coastguard Worker if (event_wait(fd, event)) {
651*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "failed to wait on event");
652*387f9dfdSAndroid Build Coastguard Worker exit(EXIT_FAILURE);
653*387f9dfdSAndroid Build Coastguard Worker }
654*387f9dfdSAndroid Build Coastguard Worker
655*387f9dfdSAndroid Build Coastguard Worker printf("received go event. executing child command\n");
656*387f9dfdSAndroid Build Coastguard Worker
657*387f9dfdSAndroid Build Coastguard Worker const int err = execl(command, command, NULL);
658*387f9dfdSAndroid Build Coastguard Worker if (err) {
659*387f9dfdSAndroid Build Coastguard Worker perror("failed to execute child command");
660*387f9dfdSAndroid Build Coastguard Worker return -1;
661*387f9dfdSAndroid Build Coastguard Worker }
662*387f9dfdSAndroid Build Coastguard Worker
663*387f9dfdSAndroid Build Coastguard Worker break;
664*387f9dfdSAndroid Build Coastguard Worker }
665*387f9dfdSAndroid Build Coastguard Worker default:
666*387f9dfdSAndroid Build Coastguard Worker printf("child created with pid: %d\n", pid);
667*387f9dfdSAndroid Build Coastguard Worker
668*387f9dfdSAndroid Build Coastguard Worker break;
669*387f9dfdSAndroid Build Coastguard Worker }
670*387f9dfdSAndroid Build Coastguard Worker
671*387f9dfdSAndroid Build Coastguard Worker return pid;
672*387f9dfdSAndroid Build Coastguard Worker }
673*387f9dfdSAndroid Build Coastguard Worker
674*387f9dfdSAndroid Build Coastguard Worker #if USE_BLAZESYM
print_stack_frame_by_blazesym(size_t frame,uint64_t addr,const blazesym_csym * sym)675*387f9dfdSAndroid Build Coastguard Worker void print_stack_frame_by_blazesym(size_t frame, uint64_t addr, const blazesym_csym *sym)
676*387f9dfdSAndroid Build Coastguard Worker {
677*387f9dfdSAndroid Build Coastguard Worker if (!sym)
678*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] <%s>\n", frame, addr, "null sym");
679*387f9dfdSAndroid Build Coastguard Worker else if (sym->path && strlen(sym->path))
680*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] %s+0x%lx %s:%ld\n", frame, addr, sym->symbol, addr - sym->start_address, sym->path, sym->line_no);
681*387f9dfdSAndroid Build Coastguard Worker else
682*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] %s+0x%lx\n", frame, addr, sym->symbol, addr - sym->start_address);
683*387f9dfdSAndroid Build Coastguard Worker }
684*387f9dfdSAndroid Build Coastguard Worker
print_stack_frames_by_blazesym()685*387f9dfdSAndroid Build Coastguard Worker void print_stack_frames_by_blazesym()
686*387f9dfdSAndroid Build Coastguard Worker {
687*387f9dfdSAndroid Build Coastguard Worker const blazesym_result *result = blazesym_symbolize(symbolizer, &src_cfg, 1, stack, env.perf_max_stack_depth);
688*387f9dfdSAndroid Build Coastguard Worker
689*387f9dfdSAndroid Build Coastguard Worker for (size_t j = 0; j < result->size; ++j) {
690*387f9dfdSAndroid Build Coastguard Worker const uint64_t addr = stack[j];
691*387f9dfdSAndroid Build Coastguard Worker
692*387f9dfdSAndroid Build Coastguard Worker if (addr == 0)
693*387f9dfdSAndroid Build Coastguard Worker break;
694*387f9dfdSAndroid Build Coastguard Worker
695*387f9dfdSAndroid Build Coastguard Worker // no symbol found
696*387f9dfdSAndroid Build Coastguard Worker if (!result || j >= result->size || result->entries[j].size == 0) {
697*387f9dfdSAndroid Build Coastguard Worker print_stack_frame_by_blazesym(j, addr, NULL);
698*387f9dfdSAndroid Build Coastguard Worker
699*387f9dfdSAndroid Build Coastguard Worker continue;
700*387f9dfdSAndroid Build Coastguard Worker }
701*387f9dfdSAndroid Build Coastguard Worker
702*387f9dfdSAndroid Build Coastguard Worker // single symbol found
703*387f9dfdSAndroid Build Coastguard Worker if (result->entries[j].size == 1) {
704*387f9dfdSAndroid Build Coastguard Worker const blazesym_csym *sym = &result->entries[j].syms[0];
705*387f9dfdSAndroid Build Coastguard Worker print_stack_frame_by_blazesym(j, addr, sym);
706*387f9dfdSAndroid Build Coastguard Worker
707*387f9dfdSAndroid Build Coastguard Worker continue;
708*387f9dfdSAndroid Build Coastguard Worker }
709*387f9dfdSAndroid Build Coastguard Worker
710*387f9dfdSAndroid Build Coastguard Worker // multi symbol found
711*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] (%lu entries)\n", j, addr, result->entries[j].size);
712*387f9dfdSAndroid Build Coastguard Worker
713*387f9dfdSAndroid Build Coastguard Worker for (size_t k = 0; k < result->entries[j].size; ++k) {
714*387f9dfdSAndroid Build Coastguard Worker const blazesym_csym *sym = &result->entries[j].syms[k];
715*387f9dfdSAndroid Build Coastguard Worker if (sym->path && strlen(sym->path))
716*387f9dfdSAndroid Build Coastguard Worker printf("\t\t%s@0x%lx %s:%ld\n", sym->symbol, sym->start_address, sym->path, sym->line_no);
717*387f9dfdSAndroid Build Coastguard Worker else
718*387f9dfdSAndroid Build Coastguard Worker printf("\t\t%s@0x%lx\n", sym->symbol, sym->start_address);
719*387f9dfdSAndroid Build Coastguard Worker }
720*387f9dfdSAndroid Build Coastguard Worker }
721*387f9dfdSAndroid Build Coastguard Worker
722*387f9dfdSAndroid Build Coastguard Worker blazesym_result_free(result);
723*387f9dfdSAndroid Build Coastguard Worker }
724*387f9dfdSAndroid Build Coastguard Worker #else
print_stack_frames_by_ksyms()725*387f9dfdSAndroid Build Coastguard Worker void print_stack_frames_by_ksyms()
726*387f9dfdSAndroid Build Coastguard Worker {
727*387f9dfdSAndroid Build Coastguard Worker for (size_t i = 0; i < env.perf_max_stack_depth; ++i) {
728*387f9dfdSAndroid Build Coastguard Worker const uint64_t addr = stack[i];
729*387f9dfdSAndroid Build Coastguard Worker
730*387f9dfdSAndroid Build Coastguard Worker if (addr == 0)
731*387f9dfdSAndroid Build Coastguard Worker break;
732*387f9dfdSAndroid Build Coastguard Worker
733*387f9dfdSAndroid Build Coastguard Worker const struct ksym *ksym = ksyms__map_addr(ksyms, addr);
734*387f9dfdSAndroid Build Coastguard Worker if (ksym)
735*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] %s+0x%lx\n", i, addr, ksym->name, addr - ksym->addr);
736*387f9dfdSAndroid Build Coastguard Worker else
737*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] <%s>\n", i, addr, "null sym");
738*387f9dfdSAndroid Build Coastguard Worker }
739*387f9dfdSAndroid Build Coastguard Worker }
740*387f9dfdSAndroid Build Coastguard Worker
print_stack_frames_by_syms_cache()741*387f9dfdSAndroid Build Coastguard Worker void print_stack_frames_by_syms_cache()
742*387f9dfdSAndroid Build Coastguard Worker {
743*387f9dfdSAndroid Build Coastguard Worker const struct syms *syms = syms_cache__get_syms(syms_cache, env.pid);
744*387f9dfdSAndroid Build Coastguard Worker if (!syms) {
745*387f9dfdSAndroid Build Coastguard Worker fprintf(stderr, "Failed to get syms\n");
746*387f9dfdSAndroid Build Coastguard Worker return;
747*387f9dfdSAndroid Build Coastguard Worker }
748*387f9dfdSAndroid Build Coastguard Worker
749*387f9dfdSAndroid Build Coastguard Worker for (size_t i = 0; i < env.perf_max_stack_depth; ++i) {
750*387f9dfdSAndroid Build Coastguard Worker const uint64_t addr = stack[i];
751*387f9dfdSAndroid Build Coastguard Worker
752*387f9dfdSAndroid Build Coastguard Worker if (addr == 0)
753*387f9dfdSAndroid Build Coastguard Worker break;
754*387f9dfdSAndroid Build Coastguard Worker
755*387f9dfdSAndroid Build Coastguard Worker char *dso_name;
756*387f9dfdSAndroid Build Coastguard Worker uint64_t dso_offset;
757*387f9dfdSAndroid Build Coastguard Worker const struct sym *sym = syms__map_addr_dso(syms, addr, &dso_name, &dso_offset);
758*387f9dfdSAndroid Build Coastguard Worker if (sym) {
759*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] %s+0x%lx", i, addr, sym->name, sym->offset);
760*387f9dfdSAndroid Build Coastguard Worker if (dso_name)
761*387f9dfdSAndroid Build Coastguard Worker printf(" [%s]", dso_name);
762*387f9dfdSAndroid Build Coastguard Worker printf("\n");
763*387f9dfdSAndroid Build Coastguard Worker } else {
764*387f9dfdSAndroid Build Coastguard Worker printf("\t%zu [<%016lx>] <%s>\n", i, addr, "null sym");
765*387f9dfdSAndroid Build Coastguard Worker }
766*387f9dfdSAndroid Build Coastguard Worker }
767*387f9dfdSAndroid Build Coastguard Worker }
768*387f9dfdSAndroid Build Coastguard Worker #endif
769*387f9dfdSAndroid Build Coastguard Worker
print_stack_frames(struct allocation * allocs,size_t nr_allocs,int stack_traces_fd)770*387f9dfdSAndroid Build Coastguard Worker int print_stack_frames(struct allocation *allocs, size_t nr_allocs, int stack_traces_fd)
771*387f9dfdSAndroid Build Coastguard Worker {
772*387f9dfdSAndroid Build Coastguard Worker for (size_t i = 0; i < nr_allocs; ++i) {
773*387f9dfdSAndroid Build Coastguard Worker const struct allocation *alloc = &allocs[i];
774*387f9dfdSAndroid Build Coastguard Worker
775*387f9dfdSAndroid Build Coastguard Worker printf("%zu bytes in %zu allocations from stack\n", alloc->size, alloc->count);
776*387f9dfdSAndroid Build Coastguard Worker
777*387f9dfdSAndroid Build Coastguard Worker if (env.show_allocs) {
778*387f9dfdSAndroid Build Coastguard Worker struct allocation_node* it = alloc->allocations;
779*387f9dfdSAndroid Build Coastguard Worker while (it != NULL) {
780*387f9dfdSAndroid Build Coastguard Worker printf("\taddr = %#lx size = %zu\n", it->address, it->size);
781*387f9dfdSAndroid Build Coastguard Worker it = it->next;
782*387f9dfdSAndroid Build Coastguard Worker }
783*387f9dfdSAndroid Build Coastguard Worker }
784*387f9dfdSAndroid Build Coastguard Worker
785*387f9dfdSAndroid Build Coastguard Worker if (bpf_map_lookup_elem(stack_traces_fd, &alloc->stack_id, stack)) {
786*387f9dfdSAndroid Build Coastguard Worker if (errno == ENOENT)
787*387f9dfdSAndroid Build Coastguard Worker continue;
788*387f9dfdSAndroid Build Coastguard Worker
789*387f9dfdSAndroid Build Coastguard Worker perror("failed to lookup stack trace");
790*387f9dfdSAndroid Build Coastguard Worker
791*387f9dfdSAndroid Build Coastguard Worker return -errno;
792*387f9dfdSAndroid Build Coastguard Worker }
793*387f9dfdSAndroid Build Coastguard Worker
794*387f9dfdSAndroid Build Coastguard Worker (*print_stack_frames_func)();
795*387f9dfdSAndroid Build Coastguard Worker }
796*387f9dfdSAndroid Build Coastguard Worker
797*387f9dfdSAndroid Build Coastguard Worker return 0;
798*387f9dfdSAndroid Build Coastguard Worker }
799*387f9dfdSAndroid Build Coastguard Worker
alloc_size_compare(const void * a,const void * b)800*387f9dfdSAndroid Build Coastguard Worker int alloc_size_compare(const void *a, const void *b)
801*387f9dfdSAndroid Build Coastguard Worker {
802*387f9dfdSAndroid Build Coastguard Worker const struct allocation *x = (struct allocation *)a;
803*387f9dfdSAndroid Build Coastguard Worker const struct allocation *y = (struct allocation *)b;
804*387f9dfdSAndroid Build Coastguard Worker
805*387f9dfdSAndroid Build Coastguard Worker // descending order
806*387f9dfdSAndroid Build Coastguard Worker
807*387f9dfdSAndroid Build Coastguard Worker if (x->size > y->size)
808*387f9dfdSAndroid Build Coastguard Worker return -1;
809*387f9dfdSAndroid Build Coastguard Worker
810*387f9dfdSAndroid Build Coastguard Worker if (x->size < y->size)
811*387f9dfdSAndroid Build Coastguard Worker return 1;
812*387f9dfdSAndroid Build Coastguard Worker
813*387f9dfdSAndroid Build Coastguard Worker return 0;
814*387f9dfdSAndroid Build Coastguard Worker }
815*387f9dfdSAndroid Build Coastguard Worker
print_outstanding_allocs(int allocs_fd,int stack_traces_fd)816*387f9dfdSAndroid Build Coastguard Worker int print_outstanding_allocs(int allocs_fd, int stack_traces_fd)
817*387f9dfdSAndroid Build Coastguard Worker {
818*387f9dfdSAndroid Build Coastguard Worker time_t t = time(NULL);
819*387f9dfdSAndroid Build Coastguard Worker struct tm *tm = localtime(&t);
820*387f9dfdSAndroid Build Coastguard Worker
821*387f9dfdSAndroid Build Coastguard Worker size_t nr_allocs = 0;
822*387f9dfdSAndroid Build Coastguard Worker
823*387f9dfdSAndroid Build Coastguard Worker // for each struct alloc_info "alloc_info" in the bpf map "allocs"
824*387f9dfdSAndroid Build Coastguard Worker for (uint64_t prev_key = 0, curr_key = 0;; prev_key = curr_key) {
825*387f9dfdSAndroid Build Coastguard Worker struct alloc_info alloc_info = {};
826*387f9dfdSAndroid Build Coastguard Worker memset(&alloc_info, 0, sizeof(alloc_info));
827*387f9dfdSAndroid Build Coastguard Worker
828*387f9dfdSAndroid Build Coastguard Worker if (bpf_map_get_next_key(allocs_fd, &prev_key, &curr_key)) {
829*387f9dfdSAndroid Build Coastguard Worker if (errno == ENOENT) {
830*387f9dfdSAndroid Build Coastguard Worker break; // no more keys, done
831*387f9dfdSAndroid Build Coastguard Worker }
832*387f9dfdSAndroid Build Coastguard Worker
833*387f9dfdSAndroid Build Coastguard Worker perror("map get next key error");
834*387f9dfdSAndroid Build Coastguard Worker
835*387f9dfdSAndroid Build Coastguard Worker return -errno;
836*387f9dfdSAndroid Build Coastguard Worker }
837*387f9dfdSAndroid Build Coastguard Worker
838*387f9dfdSAndroid Build Coastguard Worker if (bpf_map_lookup_elem(allocs_fd, &curr_key, &alloc_info)) {
839*387f9dfdSAndroid Build Coastguard Worker if (errno == ENOENT)
840*387f9dfdSAndroid Build Coastguard Worker continue;
841*387f9dfdSAndroid Build Coastguard Worker
842*387f9dfdSAndroid Build Coastguard Worker perror("map lookup error");
843*387f9dfdSAndroid Build Coastguard Worker
844*387f9dfdSAndroid Build Coastguard Worker return -errno;
845*387f9dfdSAndroid Build Coastguard Worker }
846*387f9dfdSAndroid Build Coastguard Worker
847*387f9dfdSAndroid Build Coastguard Worker // filter by age
848*387f9dfdSAndroid Build Coastguard Worker if (get_ktime_ns() - env.min_age_ns < alloc_info.timestamp_ns) {
849*387f9dfdSAndroid Build Coastguard Worker continue;
850*387f9dfdSAndroid Build Coastguard Worker }
851*387f9dfdSAndroid Build Coastguard Worker
852*387f9dfdSAndroid Build Coastguard Worker // filter invalid stacks
853*387f9dfdSAndroid Build Coastguard Worker if (alloc_info.stack_id < 0) {
854*387f9dfdSAndroid Build Coastguard Worker continue;
855*387f9dfdSAndroid Build Coastguard Worker }
856*387f9dfdSAndroid Build Coastguard Worker
857*387f9dfdSAndroid Build Coastguard Worker // when the stack_id exists in the allocs array,
858*387f9dfdSAndroid Build Coastguard Worker // increment size with alloc_info.size
859*387f9dfdSAndroid Build Coastguard Worker bool stack_exists = false;
860*387f9dfdSAndroid Build Coastguard Worker
861*387f9dfdSAndroid Build Coastguard Worker for (size_t i = 0; !stack_exists && i < nr_allocs; ++i) {
862*387f9dfdSAndroid Build Coastguard Worker struct allocation *alloc = &allocs[i];
863*387f9dfdSAndroid Build Coastguard Worker
864*387f9dfdSAndroid Build Coastguard Worker if (alloc->stack_id == alloc_info.stack_id) {
865*387f9dfdSAndroid Build Coastguard Worker alloc->size += alloc_info.size;
866*387f9dfdSAndroid Build Coastguard Worker alloc->count++;
867*387f9dfdSAndroid Build Coastguard Worker
868*387f9dfdSAndroid Build Coastguard Worker if (env.show_allocs) {
869*387f9dfdSAndroid Build Coastguard Worker struct allocation_node* node = malloc(sizeof(struct allocation_node));
870*387f9dfdSAndroid Build Coastguard Worker if (!node) {
871*387f9dfdSAndroid Build Coastguard Worker perror("malloc failed");
872*387f9dfdSAndroid Build Coastguard Worker return -errno;
873*387f9dfdSAndroid Build Coastguard Worker }
874*387f9dfdSAndroid Build Coastguard Worker node->address = curr_key;
875*387f9dfdSAndroid Build Coastguard Worker node->size = alloc_info.size;
876*387f9dfdSAndroid Build Coastguard Worker node->next = alloc->allocations;
877*387f9dfdSAndroid Build Coastguard Worker alloc->allocations = node;
878*387f9dfdSAndroid Build Coastguard Worker }
879*387f9dfdSAndroid Build Coastguard Worker
880*387f9dfdSAndroid Build Coastguard Worker stack_exists = true;
881*387f9dfdSAndroid Build Coastguard Worker break;
882*387f9dfdSAndroid Build Coastguard Worker }
883*387f9dfdSAndroid Build Coastguard Worker }
884*387f9dfdSAndroid Build Coastguard Worker
885*387f9dfdSAndroid Build Coastguard Worker if (stack_exists)
886*387f9dfdSAndroid Build Coastguard Worker continue;
887*387f9dfdSAndroid Build Coastguard Worker
888*387f9dfdSAndroid Build Coastguard Worker // when the stack_id does not exist in the allocs array,
889*387f9dfdSAndroid Build Coastguard Worker // create a new entry in the array
890*387f9dfdSAndroid Build Coastguard Worker struct allocation alloc = {
891*387f9dfdSAndroid Build Coastguard Worker .stack_id = alloc_info.stack_id,
892*387f9dfdSAndroid Build Coastguard Worker .size = alloc_info.size,
893*387f9dfdSAndroid Build Coastguard Worker .count = 1,
894*387f9dfdSAndroid Build Coastguard Worker .allocations = NULL
895*387f9dfdSAndroid Build Coastguard Worker };
896*387f9dfdSAndroid Build Coastguard Worker
897*387f9dfdSAndroid Build Coastguard Worker if (env.show_allocs) {
898*387f9dfdSAndroid Build Coastguard Worker struct allocation_node* node = malloc(sizeof(struct allocation_node));
899*387f9dfdSAndroid Build Coastguard Worker if (!node) {
900*387f9dfdSAndroid Build Coastguard Worker perror("malloc failed");
901*387f9dfdSAndroid Build Coastguard Worker return -errno;
902*387f9dfdSAndroid Build Coastguard Worker }
903*387f9dfdSAndroid Build Coastguard Worker node->address = curr_key;
904*387f9dfdSAndroid Build Coastguard Worker node->size = alloc_info.size;
905*387f9dfdSAndroid Build Coastguard Worker node->next = NULL;
906*387f9dfdSAndroid Build Coastguard Worker alloc.allocations = node;
907*387f9dfdSAndroid Build Coastguard Worker }
908*387f9dfdSAndroid Build Coastguard Worker
909*387f9dfdSAndroid Build Coastguard Worker memcpy(&allocs[nr_allocs], &alloc, sizeof(alloc));
910*387f9dfdSAndroid Build Coastguard Worker nr_allocs++;
911*387f9dfdSAndroid Build Coastguard Worker }
912*387f9dfdSAndroid Build Coastguard Worker
913*387f9dfdSAndroid Build Coastguard Worker // sort the allocs array in descending order
914*387f9dfdSAndroid Build Coastguard Worker qsort(allocs, nr_allocs, sizeof(allocs[0]), alloc_size_compare);
915*387f9dfdSAndroid Build Coastguard Worker
916*387f9dfdSAndroid Build Coastguard Worker // get min of allocs we stored vs the top N requested stacks
917*387f9dfdSAndroid Build Coastguard Worker size_t nr_allocs_to_show = nr_allocs < env.top_stacks ? nr_allocs : env.top_stacks;
918*387f9dfdSAndroid Build Coastguard Worker
919*387f9dfdSAndroid Build Coastguard Worker printf("[%d:%d:%d] Top %zu stacks with outstanding allocations:\n",
920*387f9dfdSAndroid Build Coastguard Worker tm->tm_hour, tm->tm_min, tm->tm_sec, nr_allocs_to_show);
921*387f9dfdSAndroid Build Coastguard Worker
922*387f9dfdSAndroid Build Coastguard Worker print_stack_frames(allocs, nr_allocs_to_show, stack_traces_fd);
923*387f9dfdSAndroid Build Coastguard Worker
924*387f9dfdSAndroid Build Coastguard Worker // Reset allocs list so that we dont accidentaly reuse data the next time we call this function
925*387f9dfdSAndroid Build Coastguard Worker for (size_t i = 0; i < nr_allocs; i++) {
926*387f9dfdSAndroid Build Coastguard Worker allocs[i].stack_id = 0;
927*387f9dfdSAndroid Build Coastguard Worker if (env.show_allocs) {
928*387f9dfdSAndroid Build Coastguard Worker struct allocation_node *it = allocs[i].allocations;
929*387f9dfdSAndroid Build Coastguard Worker while (it != NULL) {
930*387f9dfdSAndroid Build Coastguard Worker struct allocation_node *this = it;
931*387f9dfdSAndroid Build Coastguard Worker it = it->next;
932*387f9dfdSAndroid Build Coastguard Worker free(this);
933*387f9dfdSAndroid Build Coastguard Worker }
934*387f9dfdSAndroid Build Coastguard Worker allocs[i].allocations = NULL;
935*387f9dfdSAndroid Build Coastguard Worker }
936*387f9dfdSAndroid Build Coastguard Worker }
937*387f9dfdSAndroid Build Coastguard Worker
938*387f9dfdSAndroid Build Coastguard Worker return 0;
939*387f9dfdSAndroid Build Coastguard Worker }
940*387f9dfdSAndroid Build Coastguard Worker
print_outstanding_combined_allocs(int combined_allocs_fd,int stack_traces_fd)941*387f9dfdSAndroid Build Coastguard Worker int print_outstanding_combined_allocs(int combined_allocs_fd, int stack_traces_fd)
942*387f9dfdSAndroid Build Coastguard Worker {
943*387f9dfdSAndroid Build Coastguard Worker time_t t = time(NULL);
944*387f9dfdSAndroid Build Coastguard Worker struct tm *tm = localtime(&t);
945*387f9dfdSAndroid Build Coastguard Worker
946*387f9dfdSAndroid Build Coastguard Worker size_t nr_allocs = 0;
947*387f9dfdSAndroid Build Coastguard Worker
948*387f9dfdSAndroid Build Coastguard Worker // for each stack_id "curr_key" and union combined_alloc_info "alloc"
949*387f9dfdSAndroid Build Coastguard Worker // in bpf_map "combined_allocs"
950*387f9dfdSAndroid Build Coastguard Worker for (uint64_t prev_key = 0, curr_key = 0;; prev_key = curr_key) {
951*387f9dfdSAndroid Build Coastguard Worker union combined_alloc_info combined_alloc_info;
952*387f9dfdSAndroid Build Coastguard Worker memset(&combined_alloc_info, 0, sizeof(combined_alloc_info));
953*387f9dfdSAndroid Build Coastguard Worker
954*387f9dfdSAndroid Build Coastguard Worker if (bpf_map_get_next_key(combined_allocs_fd, &prev_key, &curr_key)) {
955*387f9dfdSAndroid Build Coastguard Worker if (errno == ENOENT) {
956*387f9dfdSAndroid Build Coastguard Worker break; // no more keys, done
957*387f9dfdSAndroid Build Coastguard Worker }
958*387f9dfdSAndroid Build Coastguard Worker
959*387f9dfdSAndroid Build Coastguard Worker perror("map get next key error");
960*387f9dfdSAndroid Build Coastguard Worker
961*387f9dfdSAndroid Build Coastguard Worker return -errno;
962*387f9dfdSAndroid Build Coastguard Worker }
963*387f9dfdSAndroid Build Coastguard Worker
964*387f9dfdSAndroid Build Coastguard Worker if (bpf_map_lookup_elem(combined_allocs_fd, &curr_key, &combined_alloc_info)) {
965*387f9dfdSAndroid Build Coastguard Worker if (errno == ENOENT)
966*387f9dfdSAndroid Build Coastguard Worker continue;
967*387f9dfdSAndroid Build Coastguard Worker
968*387f9dfdSAndroid Build Coastguard Worker perror("map lookup error");
969*387f9dfdSAndroid Build Coastguard Worker
970*387f9dfdSAndroid Build Coastguard Worker return -errno;
971*387f9dfdSAndroid Build Coastguard Worker }
972*387f9dfdSAndroid Build Coastguard Worker
973*387f9dfdSAndroid Build Coastguard Worker const struct allocation alloc = {
974*387f9dfdSAndroid Build Coastguard Worker .stack_id = curr_key,
975*387f9dfdSAndroid Build Coastguard Worker .size = combined_alloc_info.total_size,
976*387f9dfdSAndroid Build Coastguard Worker .count = combined_alloc_info.number_of_allocs,
977*387f9dfdSAndroid Build Coastguard Worker .allocations = NULL
978*387f9dfdSAndroid Build Coastguard Worker };
979*387f9dfdSAndroid Build Coastguard Worker
980*387f9dfdSAndroid Build Coastguard Worker memcpy(&allocs[nr_allocs], &alloc, sizeof(alloc));
981*387f9dfdSAndroid Build Coastguard Worker nr_allocs++;
982*387f9dfdSAndroid Build Coastguard Worker }
983*387f9dfdSAndroid Build Coastguard Worker
984*387f9dfdSAndroid Build Coastguard Worker qsort(allocs, nr_allocs, sizeof(allocs[0]), alloc_size_compare);
985*387f9dfdSAndroid Build Coastguard Worker
986*387f9dfdSAndroid Build Coastguard Worker // get min of allocs we stored vs the top N requested stacks
987*387f9dfdSAndroid Build Coastguard Worker nr_allocs = nr_allocs < env.top_stacks ? nr_allocs : env.top_stacks;
988*387f9dfdSAndroid Build Coastguard Worker
989*387f9dfdSAndroid Build Coastguard Worker printf("[%d:%d:%d] Top %zu stacks with outstanding allocations:\n",
990*387f9dfdSAndroid Build Coastguard Worker tm->tm_hour, tm->tm_min, tm->tm_sec, nr_allocs);
991*387f9dfdSAndroid Build Coastguard Worker
992*387f9dfdSAndroid Build Coastguard Worker print_stack_frames(allocs, nr_allocs, stack_traces_fd);
993*387f9dfdSAndroid Build Coastguard Worker
994*387f9dfdSAndroid Build Coastguard Worker return 0;
995*387f9dfdSAndroid Build Coastguard Worker }
996*387f9dfdSAndroid Build Coastguard Worker
has_kernel_node_tracepoints()997*387f9dfdSAndroid Build Coastguard Worker bool has_kernel_node_tracepoints()
998*387f9dfdSAndroid Build Coastguard Worker {
999*387f9dfdSAndroid Build Coastguard Worker return tracepoint_exists("kmem", "kmalloc_node") &&
1000*387f9dfdSAndroid Build Coastguard Worker tracepoint_exists("kmem", "kmem_cache_alloc_node");
1001*387f9dfdSAndroid Build Coastguard Worker }
1002*387f9dfdSAndroid Build Coastguard Worker
disable_kernel_node_tracepoints(struct memleak_bpf * skel)1003*387f9dfdSAndroid Build Coastguard Worker void disable_kernel_node_tracepoints(struct memleak_bpf *skel)
1004*387f9dfdSAndroid Build Coastguard Worker {
1005*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kmalloc_node, false);
1006*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc_node, false);
1007*387f9dfdSAndroid Build Coastguard Worker }
1008*387f9dfdSAndroid Build Coastguard Worker
disable_kernel_percpu_tracepoints(struct memleak_bpf * skel)1009*387f9dfdSAndroid Build Coastguard Worker void disable_kernel_percpu_tracepoints(struct memleak_bpf *skel)
1010*387f9dfdSAndroid Build Coastguard Worker {
1011*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__percpu_alloc_percpu, false);
1012*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__percpu_free_percpu, false);
1013*387f9dfdSAndroid Build Coastguard Worker }
1014*387f9dfdSAndroid Build Coastguard Worker
disable_kernel_tracepoints(struct memleak_bpf * skel)1015*387f9dfdSAndroid Build Coastguard Worker void disable_kernel_tracepoints(struct memleak_bpf *skel)
1016*387f9dfdSAndroid Build Coastguard Worker {
1017*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kmalloc, false);
1018*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kmalloc_node, false);
1019*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kfree, false);
1020*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc, false);
1021*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kmem_cache_alloc_node, false);
1022*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__kmem_cache_free, false);
1023*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__mm_page_alloc, false);
1024*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__mm_page_free, false);
1025*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__percpu_alloc_percpu, false);
1026*387f9dfdSAndroid Build Coastguard Worker bpf_program__set_autoload(skel->progs.memleak__percpu_free_percpu, false);
1027*387f9dfdSAndroid Build Coastguard Worker }
1028*387f9dfdSAndroid Build Coastguard Worker
attach_uprobes(struct memleak_bpf * skel)1029*387f9dfdSAndroid Build Coastguard Worker int attach_uprobes(struct memleak_bpf *skel)
1030*387f9dfdSAndroid Build Coastguard Worker {
1031*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, malloc, malloc_enter);
1032*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE_CHECKED(skel, malloc, malloc_exit);
1033*387f9dfdSAndroid Build Coastguard Worker
1034*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, calloc, calloc_enter);
1035*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE_CHECKED(skel, calloc, calloc_exit);
1036*387f9dfdSAndroid Build Coastguard Worker
1037*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, realloc, realloc_enter);
1038*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE_CHECKED(skel, realloc, realloc_exit);
1039*387f9dfdSAndroid Build Coastguard Worker
1040*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, mmap, mmap_enter);
1041*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE_CHECKED(skel, mmap, mmap_exit);
1042*387f9dfdSAndroid Build Coastguard Worker
1043*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, posix_memalign, posix_memalign_enter);
1044*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE_CHECKED(skel, posix_memalign, posix_memalign_exit);
1045*387f9dfdSAndroid Build Coastguard Worker
1046*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, memalign, memalign_enter);
1047*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE_CHECKED(skel, memalign, memalign_exit);
1048*387f9dfdSAndroid Build Coastguard Worker
1049*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, free, free_enter);
1050*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE_CHECKED(skel, munmap, munmap_enter);
1051*387f9dfdSAndroid Build Coastguard Worker
1052*387f9dfdSAndroid Build Coastguard Worker // the following probes are intentinally allowed to fail attachment
1053*387f9dfdSAndroid Build Coastguard Worker
1054*387f9dfdSAndroid Build Coastguard Worker // deprecated in libc.so bionic
1055*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE(skel, valloc, valloc_enter);
1056*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE(skel, valloc, valloc_exit);
1057*387f9dfdSAndroid Build Coastguard Worker
1058*387f9dfdSAndroid Build Coastguard Worker // deprecated in libc.so bionic
1059*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE(skel, pvalloc, pvalloc_enter);
1060*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE(skel, pvalloc, pvalloc_exit);
1061*387f9dfdSAndroid Build Coastguard Worker
1062*387f9dfdSAndroid Build Coastguard Worker // added in C11
1063*387f9dfdSAndroid Build Coastguard Worker ATTACH_UPROBE(skel, aligned_alloc, aligned_alloc_enter);
1064*387f9dfdSAndroid Build Coastguard Worker ATTACH_URETPROBE(skel, aligned_alloc, aligned_alloc_exit);
1065*387f9dfdSAndroid Build Coastguard Worker
1066*387f9dfdSAndroid Build Coastguard Worker
1067*387f9dfdSAndroid Build Coastguard Worker return 0;
1068*387f9dfdSAndroid Build Coastguard Worker }
1069