1 |
786 |
skrzyp |
//{{{ Banner
|
2 |
|
|
|
3 |
|
|
//============================================================================
|
4 |
|
|
//
|
5 |
|
|
// trace.cxx
|
6 |
|
|
//
|
7 |
|
|
// Host side implementation of the infrastructure trace facilities.
|
8 |
|
|
//
|
9 |
|
|
//============================================================================
|
10 |
|
|
// ####ECOSHOSTGPLCOPYRIGHTBEGIN####
|
11 |
|
|
// -------------------------------------------
|
12 |
|
|
// This file is part of the eCos host tools.
|
13 |
|
|
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
|
14 |
|
|
//
|
15 |
|
|
// This program is free software; you can redistribute it and/or modify
|
16 |
|
|
// it under the terms of the GNU General Public License as published by
|
17 |
|
|
// the Free Software Foundation; either version 2 or (at your option) any
|
18 |
|
|
// later version.
|
19 |
|
|
//
|
20 |
|
|
// This program is distributed in the hope that it will be useful, but
|
21 |
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
22 |
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
23 |
|
|
// General Public License for more details.
|
24 |
|
|
//
|
25 |
|
|
// You should have received a copy of the GNU General Public License
|
26 |
|
|
// along with this program; if not, write to the
|
27 |
|
|
// Free Software Foundation, Inc., 51 Franklin Street,
|
28 |
|
|
// Fifth Floor, Boston, MA 02110-1301, USA.
|
29 |
|
|
// -------------------------------------------
|
30 |
|
|
// ####ECOSHOSTGPLCOPYRIGHTEND####
|
31 |
|
|
//============================================================================
|
32 |
|
|
//#####DESCRIPTIONBEGIN####
|
33 |
|
|
//
|
34 |
|
|
// Author(s): bartv
|
35 |
|
|
// Contact(s): bartv
|
36 |
|
|
// Date: 1998/12/07
|
37 |
|
|
// Version: 0.01
|
38 |
|
|
// Purpose: To provide a host-side implementation of the eCos tracing
|
39 |
|
|
// facilities.
|
40 |
|
|
//
|
41 |
|
|
//####DESCRIPTIONEND####
|
42 |
|
|
//============================================================================
|
43 |
|
|
|
44 |
|
|
//}}}
|
45 |
|
|
//{{{ #include's
|
46 |
|
|
|
47 |
|
|
// Make sure that the host-side extensions get prototyped
|
48 |
|
|
// as well. Note that the tracing code needs to interact
|
49 |
|
|
// with the assertion facilities to set up an appropriate
|
50 |
|
|
// callback.
|
51 |
|
|
#define CYG_DECLARE_HOST_ASSERTION_SUPPORT
|
52 |
|
|
#include "pkgconf/infra.h"
|
53 |
|
|
#include "cyg/infra/cyg_type.h"
|
54 |
|
|
#include "cyg/infra/cyg_ass.h"
|
55 |
|
|
|
56 |
|
|
// Without this #define the tracing enums and prototypes are
|
57 |
|
|
// not visible.
|
58 |
|
|
#define CYGDBG_USE_TRACING
|
59 |
|
|
#include "cyg/infra/cyg_trac.h"
|
60 |
|
|
|
61 |
|
|
// The standard C++ string class is used extensively
|
62 |
|
|
#include <string>
|
63 |
|
|
|
64 |
|
|
// Add a few C headers
|
65 |
|
|
#include <cctype>
|
66 |
|
|
#include <cstring>
|
67 |
|
|
#include <cstdio>
|
68 |
|
|
|
69 |
|
|
//}}}
|
70 |
|
|
|
71 |
|
|
//{{{ Description
|
72 |
|
|
|
73 |
|
|
// -------------------------------------------------------------------------
|
74 |
|
|
// The tracing macros end up calling one of the following routines:
|
75 |
|
|
//
|
76 |
|
|
// void cyg_tracenomsg(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line)
|
77 |
|
|
// void cyg_tracemsg( ..., const char* msg)
|
78 |
|
|
// void cyg_tracemsg2( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1 )
|
79 |
|
|
// void cyg_tracemsg4( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1, ... )
|
80 |
|
|
// void cyg_tracemsg6( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1, ... )
|
81 |
|
|
// void cyg_tracemsg8( ..., CYG_ADDRWORD arg0, CYG_ADDRWORD arg1, ... )
|
82 |
|
|
//
|
83 |
|
|
// For the 2/4/6/8 variants the msg argument is essentially a printf()
|
84 |
|
|
// style format string. However the intention is that the implementation
|
85 |
|
|
// of the trace code can delay doing the formatting until the trace
|
86 |
|
|
// information is actually needed (with obvious consequences for
|
87 |
|
|
// generated strings). Such an implementation would significantly
|
88 |
|
|
// reduce the overheads associated with tracing, and is what is implemented
|
89 |
|
|
// here.
|
90 |
|
|
//
|
91 |
|
|
// CYG_ADDRWORD is likely to be either "int" or the platform-specific
|
92 |
|
|
// 64 bit data type: it should be big enough to hold either a pointer
|
93 |
|
|
// or any normal integral type. This causes problems on machines which
|
94 |
|
|
// have e.g. 32 bit int and 64 bit long: any 32 bit quantities will
|
95 |
|
|
// have been converted to 64 bit quantities in the calling code, and
|
96 |
|
|
// it is no longer possible to just pass the format string to sprintf().
|
97 |
|
|
// Instead what amounts to a re-implementation of sprintf() is needed
|
98 |
|
|
// here.
|
99 |
|
|
//
|
100 |
|
|
// The basic implementation of this trace code is as follows:
|
101 |
|
|
//
|
102 |
|
|
// 1) a static array of data structures to hold the trace data. The
|
103 |
|
|
// size can be configured. There is a current index into this
|
104 |
|
|
// array.
|
105 |
|
|
//
|
106 |
|
|
// 2) the various trace functions simply update this array and the
|
107 |
|
|
// counter.
|
108 |
|
|
//
|
109 |
|
|
// 3) all of the trace functions also check a static to see whether
|
110 |
|
|
// or not it is necessary to install a trace handler. This cannot
|
111 |
|
|
// be done by means of a static object due to constructor priority
|
112 |
|
|
// ordering problems.
|
113 |
|
|
//
|
114 |
|
|
// 4) the callback function does all the hardware of the formatting
|
115 |
|
|
// etc.
|
116 |
|
|
|
117 |
|
|
//}}}
|
118 |
|
|
//{{{ Types and statics
|
119 |
|
|
|
120 |
|
|
// ----------------------------------------------------------------------------
|
121 |
|
|
// A data structure rather than a class is used to hold the trace data.
|
122 |
|
|
// This guarantees that the array gets put in the bss section and is properly
|
123 |
|
|
// zeroed. A "valid" field in the structure can be checked when dumping the
|
124 |
|
|
// array.
|
125 |
|
|
|
126 |
|
|
typedef struct trace_entry {
|
127 |
|
|
bool valid;
|
128 |
|
|
cyg_uint32 what;
|
129 |
|
|
cyg_uint32 line;
|
130 |
|
|
const char* fn;
|
131 |
|
|
const char* file;
|
132 |
|
|
const char* msg;
|
133 |
|
|
CYG_ADDRWORD data[8];
|
134 |
|
|
} trace_entry;
|
135 |
|
|
|
136 |
|
|
#ifndef CYGNUM_INFRA_TRACE_VECTOR_SIZE
|
137 |
|
|
# define CYGNUM_INFRA_TRACE_VECTOR_SIZE 2048
|
138 |
|
|
#endif
|
139 |
|
|
|
140 |
|
|
static trace_entry tracevec[CYGNUM_INFRA_TRACE_VECTOR_SIZE];
|
141 |
|
|
static volatile int trace_index = 0;
|
142 |
|
|
|
143 |
|
|
// Forward declaration of the callback function, for convenience.
|
144 |
|
|
static void trace_callback(void (*)(const char*));
|
145 |
|
|
|
146 |
|
|
// Has the callback been installed yet?
|
147 |
|
|
static bool callback_installed = false;
|
148 |
|
|
|
149 |
|
|
//}}}
|
150 |
|
|
//{{{ The trace functions themselves
|
151 |
|
|
|
152 |
|
|
// ----------------------------------------------------------------------------
|
153 |
|
|
// The functions that get called by the trace macros. Typically these work
|
154 |
|
|
// as follows:
|
155 |
|
|
//
|
156 |
|
|
// 1) read and increment the trace index. This makes tracing marginally usable
|
157 |
|
|
// in multi-threaded systems.
|
158 |
|
|
//
|
159 |
|
|
// 2) invalidate the entry that is about to be updated. Again this helps a bit
|
160 |
|
|
// with multi-threaded systems.
|
161 |
|
|
//
|
162 |
|
|
// 3) fill in all the fields as per the command-line arguments, zeroing
|
163 |
|
|
// unused fields.
|
164 |
|
|
//
|
165 |
|
|
// 4) set the valid flag to true, which means the contents can now be output.
|
166 |
|
|
//
|
167 |
|
|
// This is by no means sufficient to guarantee that a call to dump the trace
|
168 |
|
|
// vector in some other thread can work safely, but it may help a little bit.
|
169 |
|
|
|
170 |
|
|
extern "C" void
|
171 |
|
|
cyg_tracenomsg(const char* fn, const char* file, cyg_uint32 line)
|
172 |
|
|
{
|
173 |
|
|
int i = trace_index;
|
174 |
|
|
tracevec[i].valid = false;
|
175 |
|
|
trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
|
176 |
|
|
|
177 |
|
|
tracevec[i].what = cyg_trace_trace;
|
178 |
|
|
tracevec[i].fn = fn;
|
179 |
|
|
tracevec[i].file = file;
|
180 |
|
|
tracevec[i].line = line;
|
181 |
|
|
tracevec[i].msg = 0;
|
182 |
|
|
tracevec[i].data[0] = 0;
|
183 |
|
|
tracevec[i].data[1] = 0;
|
184 |
|
|
tracevec[i].data[2] = 0;
|
185 |
|
|
tracevec[i].data[3] = 0;
|
186 |
|
|
tracevec[i].data[4] = 0;
|
187 |
|
|
tracevec[i].data[5] = 0;
|
188 |
|
|
tracevec[i].data[6] = 0;
|
189 |
|
|
tracevec[i].data[7] = 0;
|
190 |
|
|
tracevec[i].valid = true;
|
191 |
|
|
|
192 |
|
|
if (!callback_installed) {
|
193 |
|
|
cyg_assert_install_failure_callback("Trace", &trace_callback);
|
194 |
|
|
callback_installed = true;
|
195 |
|
|
}
|
196 |
|
|
}
|
197 |
|
|
|
198 |
|
|
extern "C" void
|
199 |
|
|
cyg_tracemsg(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line, const char* msg)
|
200 |
|
|
{
|
201 |
|
|
int i = trace_index;
|
202 |
|
|
tracevec[i].valid = false;
|
203 |
|
|
trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
|
204 |
|
|
|
205 |
|
|
tracevec[i].what = what;
|
206 |
|
|
tracevec[i].fn = fn;
|
207 |
|
|
tracevec[i].file = file;
|
208 |
|
|
tracevec[i].line = line;
|
209 |
|
|
tracevec[i].msg = msg;
|
210 |
|
|
tracevec[i].data[0] = 0;
|
211 |
|
|
tracevec[i].data[1] = 0;
|
212 |
|
|
tracevec[i].data[2] = 0;
|
213 |
|
|
tracevec[i].data[3] = 0;
|
214 |
|
|
tracevec[i].data[4] = 0;
|
215 |
|
|
tracevec[i].data[5] = 0;
|
216 |
|
|
tracevec[i].data[6] = 0;
|
217 |
|
|
tracevec[i].data[7] = 0;
|
218 |
|
|
tracevec[i].valid = true;
|
219 |
|
|
|
220 |
|
|
if (!callback_installed) {
|
221 |
|
|
cyg_assert_install_failure_callback("Trace", &trace_callback);
|
222 |
|
|
callback_installed = true;
|
223 |
|
|
}
|
224 |
|
|
}
|
225 |
|
|
|
226 |
|
|
extern "C" void
|
227 |
|
|
cyg_tracemsg2(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line, const char *msg,
|
228 |
|
|
CYG_ADDRWORD arg0, CYG_ADDRWORD arg1)
|
229 |
|
|
{
|
230 |
|
|
int i = trace_index;
|
231 |
|
|
tracevec[i].valid = false;
|
232 |
|
|
trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
|
233 |
|
|
|
234 |
|
|
tracevec[i].what = what;
|
235 |
|
|
tracevec[i].fn = fn;
|
236 |
|
|
tracevec[i].file = file;
|
237 |
|
|
tracevec[i].line = line;
|
238 |
|
|
tracevec[i].msg = msg;
|
239 |
|
|
tracevec[i].data[0] = arg0;
|
240 |
|
|
tracevec[i].data[1] = arg1;
|
241 |
|
|
tracevec[i].data[2] = 0;
|
242 |
|
|
tracevec[i].data[3] = 0;
|
243 |
|
|
tracevec[i].data[4] = 0;
|
244 |
|
|
tracevec[i].data[5] = 0;
|
245 |
|
|
tracevec[i].data[6] = 0;
|
246 |
|
|
tracevec[i].data[7] = 0;
|
247 |
|
|
tracevec[i].valid = true;
|
248 |
|
|
|
249 |
|
|
if (!callback_installed) {
|
250 |
|
|
cyg_assert_install_failure_callback("Trace", &trace_callback);
|
251 |
|
|
callback_installed = true;
|
252 |
|
|
}
|
253 |
|
|
}
|
254 |
|
|
|
255 |
|
|
extern "C" void
|
256 |
|
|
cyg_tracemsg4(cyg_uint32 what, const char *fn, const char* file, cyg_uint32 line, const char *msg,
|
257 |
|
|
CYG_ADDRWORD arg0, CYG_ADDRWORD arg1,
|
258 |
|
|
CYG_ADDRWORD arg2, CYG_ADDRWORD arg3)
|
259 |
|
|
{
|
260 |
|
|
int i = trace_index;
|
261 |
|
|
tracevec[i].valid = false;
|
262 |
|
|
trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
|
263 |
|
|
|
264 |
|
|
tracevec[i].what = what;
|
265 |
|
|
tracevec[i].fn = fn;
|
266 |
|
|
tracevec[i].file = file;
|
267 |
|
|
tracevec[i].line = line;
|
268 |
|
|
tracevec[i].msg = msg;
|
269 |
|
|
tracevec[i].data[0] = arg0;
|
270 |
|
|
tracevec[i].data[1] = arg1;
|
271 |
|
|
tracevec[i].data[2] = arg2;
|
272 |
|
|
tracevec[i].data[3] = arg3;
|
273 |
|
|
tracevec[i].data[4] = 0;
|
274 |
|
|
tracevec[i].data[5] = 0;
|
275 |
|
|
tracevec[i].data[6] = 0;
|
276 |
|
|
tracevec[i].data[7] = 0;
|
277 |
|
|
tracevec[i].valid = true;
|
278 |
|
|
|
279 |
|
|
if (!callback_installed) {
|
280 |
|
|
cyg_assert_install_failure_callback("Trace", &trace_callback);
|
281 |
|
|
callback_installed = true;
|
282 |
|
|
}
|
283 |
|
|
}
|
284 |
|
|
|
285 |
|
|
extern "C" void
|
286 |
|
|
cyg_tracemsg6(cyg_uint32 what, const char *fn, const char* file, cyg_uint32 line, const char *msg,
|
287 |
|
|
CYG_ADDRWORD arg0, CYG_ADDRWORD arg1,
|
288 |
|
|
CYG_ADDRWORD arg2, CYG_ADDRWORD arg3,
|
289 |
|
|
CYG_ADDRWORD arg4, CYG_ADDRWORD arg5)
|
290 |
|
|
{
|
291 |
|
|
int i = trace_index;
|
292 |
|
|
tracevec[i].valid = false;
|
293 |
|
|
trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
|
294 |
|
|
|
295 |
|
|
tracevec[i].what = what;
|
296 |
|
|
tracevec[i].fn = fn;
|
297 |
|
|
tracevec[i].file = file;
|
298 |
|
|
tracevec[i].line = line;
|
299 |
|
|
tracevec[i].msg = msg;
|
300 |
|
|
tracevec[i].data[0] = arg0;
|
301 |
|
|
tracevec[i].data[1] = arg1;
|
302 |
|
|
tracevec[i].data[2] = arg2;
|
303 |
|
|
tracevec[i].data[3] = arg3;
|
304 |
|
|
tracevec[i].data[4] = arg4;
|
305 |
|
|
tracevec[i].data[5] = arg5;
|
306 |
|
|
tracevec[i].data[6] = 0;
|
307 |
|
|
tracevec[i].data[7] = 0;
|
308 |
|
|
tracevec[i].valid = true;
|
309 |
|
|
|
310 |
|
|
if (!callback_installed) {
|
311 |
|
|
cyg_assert_install_failure_callback("Trace", &trace_callback);
|
312 |
|
|
callback_installed = true;
|
313 |
|
|
}
|
314 |
|
|
}
|
315 |
|
|
|
316 |
|
|
extern "C" void
|
317 |
|
|
cyg_tracemsg8(cyg_uint32 what, const char* fn, const char* file, cyg_uint32 line, const char *msg,
|
318 |
|
|
CYG_ADDRWORD arg0, CYG_ADDRWORD arg1,
|
319 |
|
|
CYG_ADDRWORD arg2, CYG_ADDRWORD arg3,
|
320 |
|
|
CYG_ADDRWORD arg4, CYG_ADDRWORD arg5,
|
321 |
|
|
CYG_ADDRWORD arg6, CYG_ADDRWORD arg7)
|
322 |
|
|
{
|
323 |
|
|
int i = trace_index;
|
324 |
|
|
tracevec[i].valid = false;
|
325 |
|
|
trace_index = (trace_index + 1) % CYGNUM_INFRA_TRACE_VECTOR_SIZE;
|
326 |
|
|
|
327 |
|
|
tracevec[i].what = what;
|
328 |
|
|
tracevec[i].fn = fn;
|
329 |
|
|
tracevec[i].file = file;
|
330 |
|
|
tracevec[i].line = line;
|
331 |
|
|
tracevec[i].msg = msg;
|
332 |
|
|
tracevec[i].data[0] = arg0;
|
333 |
|
|
tracevec[i].data[1] = arg1;
|
334 |
|
|
tracevec[i].data[2] = arg2;
|
335 |
|
|
tracevec[i].data[3] = arg3;
|
336 |
|
|
tracevec[i].data[4] = arg4;
|
337 |
|
|
tracevec[i].data[5] = arg5;
|
338 |
|
|
tracevec[i].data[6] = arg6;
|
339 |
|
|
tracevec[i].data[7] = arg7;
|
340 |
|
|
tracevec[i].valid = true;
|
341 |
|
|
|
342 |
|
|
if (!callback_installed) {
|
343 |
|
|
cyg_assert_install_failure_callback("Trace", &trace_callback);
|
344 |
|
|
callback_installed = true;
|
345 |
|
|
}
|
346 |
|
|
}
|
347 |
|
|
|
348 |
|
|
//}}}
|
349 |
|
|
//{{{ Output callback
|
350 |
|
|
|
351 |
|
|
// ----------------------------------------------------------------------------
|
352 |
|
|
// Dumping the output. The assertion code will invoke a single callback
|
353 |
|
|
// function, cyg_trace_dummy::trace_callback(), with a function pointer
|
354 |
|
|
// that can be used for the actual output.
|
355 |
|
|
//
|
356 |
|
|
// The trace_callback() function loops through the various entries in the
|
357 |
|
|
// vector, ignoring invalid ones, and invoking output_entry() for the
|
358 |
|
|
// valid ones.
|
359 |
|
|
//
|
360 |
|
|
// There are a number of utility routines:
|
361 |
|
|
//
|
362 |
|
|
// trim_file() is used to take a full pathname and return just the
|
363 |
|
|
// final part of it as a C++ string. There is an upper bound on the
|
364 |
|
|
// length of this string.
|
365 |
|
|
//
|
366 |
|
|
// trim_linenum() formats the linenumber sensibly.
|
367 |
|
|
//
|
368 |
|
|
// trim_function() is used to parse a __PRETTY_FUNCTION__ value
|
369 |
|
|
// and produce something more manageable.
|
370 |
|
|
//
|
371 |
|
|
// parse_msg() is used to construct the full trace message.
|
372 |
|
|
// Because of possible 32/64 bit confusion it is not possible
|
373 |
|
|
// to just use sprintf() for this.
|
374 |
|
|
|
375 |
|
|
static std::string
|
376 |
|
|
trim_file(const char* file)
|
377 |
|
|
{
|
378 |
|
|
// If the output is to look reasonable then the result should be a
|
379 |
|
|
// fixed length. 20 characters is reasonable for now.
|
380 |
|
|
const int max_filename_len = 20;
|
381 |
|
|
|
382 |
|
|
if (0 == file) {
|
383 |
|
|
return std::string(max_filename_len, ' ');
|
384 |
|
|
}
|
385 |
|
|
|
386 |
|
|
// Move to the end of the string, and then back again until
|
387 |
|
|
// a directory separator is found. Given the number of levels
|
388 |
|
|
// in a typical eCos directory hierarchy it is probably not
|
389 |
|
|
// worthwhile outputting any of that information.
|
390 |
|
|
const char * pEnd = file + strlen(file);
|
391 |
|
|
while ((pEnd > file) && ('/' != *pEnd) && ('\\' != *pEnd)) {
|
392 |
|
|
pEnd--;
|
393 |
|
|
}
|
394 |
|
|
if (pEnd != file)
|
395 |
|
|
pEnd++;
|
396 |
|
|
|
397 |
|
|
std::string result = "";
|
398 |
|
|
int i = 0;
|
399 |
|
|
for ( ;(*pEnd != '\0') && (i < max_filename_len); i++, pEnd++) {
|
400 |
|
|
result += *pEnd;
|
401 |
|
|
}
|
402 |
|
|
for ( ; i < max_filename_len; i++) {
|
403 |
|
|
result += ' ';
|
404 |
|
|
}
|
405 |
|
|
|
406 |
|
|
return result;
|
407 |
|
|
}
|
408 |
|
|
|
409 |
|
|
// The linenumber output should be up to four digits, right-padded
|
410 |
|
|
// with spaces. sprintf() will do the trick nicely.
|
411 |
|
|
|
412 |
|
|
static std::string
|
413 |
|
|
trim_linenum(cyg_uint32 line)
|
414 |
|
|
{
|
415 |
|
|
char buf[32];
|
416 |
|
|
sprintf(buf, "%-4d", (int) line);
|
417 |
|
|
return buf;
|
418 |
|
|
}
|
419 |
|
|
|
420 |
|
|
// Extract a function name. On the target side function names
|
421 |
|
|
// are usually obtained via __PRETTY_FUNCTION__, and the resulting
|
422 |
|
|
// output is a bit on the large side: return value, arguments, etc
|
423 |
|
|
// are all included. On the host side the function name is normally
|
424 |
|
|
// supplied explicitly and should not be trimmed at all.
|
425 |
|
|
//
|
426 |
|
|
// Padding is not appropriate since the function name is likely
|
427 |
|
|
// to be followed immediately by the argument list. No maximum
|
428 |
|
|
// length is imposed - arguably that is a bad idea.
|
429 |
|
|
static std::string
|
430 |
|
|
trim_function(const char* fn)
|
431 |
|
|
{
|
432 |
|
|
if (0 == fn) {
|
433 |
|
|
return "<unknown>";
|
434 |
|
|
}
|
435 |
|
|
|
436 |
|
|
#if 1
|
437 |
|
|
return fn;
|
438 |
|
|
#else
|
439 |
|
|
// This implements the target-side behaviour.
|
440 |
|
|
//
|
441 |
|
|
// First locate the opening bracket. The function name can
|
442 |
|
|
// be identified by walking backwards from that.
|
443 |
|
|
const char *s;
|
444 |
|
|
for (s = fn; ('\0' != *s) && ('(' != *s); s++);
|
445 |
|
|
for ( ; (s > fn) && (*s != ' '); s--);
|
446 |
|
|
if ( s > fn) s++;
|
447 |
|
|
|
448 |
|
|
std::string result = "";
|
449 |
|
|
while ( ('\0' != *s) && ('(' != *s) )
|
450 |
|
|
result += *s++;
|
451 |
|
|
|
452 |
|
|
return result;
|
453 |
|
|
#endif
|
454 |
|
|
}
|
455 |
|
|
|
456 |
|
|
// The trace format string contained a %s. It is necessary to check
|
457 |
|
|
// whether the argument is still valid, and return a suitable
|
458 |
|
|
// approximation to the actual data.
|
459 |
|
|
static std::string
|
460 |
|
|
trim_string(const char * arg)
|
461 |
|
|
{
|
462 |
|
|
const int max_string_len = 20;
|
463 |
|
|
|
464 |
|
|
std::string result = "";
|
465 |
|
|
if (0 == arg) {
|
466 |
|
|
return result;
|
467 |
|
|
}
|
468 |
|
|
int i;
|
469 |
|
|
for ( i = 0; (i < max_string_len) && ('\0' != *arg) && isprint(*arg); i++, arg++) {
|
470 |
|
|
result += *arg;
|
471 |
|
|
}
|
472 |
|
|
return result;
|
473 |
|
|
}
|
474 |
|
|
|
475 |
|
|
// ----------------------------------------------------------------------------
|
476 |
|
|
// Parse a printf() style format string and do the appropriate expansions.
|
477 |
|
|
// Because of possible confusion between 32 and 64 bit integers it is not
|
478 |
|
|
// possible to use sprintf() itself.
|
479 |
|
|
//
|
480 |
|
|
// It is assumed that the format string is valid, as are most of the
|
481 |
|
|
// arguments. The possible exception is %s arguments where a little bit of
|
482 |
|
|
// checking happens first.
|
483 |
|
|
|
484 |
|
|
static std::string
|
485 |
|
|
parse_msg(const char* msg, trace_entry& entry)
|
486 |
|
|
{
|
487 |
|
|
if (0 == msg) {
|
488 |
|
|
return "";
|
489 |
|
|
}
|
490 |
|
|
// Keep track of the number of arguments in the trace_entry
|
491 |
|
|
// that have been processed.
|
492 |
|
|
int args_index = 0;
|
493 |
|
|
|
494 |
|
|
// A utility buffer for sprintf(), e.g. for integer-> string conversions.
|
495 |
|
|
char util_buf[64];
|
496 |
|
|
|
497 |
|
|
std::string result = "";
|
498 |
|
|
for ( ; '\0' != *msg; msg++) {
|
499 |
|
|
|
500 |
|
|
if ('%' != *msg) {
|
501 |
|
|
result += *msg;
|
502 |
|
|
continue;
|
503 |
|
|
}
|
504 |
|
|
|
505 |
|
|
// We have a format string. Extract all of it.
|
506 |
|
|
std::string format = "%";
|
507 |
|
|
msg++;
|
508 |
|
|
|
509 |
|
|
// The first part of the format string may be one or more flags.
|
510 |
|
|
while ( ('-' == *msg) || ('+' == *msg) || (' ' == *msg) ||
|
511 |
|
|
('#' == *msg) || ('0' == *msg) ) {
|
512 |
|
|
format += *msg++;
|
513 |
|
|
}
|
514 |
|
|
|
515 |
|
|
// Next comes the width. If this is an asterix it is necessary to
|
516 |
|
|
// substitute in an actual argument.
|
517 |
|
|
if ('*' == *msg) {
|
518 |
|
|
int width = (args_index < 8) ? (int) entry.data[args_index++] : 0;
|
519 |
|
|
sprintf(util_buf, "%d", width);
|
520 |
|
|
format += util_buf;
|
521 |
|
|
msg++;
|
522 |
|
|
} else {
|
523 |
|
|
// Otherwise the width should be one or more digits
|
524 |
|
|
while( isdigit(*msg) ) {
|
525 |
|
|
format += *msg++;
|
526 |
|
|
}
|
527 |
|
|
}
|
528 |
|
|
|
529 |
|
|
// Look for a precision, again coping with an asterix.
|
530 |
|
|
if ('.' == *msg) {
|
531 |
|
|
format += *msg++;
|
532 |
|
|
if ('*' == *msg) {
|
533 |
|
|
int precision = (args_index < 8) ? (int) entry.data[args_index++] : 0;
|
534 |
|
|
sprintf(util_buf, "%d", precision);
|
535 |
|
|
format += util_buf;
|
536 |
|
|
msg++;
|
537 |
|
|
} else {
|
538 |
|
|
// The precision should be one or more digits, with an optional -
|
539 |
|
|
if ('-' == *msg) {
|
540 |
|
|
format += *msg++;
|
541 |
|
|
}
|
542 |
|
|
while (isdigit(*msg)) {
|
543 |
|
|
format += *msg++;
|
544 |
|
|
}
|
545 |
|
|
}
|
546 |
|
|
}
|
547 |
|
|
|
548 |
|
|
// Now look for h,l and L. These have to be remembered.
|
549 |
|
|
bool short_version = false;
|
550 |
|
|
bool long_version = false;
|
551 |
|
|
if ('h' == *msg) {
|
552 |
|
|
format += *msg++;
|
553 |
|
|
short_version = true;
|
554 |
|
|
} else if (('l' == *msg) || ('L' == *msg)) {
|
555 |
|
|
format += *msg++;
|
556 |
|
|
long_version = true;
|
557 |
|
|
}
|
558 |
|
|
|
559 |
|
|
// The end of the format string has been reached.
|
560 |
|
|
int format_ch = *msg;
|
561 |
|
|
format += *msg;
|
562 |
|
|
|
563 |
|
|
// If we have already formatted too many arguments, there is no point
|
564 |
|
|
// in trying to do the actual formatting.
|
565 |
|
|
if ( 8 <= args_index ) {
|
566 |
|
|
continue;
|
567 |
|
|
}
|
568 |
|
|
CYG_ADDRWORD val = entry.data[args_index++];
|
569 |
|
|
|
570 |
|
|
switch( format_ch ) {
|
571 |
|
|
case '%' :
|
572 |
|
|
result += '%';
|
573 |
|
|
break;
|
574 |
|
|
|
575 |
|
|
case 'd' :
|
576 |
|
|
case 'i' :
|
577 |
|
|
case 'o' :
|
578 |
|
|
case 'u' :
|
579 |
|
|
case 'x' :
|
580 |
|
|
case 'X' :
|
581 |
|
|
// "format" contains the appropriate format string.
|
582 |
|
|
// Invoke sprintf() using util_buf, doing the
|
583 |
|
|
// appropriate cast, and then append the output
|
584 |
|
|
// of util_buf.
|
585 |
|
|
//
|
586 |
|
|
// This is not totally robust. If a ridiculous
|
587 |
|
|
// precision has been specified then util_buf may
|
588 |
|
|
// overflow.
|
589 |
|
|
if (long_version) {
|
590 |
|
|
sprintf(util_buf, format.c_str(), (long) val);
|
591 |
|
|
} else {
|
592 |
|
|
// The implicit cast rules mean that shorts do not
|
593 |
|
|
// require any special attention.
|
594 |
|
|
sprintf(util_buf, format.c_str(), (int) val);
|
595 |
|
|
}
|
596 |
|
|
result += util_buf;
|
597 |
|
|
break;
|
598 |
|
|
|
599 |
|
|
case 'c' :
|
600 |
|
|
sprintf(util_buf, format.c_str(), (int) val);
|
601 |
|
|
result += util_buf;
|
602 |
|
|
break;
|
603 |
|
|
|
604 |
|
|
case 'p' :
|
605 |
|
|
sprintf(util_buf, format.c_str(), (void *) val);
|
606 |
|
|
result += util_buf;
|
607 |
|
|
break;
|
608 |
|
|
|
609 |
|
|
case 's' :
|
610 |
|
|
{
|
611 |
|
|
std::string data = trim_string((char *) val);
|
612 |
|
|
sprintf(util_buf, format.c_str(), data.c_str());
|
613 |
|
|
result += util_buf;
|
614 |
|
|
break;
|
615 |
|
|
}
|
616 |
|
|
|
617 |
|
|
default :
|
618 |
|
|
// Any attempt to do floating point conversions would be
|
619 |
|
|
// rather tricky given the casts that have been applied.
|
620 |
|
|
// There is no point in doing anything for unrecognised
|
621 |
|
|
// sequences.
|
622 |
|
|
break;
|
623 |
|
|
}
|
624 |
|
|
}
|
625 |
|
|
return result;
|
626 |
|
|
}
|
627 |
|
|
|
628 |
|
|
// ----------------------------------------------------------------------------
|
629 |
|
|
|
630 |
|
|
|
631 |
|
|
static void
|
632 |
|
|
output_entry(void (*pOutputFn)(const char*), trace_entry& entry)
|
633 |
|
|
{
|
634 |
|
|
std::string output = trim_file(entry.file) + " " +
|
635 |
|
|
trim_linenum(entry.line) + " " +
|
636 |
|
|
trim_function(entry.fn) + " ";
|
637 |
|
|
if (0 != entry.msg) {
|
638 |
|
|
|
639 |
|
|
switch( entry.what) {
|
640 |
|
|
case cyg_trace_trace : output += " '"; break;
|
641 |
|
|
case cyg_trace_enter : output += "{{"; break;
|
642 |
|
|
case cyg_trace_args : output += "(("; break;
|
643 |
|
|
case cyg_trace_return : output += "}}"; break;
|
644 |
|
|
default : output += " ?";
|
645 |
|
|
}
|
646 |
|
|
output += parse_msg(entry.msg, entry);
|
647 |
|
|
switch( entry.what) {
|
648 |
|
|
case cyg_trace_trace : output += "' "; break;
|
649 |
|
|
case cyg_trace_enter : break;
|
650 |
|
|
case cyg_trace_args : output += "))"; break;
|
651 |
|
|
case cyg_trace_return : break;
|
652 |
|
|
default : output += "? ";
|
653 |
|
|
}
|
654 |
|
|
}
|
655 |
|
|
output += "\n";
|
656 |
|
|
(*pOutputFn)(output.c_str());
|
657 |
|
|
}
|
658 |
|
|
|
659 |
|
|
static void
|
660 |
|
|
trace_callback( void (*pOutputFn)(const char*))
|
661 |
|
|
{
|
662 |
|
|
if ((trace_index < 0) || (trace_index >= CYGNUM_INFRA_TRACE_VECTOR_SIZE))
|
663 |
|
|
return;
|
664 |
|
|
|
665 |
|
|
// Start at the last entry and work back down to zero, skipping
|
666 |
|
|
// invalid ones. Then go to the top and work back to the current index.
|
667 |
|
|
int i;
|
668 |
|
|
for (i = trace_index - 1; i >= 0; i--) {
|
669 |
|
|
if (tracevec[i].valid) {
|
670 |
|
|
output_entry(pOutputFn, tracevec[i]);
|
671 |
|
|
}
|
672 |
|
|
}
|
673 |
|
|
for (i = (CYGNUM_INFRA_TRACE_VECTOR_SIZE - 1); i >= trace_index; i--) {
|
674 |
|
|
if (tracevec[i].valid) {
|
675 |
|
|
output_entry(pOutputFn, tracevec[i]);
|
676 |
|
|
}
|
677 |
|
|
}
|
678 |
|
|
}
|
679 |
|
|
|
680 |
|
|
//}}}
|