1/*
2 * Copyright (C) 1995 Advanced RISC Machines Limited. All rights reserved.
3 *
4 * This software may be freely used, copied, modified, and distributed
5 * provided that the above copyright notice is preserved in all copies of the
6 * software.
7 */
8
9/* -*-C-*-
10 *
11 * $Revision: 1.3 $
12 *     $Date: 2004/12/27 14:00:54 $
13 *
14 *
15 * logging.c - methods for logging warnings, errors and trace info
16 *
17 */
18
19#include <stdarg.h>     /* ANSI varargs support */
20
21#ifdef TARGET
22# include "angel.h"
23# include "devconf.h"
24#else
25# include "host.h"
26#endif
27
28#include "logging.h"    /* Header file for this source code */
29
30#ifndef UNUSED
31# define UNUSED(x) ((x)=(x))
32#endif
33
34/*
35 * __rt_warning
36 * ------------
37 * This routine is provided as a standard method of generating
38 * run-time system warnings. The actual action taken by this code can
39 * be board or target application specific, e.g. internal logging,
40 * debug message, etc.
41 */
42
43#ifdef DEBUG
44
45# ifdef DEBUG_METHOD
46
47#  define  STRINGIFY2(x) #x
48#  define  STRINGIFY(x)  STRINGIFY2(x)
49#  define  DEBUG_METHOD_HEADER        STRINGIFY(DEBUG_METHOD##.h)
50
51#  include DEBUG_METHOD_HEADER
52
53#  define  METHOD_EXPAND_2(m, p, c) m##p(c)
54#  define  METHOD_EXPAND(m, p, c)   METHOD_EXPAND_2(m, p, c)
55
56#  define  CHAROUT(c)    METHOD_EXPAND(DEBUG_METHOD, _PutChar,  (c))
57#  define  PRE_DEBUG(l)  METHOD_EXPAND(DEBUG_METHOD, _PreWarn,  (l))
58#  define  POST_DEBUG(n) METHOD_EXPAND(DEBUG_METHOD, _PostWarn, (n))
59
60# else
61#  error Must define DEBUG_METHOD
62# endif
63
64#endif /* def DEBUG */
65
66/*
67 * the guts of __rt_warning
68 */
69
70#pragma no_check_stack
71#ifdef DEBUG
72
73static const char hextab[] = "0123456789ABCDEF";
74
75/*
76 * If debugging, then we break va_warn into sub-functions which
77 * allow us to get an easy breakpoint on the formatted string
78 */
79static int va_warn0(char *format, va_list args)
80{
81    int len = 0;
82
83    while ((format != NULL) && (*format != '\0'))
84    {
85        if (*format == '%')
86        {
87            char fch = *(++format); /* get format character (skipping '%') */
88            int ival; /* holder for integer arguments */
89            char *string; /* holder for string arguments */
90            int width = 0; /* No field width by default */
91            int padzero = FALSE; /* By default we pad with spaces */
92
93            /*
94             * Check if the format has a width specified. NOTE: We do
95             * not use the "isdigit" function here, since it will
96             * require run-time support. The current ARM Ltd header
97             * defines "isdigit" as a macro, that uses a fixed
98             * character description table.
99             */
100            if ((fch >= '0') && (fch <= '9'))
101            {
102                if (fch == '0')
103                {
104                    /* Leading zeroes padding */
105                    padzero = TRUE;
106                    fch = *(++format);
107                }
108
109                while ((fch >= '0') && (fch <= '9'))
110                {
111                    width = ((width * 10) + (fch - '0'));
112                    fch = *(++format);
113                }
114            }
115
116            if (fch == 'l')
117                /* skip 'l' in "%lx", etc. */
118                fch = *(++format);
119
120            switch (fch)
121            {
122              case 'c':
123                  /* char */
124                  ival = va_arg(args, int);
125                  CHAROUT((char)ival);
126                  len++;
127                  break;
128
129              case 'x':
130              case 'X':
131              {
132                  /* hexadecimal */
133                  unsigned int uval = va_arg(args, unsigned int);
134                  int loop;
135
136                  UNUSED(uval);
137
138                  if ((width == 0) || (width > 8))
139                      width = 8;
140
141                  for(loop = (width * 4); (loop != 0); loop -= 4)
142                  {
143                      CHAROUT(hextab[(uval >> (loop - 4)) & 0xF]);
144                      len++;
145                  }
146              }
147
148              break;
149
150              case 'd':
151                  /* decimal */
152                  ival = va_arg(args, int);
153
154                  if (ival < 0)
155                  {
156                      ival = -ival;
157                      CHAROUT('-');
158                      len++;
159                  }
160
161                  if (ival == 0)
162                  {
163                      CHAROUT('0');
164                      len++;
165                  }
166                  else
167                  {
168                      /*
169                       * The simplest method of displaying numbers is
170                       * to provide a small recursive routine, that
171                       * nests until the most-significant digit is
172                       * reached, and then falls back out displaying
173                       * individual digits. However, we want to avoid
174                       * using recursive code within the lo-level
175                       * parts of Angel (to minimise the stack
176                       * usage). The following number conversion is a
177                       * non-recursive solution.
178                       */
179                      char buffer[16]; /* stack space used to hold number */
180                      int count = 0; /* pointer into buffer */
181
182                      /*
183                       * Place the conversion into the buffer in
184                       * reverse order:
185                       */
186                      while (ival != 0)
187                      {
188                          buffer[count++] = ('0' + ((unsigned int)ival % 10));
189                          ival = ((unsigned int)ival / 10);
190                      }
191
192                      /*
193                       * Check if we are placing the data in a
194                       * fixed width field:
195                       */
196                      if (width != 0)
197                      {
198                          width -= count;
199
200                          for (; (width != 0); width--)
201                          {
202                              CHAROUT(padzero ? '0': ' ');
203                              len++;
204                          }
205                      }
206
207                      /* then display the buffer in reverse order */
208                      for (; (count != 0); count--)
209                      {
210                          CHAROUT(buffer[count - 1]);
211                          len++;
212                      }
213                  }
214
215                  break;
216
217              case 's':
218                  /* string */
219                  string = va_arg(args, char *);
220
221                  /* we only need this test once */
222                  if (string != NULL)
223                      /* whilst we check this for every character */
224                      while (*string)
225                      {
226                          CHAROUT(*string);
227                          len++;
228                          string++;
229
230                          /*
231                           * NOTE: We do not use "*string++" as the macro
232                           * parameter, since we do not know how many times
233                           *the parameter may be expanded within the macro.
234                           */
235                      }
236
237                  break;
238
239              case '\0':
240                  /*
241                   * string terminated by '%' character, bodge things
242                   * to prepare for default "format++" below
243                   */
244                  format--;
245
246                  break;
247
248              default:
249                  /* just display the character */
250                  CHAROUT(*format);
251                  len++;
252
253                  break;
254            }
255
256            format++; /* step over format character */
257        }
258        else
259        {
260            CHAROUT(*format);
261            len++;
262            format++;
263        }
264    }
265    return len;
266}
267
268/*
269 * this routine is simply here as a good breakpoint for dumping msg -
270 * can be used by DEBUG_METHOD macros or functions, if required.
271 */
272# ifdef DEBUG_NEED_VA_WARN1
273static void va_warn1(int len, char *msg)
274{
275    UNUSED(len); UNUSED(msg);
276}
277# endif
278
279void va_warn(WarnLevel level, char *format, va_list args)
280{
281    int len;
282
283    if ( PRE_DEBUG( level ) )
284    {
285        len = va_warn0(format, args);
286        POST_DEBUG( len );
287    }
288}
289
290#else /* ndef DEBUG */
291
292void va_warn(WarnLevel level, char *format, va_list args)
293{
294    UNUSED(level); UNUSED(format); UNUSED(args);
295}
296
297#endif /* ... else ndef(DEBUG) ... */
298#pragma check_stack
299
300#pragma no_check_stack
301void __rt_warning(char *format, ...)
302{
303    va_list args;
304
305    /*
306     * For a multi-threaded system we should provide a lock at this point
307     * to ensure that the warning messages are sequenced properly.
308     */
309
310    va_start(args, format);
311    va_warn(WL_WARN, format, args);
312    va_end(args);
313
314    return;
315}
316#pragma check_stack
317
318#ifdef TARGET
319
320#pragma no_check_stack
321void __rt_uninterruptable_loop( void ); /* in suppasm.s */
322
323void __rt_error(char *format, ...)
324{
325    va_list args;
326
327    va_start(args, format);
328
329    /* Display warning message */
330    va_warn(WL_ERROR, format, args);
331
332    __rt_uninterruptable_loop();
333
334    va_end(args);
335    return;
336}
337#pragma check_stack
338
339#endif /* def TARGET */
340
341#ifdef DO_TRACE
342
343static bool trace_on = FALSE; /* must be set true in debugger if req'd */
344
345#pragma no_check_stack
346void __rt_trace(char *format, ...)
347{
348    va_list args;
349
350    /*
351     * For a multi-threaded system we should provide a lock at this point
352     * to ensure that the warning messages are sequenced properly.
353     */
354
355    if (trace_on)
356    {
357        va_start(args, format);
358        va_warn(WL_TRACE, format, args);
359        va_end(args);
360    }
361
362    return;
363}
364#pragma check_stack
365
366#endif /* def DO_TRACE */
367
368
369/* EOF logging.c */
370