xref: /optee_os/lib/libutils/ext/ftrace/ftrace.c (revision e3dddf723e2a992c5f557725391c730dfb374ebf)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2019, Linaro Limited
4  */
5 
6 /*
7  * APIs defined in this file are required to use __noprof attribute to
8  * avoid any circular dependency during profiling. So this requirement
9  * prohibits these APIs to use standard library APIs as those can be
10  * profiled too.
11  */
12 
13 #include <arm_user_sysreg.h>
14 #include <assert.h>
15 #include <setjmp.h>
16 #include <user_ta_header.h>
17 #include <utee_syscalls.h>
18 #include "ftrace.h"
19 
20 #define DURATION_MAX_LEN		16
21 
22 static const char hex_str[] = "0123456789abcdef";
23 
24 /*
25  * This API shifts/moves ftrace buffer to create space for new dump
26  * in case the buffer size falls short of actual dump.
27  */
28 static void __noprof fbuf_shift(struct ftrace_buf *fbuf, size_t size)
29 {
30 	char *dst = (char *)fbuf + fbuf->buf_off;
31 	const char *src = (char *)fbuf + fbuf->buf_off + size;
32 	size_t n = 0;
33 
34 	fbuf->curr_size -= size;
35 
36 	for (n = 0; n < fbuf->curr_size; n++)
37 		dst[n] = src[n];
38 }
39 
40 static size_t __noprof to_func_enter_fmt(char *buf, uint32_t ret_idx,
41 					 unsigned long pc)
42 {
43 	char *str = buf;
44 	uint32_t addr_size = 2 * sizeof(unsigned long);
45 	uint32_t i = 0;
46 
47 	for (i = 0; i < (DURATION_MAX_LEN + ret_idx); i++)
48 		if (i == (DURATION_MAX_LEN - 2))
49 			*str++ = '|';
50 		else
51 			*str++ = ' ';
52 
53 	*str++ = '0';
54 	*str++ = 'x';
55 
56 	for (i = 0; i < addr_size; i++)
57 		*str++ = hex_str[(pc >> 4 * (addr_size - i - 1)) & 0xf];
58 
59 	*str++ = '(';
60 	*str++ = ')';
61 	*str++ = ' ';
62 	*str++ = '{';
63 	*str++ = '\n';
64 	*str = '\0';
65 
66 	return str - buf;
67 }
68 
69 void __noprof ftrace_enter(unsigned long pc, unsigned long *lr)
70 {
71 	struct ftrace_buf *fbuf = NULL;
72 	size_t dump_size = 0;
73 
74 	fbuf = &__ftrace_buf_start;
75 
76 	if (!fbuf->buf_off || !fbuf->max_size)
77 		return;
78 
79 	dump_size = DURATION_MAX_LEN + fbuf->ret_idx +
80 			(2 * sizeof(unsigned long)) + 8;
81 
82 	/*
83 	 * Check if we have enough space in ftrace buffer. If not then just
84 	 * remove oldest dump under the assumption that its the least
85 	 * interesting data.
86 	 */
87 	if ((fbuf->curr_size + dump_size) > fbuf->max_size)
88 		fbuf_shift(fbuf, dump_size);
89 
90 	fbuf->curr_size += to_func_enter_fmt((char *)fbuf + fbuf->buf_off +
91 					     fbuf->curr_size, fbuf->ret_idx,
92 					     pc);
93 
94 	if (fbuf->ret_idx < FTRACE_RETFUNC_DEPTH) {
95 		fbuf->ret_stack[fbuf->ret_idx] = *lr;
96 		fbuf->begin_time[fbuf->ret_idx] = read_cntpct();
97 		fbuf->ret_idx++;
98 	} else {
99 		/*
100 		 * This scenario isn't expected as function call depth
101 		 * shouldn't be more than FTRACE_RETFUNC_DEPTH.
102 		 */
103 		utee_panic(0);
104 	}
105 
106 	*lr = (unsigned long)&__ftrace_return;
107 }
108 
109 static void __noprof ftrace_duration(char *buf, uint64_t start, uint64_t end)
110 {
111 	uint32_t max_us = CFG_FTRACE_US_MS;
112 	uint32_t cntfrq = read_cntfrq();
113 	uint64_t ticks = end - start;
114 	uint32_t ms = 0;
115 	uint32_t us = 0;
116 	uint32_t ns = 0;
117 	uint32_t frac = 0;
118 	uint32_t in = 0;
119 	char unit = 'u';
120 	int i = 0;
121 
122 	ticks = ticks * 1000000000 / cntfrq;
123 	us = ticks / 1000;
124 	ns = ticks % 1000;
125 
126 	if (max_us && us >= max_us) {
127 		/* Display value in milliseconds */
128 		unit = 'm';
129 		ms = us / 1000;
130 		us = us % 1000;
131 		frac = us;
132 		in = ms;
133 	} else {
134 		/* Display value in microseconds */
135 		frac = ns;
136 		in = us;
137 	}
138 
139 	*buf-- = 's';
140 	*buf-- = unit;
141 	*buf-- = ' ';
142 
143 	COMPILE_TIME_ASSERT(DURATION_MAX_LEN == 16);
144 	if (in > 999999) {
145 		/* Not enough space to print the value */
146 		for (i = 0; i < 10; i++)
147 			*buf-- = '-';
148 		return;
149 	}
150 
151 	for (i = 0; i < 3; i++) {
152 		*buf-- = hex_str[frac % 10];
153 		frac /= 10;
154 	}
155 
156 	*buf-- = '.';
157 
158 	while (in) {
159 		*buf-- = hex_str[in % 10];
160 		in /= 10;
161 	}
162 }
163 
164 unsigned long __noprof ftrace_return(void)
165 {
166 	struct ftrace_buf *fbuf = NULL;
167 	size_t dump_size = 0;
168 	char *curr_buf = NULL;
169 	char *dur_loc = NULL;
170 	uint32_t i = 0;
171 
172 	fbuf = &__ftrace_buf_start;
173 
174 	/* Check for valid return index */
175 	if (fbuf->ret_idx && (fbuf->ret_idx <= FTRACE_RETFUNC_DEPTH))
176 		fbuf->ret_idx--;
177 	else
178 		return 0;
179 
180 	curr_buf = (char *)fbuf + fbuf->buf_off + fbuf->curr_size;
181 
182 	/*
183 	 * Check for '{' symbol as it represents if it is an exit from current
184 	 * or nested function. If exit is from current function, than exit dump
185 	 * via ';' symbol else exit dump via '}' symbol.
186 	 */
187 	if (*(curr_buf - 2) == '{') {
188 		*(curr_buf - 3) = ';';
189 		*(curr_buf - 2) = '\n';
190 		*(curr_buf - 1) = '\0';
191 		fbuf->curr_size -= 1;
192 
193 		dur_loc = curr_buf - (fbuf->ret_idx +
194 				      (2 * sizeof(unsigned long)) + 11);
195 		ftrace_duration(dur_loc, fbuf->begin_time[fbuf->ret_idx],
196 				read_cntpct());
197 	} else {
198 		dump_size = DURATION_MAX_LEN + fbuf->ret_idx + 3;
199 		if ((fbuf->curr_size + dump_size) > fbuf->max_size)
200 			fbuf_shift(fbuf, dump_size);
201 
202 		curr_buf = (char *)fbuf + fbuf->buf_off + fbuf->curr_size;
203 
204 		for (i = 0; i < (DURATION_MAX_LEN + fbuf->ret_idx); i++)
205 			if (i == (DURATION_MAX_LEN - 2))
206 				*curr_buf++ = '|';
207 			else
208 				*curr_buf++ = ' ';
209 
210 		*curr_buf++ = '}';
211 		*curr_buf++ = '\n';
212 		*curr_buf = '\0';
213 
214 		fbuf->curr_size += dump_size - 1;
215 
216 		dur_loc = curr_buf - fbuf->ret_idx - 6;
217 		ftrace_duration(dur_loc, fbuf->begin_time[fbuf->ret_idx],
218 				read_cntpct());
219 	}
220 
221 	return fbuf->ret_stack[fbuf->ret_idx];
222 }
223 
224 void __noprof ftrace_longjmp(unsigned int *ret_idx)
225 {
226 	while (__ftrace_buf_start.ret_idx > *ret_idx)
227 		ftrace_return();
228 }
229 
230 void __noprof ftrace_setjmp(unsigned int *ret_idx)
231 {
232 	*ret_idx = __ftrace_buf_start.ret_idx;
233 }
234