xref: /optee_os/lib/libutils/ext/trace.c (revision 44202a48c5de6db267056151a60995d7ed171383)
1 /*
2  * Copyright (c) 2014, STMicroelectronics International N.V.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <trace.h>
32 #include <util.h>
33 
34 #if (CFG_TRACE_LEVEL != 0)
35 
36 #if (CFG_TRACE_LEVEL < TRACE_MIN) || (CFG_TRACE_LEVEL > TRACE_MAX)
37 #error "Invalid value of CFG_TRACE_LEVEL"
38 #endif
39 static int trace_level = CFG_TRACE_LEVEL;
40 
41 void trace_set_level(int level)
42 {
43 	if (((int)level >= TRACE_MIN) && (level <= TRACE_MAX))
44 		trace_level = level;
45 	else
46 		trace_level = TRACE_MAX;
47 }
48 
49 int trace_get_level(void)
50 {
51 	return trace_level;
52 }
53 
54 static const char *trace_level_to_string(int level, bool level_ok)
55 {
56 	static const char lvl_strs[][4] = {
57 		"UKN", "ERR", "INF", "DBG", "FLW" };
58 	int l = 0;
59 
60 	if (!level_ok)
61 		return "MSG";
62 
63 	if ((level >= TRACE_MIN) && (level <= TRACE_MAX))
64 		l = level;
65 
66 	return lvl_strs[l];
67 }
68 
69 /* Format trace of user ta. Inline with kernel ta */
70 void trace_printf(const char *function, int line, int level, bool level_ok,
71 		  bool sync, const char *fmt, ...)
72 {
73 	va_list ap;
74 	char buf[MAX_PRINT_SIZE];
75 	size_t boffs = 0;
76 	int res;
77 
78 	if (level_ok && level > trace_level)
79 		return;
80 
81 	if (function) {
82 		int thread_id = trace_ext_get_thread_id();
83 
84 		if (thread_id >= 0)
85 			res = snprintf(buf, sizeof(buf), "%s [0x%x] %s:%s:%d: ",
86 				       trace_level_to_string(level, level_ok),
87 				       thread_id, trace_ext_prefix,
88 				       function, line);
89 		else
90 			res = snprintf(buf, sizeof(buf), "%s %s:%s:%d: ",
91 				       trace_level_to_string(level, level_ok),
92 				       trace_ext_prefix, function, line);
93 		if (res < 0)
94 			return; /* "Can't happen" */
95 		boffs = res;
96 	}
97 
98 	va_start(ap, fmt);
99 	res = vsnprintf(buf + boffs, sizeof(buf) - boffs, fmt, ap);
100 	va_end(ap);
101 	if (res > 0)
102 		boffs += res;
103 
104 	if (boffs >= sizeof(buf)) {
105 		boffs = sizeof(buf) - 2;
106 		/* Make there's a newline at the end */
107 		buf[boffs] = '\n';
108 	} else if (buf[boffs - 1] != '\n') {
109 		/* Append a newline */
110 		buf[boffs] = '\n';
111 		buf[boffs + 1] = '\0';
112 	}
113 
114 	trace_ext_puts(sync, buf);
115 }
116 #endif
117 
118 #if (CFG_TRACE_LEVEL >= TRACE_DEBUG)
119 struct strbuf {
120 	char buf[MAX_PRINT_SIZE];
121 	char *ptr;
122 };
123 
124 static int __printf(2, 3) append(struct strbuf *sbuf, const char *fmt, ...)
125 {
126 	int left;
127 	int len;
128 	va_list ap;
129 
130 	if (sbuf->ptr == NULL)
131 		sbuf->ptr = sbuf->buf;
132 	left = sizeof(sbuf->buf) - (sbuf->ptr - sbuf->buf);
133 	va_start(ap, fmt);
134 	len = vsnprintf(sbuf->ptr, left, fmt, ap);
135 	va_end(ap);
136 	if (len < 0) {
137 		/* Format error */
138 		return 0;
139 	}
140 	if (len >= left) {
141 		/* Output was truncated */
142 		return 0;
143 	}
144 	sbuf->ptr += MIN(left, len);
145 	return 1;
146 }
147 
148 void dhex_dump(const char *function, int line, int level,
149 	       const void *buf, int len)
150 {
151 	int i;
152 	int ok;
153 	struct strbuf sbuf;
154 	char *in = (char *)buf;
155 
156 	if (level <= trace_level) {
157 		sbuf.ptr = NULL;
158 		for (i = 0; i < len; i++) {
159 			ok = append(&sbuf, "%02x ", in[i]);
160 			if (!ok)
161 				goto err;
162 			if ((i % 16) == 7) {
163 				ok = append(&sbuf, " ");
164 				if (!ok)
165 					goto err;
166 			} else if ((i % 16) == 15) {
167 				trace_printf(function, line, level, true, false,
168 					      "%s", sbuf.buf);
169 				sbuf.ptr = NULL;
170 			}
171 		}
172 		if (sbuf.ptr) {
173 			/* Buffer is not empty: flush it */
174 			trace_printf(function, line, level, true, false, "%s",
175 				      sbuf.buf);
176 
177 		}
178 	}
179 	return;
180 err:
181 	DMSG("Hex dump error");
182 }
183 #endif
184