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