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