blob: f11aa8bd9ece39e893ec5c146966728a95dc2705 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (C) 2018 MediaTek Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/mm.h>
15#include <linux/slab.h>
16#include <linux/sched.h>
17#include <linux/cpu.h>
18#include <linux/module.h>
19#include <linux/fs.h>
20#include <asm/irq_regs.h>
21#include <asm/stacktrace.h>
22#include <linux/stacktrace.h>
23#include "interface.h"
24#include "met_drv.h"
25
26#define LINE_SIZE 256
27
28struct cookie_info {
29 int depth;
30 int strlen;
31 char strbuf[LINE_SIZE];
32};
33
34static unsigned int back_trace_depth;
35static DEFINE_PER_CPU(struct cookie_info, info);
36
37static int reset_driver_stat(void)
38{
39 back_trace_depth = 0;
40 met_cookie.mode = 0;
41 return 0;
42}
43
44
45noinline void cookie(char *strbuf)
46{
47 MET_TRACE("%s\n", strbuf);
48}
49
50noinline void cookie2(char *strbuf)
51{
52 MET_TRACE("%s\n", strbuf);
53}
54
55static void get_kernel_cookie(unsigned long pc, struct cookie_info *pinfo)
56{
57 int ret;
58#ifdef CONFIG_MODULES
59 off_t off;
60 struct module *mod = __module_address(pc);
61
62 if (mod) {
63 off = pc - (unsigned long)mod->module_core;
64 ret = snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
65 ",%s,%lx", mod->name, off);
66 pinfo->strlen += ret;
67 /* cookie(current->comm, pc, mod->name, off, 1); */
68 } else
69#endif
70 {
71 ret =
72 snprintf(pinfo->strbuf + pinfo->strlen, LINE_SIZE - pinfo->strlen,
73 ",vmlinux,%lx", pc);
74 pinfo->strlen += ret;
75 /* cookie(current->comm, pc, "vmlinux", pc, 0); */
76 }
77}
78
79#if defined(__arm__)
80static int report_trace(struct stackframe *frame, void *d)
81{
82 struct cookie_info *pinfo = d;
83 unsigned long pc = frame->pc;
84
85 if (pinfo->depth > 0) {
86 get_kernel_cookie(pc, pinfo);
87 pinfo->depth--;
88 return 0;
89 }
90 return 1;
91}
92#endif
93
94static void kernel_backtrace(struct pt_regs *const regs, struct cookie_info *pinfo)
95{
96#if defined(__arm__)
97 struct stackframe frame;
98
99 frame.fp = regs->ARM_fp;
100 frame.sp = regs->ARM_sp;
101 frame.lr = regs->ARM_lr;
102 frame.pc = regs->ARM_pc;
103 walk_stackframe(&frame, report_trace, pinfo);
104#else
105 return;
106#endif
107}
108
109
110void met_cookie_polling(unsigned long long stamp, int cpu)
111{
112 struct pt_regs *regs;
113 struct cookie_info *pinfo;
114 unsigned long pc;
115 int ret, outflag = 0;
116 off_t off;
117
118 regs = get_irq_regs();
119
120 if (regs == 0)
121 return;
122
123 pc = profile_pc(regs);
124
125 pinfo = &(per_cpu(info, cpu));
126 pinfo->strlen = snprintf(pinfo->strbuf, LINE_SIZE, "%s,%lx", current->comm, pc);
127
128 if (user_mode(regs)) {
129 struct mm_struct *mm;
130 struct vm_area_struct *vma;
131 struct path *ppath;
132
133 mm = current->mm;
134 for (vma = find_vma(mm, pc); vma; vma = vma->vm_next) {
135
136 if (pc < vma->vm_start || pc >= vma->vm_end)
137 continue;
138
139 if (vma->vm_file) {
140 ppath = &(vma->vm_file->f_path);
141
142 if (vma->vm_flags & VM_DENYWRITE)
143 off = pc;
144 else
145 off = (vma->vm_pgoff << PAGE_SHIFT) + pc - vma->vm_start;
146
147 ret =
148 snprintf(pinfo->strbuf + pinfo->strlen,
149 LINE_SIZE - pinfo->strlen, ",%s,%lx",
150 (char *)(ppath->dentry->d_name.name), off);
151 pinfo->strlen += ret;
152 outflag = 1;
153 } else {
154 /* must be an anonymous map */
155 ret =
156 snprintf(pinfo->strbuf + pinfo->strlen,
157 LINE_SIZE - pinfo->strlen, ",nofile,%lx", pc);
158 pinfo->strlen += ret;
159 outflag = 1;
160 }
161 break;
162 }
163 } else {
164 /* kernel mode code */
165 if (back_trace_depth > 0) {
166 pinfo->depth = back_trace_depth + 1;
167 kernel_backtrace(regs, pinfo);
168 } else
169 get_kernel_cookie(pc, pinfo);
170 outflag = 1;
171 }
172
173 /* check task is resolvable */
174 if (outflag == 0)
175 return;
176
177 if (back_trace_depth == 0)
178 cookie(pinfo->strbuf);
179 else
180 cookie2(pinfo->strbuf);
181}
182
183
184static void met_cookie_start(void)
185{
186 /* return; */
187}
188
189static void met_cookie_stop(void)
190{
191 /* return; */
192}
193
194
195static int met_cookie_process_argument(const char *arg, int len)
196{
197 unsigned int value;
198
199 if (met_parse_num(arg, &value, len) < 0) {
200 met_cookie.mode = 0;
201 return -EINVAL;
202 }
203
204 back_trace_depth = value;
205 met_cookie.mode = 1;
206
207 return 0;
208}
209
210static const char help[] =
211" --cookie enable sampling task and PC\n"
212" --cookie=N enable back trace (depth is N)\n";
213
214static int met_cookie_print_help(char *buf, int len)
215{
216 len = snprintf(buf, PAGE_SIZE, help);
217 return len;
218}
219
220
221static const char header[] =
222"# cookie: task_name,PC,cookie_name,offset\n"
223"met-info [000] 0.0: cookie_header: task_name,PC,cookie_name,offset\n";
224
225static const char header2_1[] = "# cookie2: task_name,PC,cookie,offset";
226static const char header2_2[] = "met-info [000] 0.0: cookie2_header: task_name,PC,cookie,offset";
227
228static int met_cookie_print_header(char *buf, int len)
229{
230 int i, ret;
231
232 if (back_trace_depth == 0) {
233 len = snprintf(buf, PAGE_SIZE, header);
234 } else {
235 len = snprintf(buf, PAGE_SIZE, header2_1);
236 for (i = 0; i < back_trace_depth; i++) {
237 ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
238 len += ret;
239 }
240 ret = snprintf(buf + len, PAGE_SIZE, "\n");
241 len += ret;
242
243 ret = snprintf(buf + len, PAGE_SIZE, header2_2);
244 len += ret;
245 for (i = 0; i < back_trace_depth; i++) {
246 ret = snprintf(buf + len, PAGE_SIZE, ",cookie%d,offset%d", i + 1, i + 1);
247 len += ret;
248 }
249 ret = snprintf(buf + len, PAGE_SIZE, "\n");
250 len += ret;
251 }
252
253 return len;
254}
255
256struct metdevice met_cookie = {
257 .name = "cookie",
258 .type = MET_TYPE_PMU,
259 .cpu_related = 1,
260 .start = met_cookie_start,
261 .stop = met_cookie_stop,
262 .reset = reset_driver_stat,
263 .polling_interval = 1,
264 .timed_polling = met_cookie_polling,
265 .process_argument = met_cookie_process_argument,
266 .print_help = met_cookie_print_help,
267 .print_header = met_cookie_print_header,
268};