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