IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ticket #524: psTrace.c

File psTrace.c, 24.2 KB (added by eugene, 21 years ago)

psTrace.c fixes

Line 
1/** @file psTrace.c
2 * \brief basic run-time trace facilities
3 * \ingroup LogTrace
4 *
5 * This file will hold the code for procedures to insert
6 * trace messages into the code.
7 *
8 * @author Robert Lupton, Princeton University
9 * @author GLG, MHPCC
10 *
11 * @version $Revision: 1.58 $ $Name: $
12 * @date $Date: 2005/09/08 00:27:50 $
13 *
14 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
15 */
16
17/*****************************************************************************
18 NOTES:
19 In the SRD, higher trace levels correspond to a numerically lower trace
20 value in the code. This is a bit confusing. For example, a high-level
21 message might be something like "Begin Processing". The module programmer
22 might give that a numerically low trace level, such as 1, so then any
23 non-zero trace level in that code component will display thatmessage.
24
25 We build a tree of trace components. Every node in the tree has a
26 depth, which is it's distance from the root. However, this is not
27 not the same thing as a node's "level", which corresponds to the
28 trace level of that node.
29
30I think the following is the correct behavior, but not sure:
31 PS_UNKNOWN_TRACE_LEVEL: We never set the level of a component to this
32 value. This value is only used when psTraceGetLevel is called with
33 a bad component name, or if the component root is undefined, I think.
34
35 PS_DEFAULT_TRACE_LEVEL: This should only be used when adding the
36 intermediate components of a psS64 name. Ie. the "B" in .A.B.C
37
38 *****************************************************************************/
39
40#ifndef PS_NO_TRACE
41
42#include <unistd.h>
43#include <stdlib.h>
44#include <stdio.h>
45#include <string.h>
46#include <stdarg.h>
47#include "psMemory.h"
48#include "psTrace.h"
49#include "psString.h"
50#include "psError.h"
51#include "psLogMsg.h"
52
53#include "psErrorText.h"
54
55static p_psComponent* cRoot = NULL; // The root of the trace component
56// static FILE *traceFP = NULL; // File destination for messages.
57static psBool traceTime = false; // Flag to include time info
58static psBool traceHost = false; // Flag to include host info
59static psBool traceLevel = false; // Flag to include level info
60static psBool traceName = false; // Flag to include name info
61static psBool traceMsg = true; // Flag to include message info
62static int traceFD = STDOUT_FILENO; // default value
63
64static void componentFree(p_psComponent* comp);
65static p_psComponent* componentAlloc(const char *name, int level);
66
67/*****************************************************************************
68componentAlloc(): allocate memory for a new node, and initialize members.
69 *****************************************************************************/
70static p_psComponent* componentAlloc(const char *name, int level)
71{
72 p_psComponent* comp = psAlloc(sizeof(p_psComponent));
73
74 p_psMemSetPersistent(comp,true);
75 psMemSetDeallocator(comp, (psFreeFunc) componentFree);
76 comp->name = psStringCopy(name);
77 p_psMemSetPersistent((psPtr)comp->name,true);
78 comp->level = level;
79 comp->n = 0;
80 comp->p_psSpecified = false;
81 comp->subcomp = NULL;
82 return comp;
83}
84
85/*****************************************************************************
86componentFree(): free the current node in the root tree, and all children
87nodes as well.
88 *****************************************************************************/
89static void componentFree(p_psComponent* comp)
90{
91 if (comp == NULL) {
92 return;
93 }
94
95 if (comp->subcomp != NULL) {
96 for (psS32 i = 0; i < comp->n; i++) {
97 p_psMemSetPersistent(comp->subcomp[i],false);
98 psFree(comp->subcomp[i]);
99 }
100 p_psMemSetPersistent(comp->subcomp,false);
101 psFree(comp->subcomp);
102 }
103
104 p_psMemSetPersistent((psPtr)comp->name,false);
105 psFree(comp->name);
106}
107
108/*****************************************************************************
109initTrace(): simply initialize the component root tree.
110*****************************************************************************/
111static void initTrace(void)
112{
113 if (cRoot == NULL) {
114 cRoot = componentAlloc(".", PS_DEFAULT_TRACE_LEVEL);
115 }
116}
117
118/*****************************************************************************
119Set all trace levels to zero.
120
121XXX: Currently, no function calls this routine.
122 *****************************************************************************/
123void p_psTraceReset(p_psComponent* currentNode)
124{
125 psS32 i = 0;
126
127 if (NULL == currentNode) {
128 return;
129 }
130
131 currentNode->level = 0;
132 for (i = 0; i < currentNode->n; i++) {
133 if (NULL == currentNode->subcomp[i]) {
134 psLogMsg("p_psTraceReset", PS_LOG_WARN,
135 PS_ERRORTEXT_psTrace_NULL_SUBCOMPONENT,
136 i, currentNode->name);
137 } else {
138 p_psTraceReset(currentNode->subcomp[i]);
139 }
140 }
141 return;
142}
143
144/*****************************************************************************
145Set all trace levels to zero.
146 *****************************************************************************/
147void psTraceReset()
148{
149 psFree(cRoot);
150 cRoot = NULL;
151}
152
153/*****************************************************************************
154componentAdd(): Adds the component named "addNodeName" to the root tree.
155 *****************************************************************************/
156static psBool componentAdd(const char *addNodeName, psS32 level)
157{
158 psS32 i = 0; // Loop index variable.
159 char name[strlen(addNodeName) + 1]; // buffer for writeable copy.
160 char *pname = name;
161 char *firstComponent = NULL; // first component of name
162 p_psComponent* currentNode = cRoot;
163 psS32 nodeExists = 0;
164
165 // XXX: Verify that this is the correct behavior.
166 if (strcmp("", addNodeName) == 0) {
167 psError(PS_ERR_BAD_PARAMETER_NULL,true,
168 PS_ERRORTEXT_psTrace_ADD_NULL_COMPONENT);
169 return false;
170 }
171
172 // Is this the root node? If so, simply set level and return.
173 if (strcmp(".", addNodeName) == 0) {
174 cRoot->level = level;
175 return true;
176 }
177
178 if (addNodeName[0] != '.') {
179 psError(PS_ERR_BAD_PARAMETER_VALUE,true,
180 PS_ERRORTEXT_psTrace_MALFORMED_COMPONENT_NAME,
181 addNodeName);
182 return false;
183 }
184
185 strcpy(name, addNodeName);
186 pname = name+1;
187 // Iterate through the components of addNodeName. Strip off the first
188 // component of the name, find that in the root tree, or add it if it
189 // does not exist, then move to the next component in the name.
190
191 while (pname != NULL) {
192 firstComponent = pname;
193 pname = strchr(firstComponent, '.');
194 if (pname != NULL) {
195 *pname = '\0';
196 pname++;
197 }
198 nodeExists = 0;
199 for (i = 0; i < currentNode->n; i++) {
200 if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
201 currentNode = currentNode->subcomp[i];
202 nodeExists = 1;
203 if (pname == NULL) {
204 currentNode->level = level;
205 }
206 }
207 }
208
209 if (nodeExists == 0) {
210 currentNode->subcomp = psRealloc(currentNode->subcomp,
211 (currentNode->n + 1) * sizeof(p_psComponent* ));
212 p_psMemSetPersistent(currentNode->subcomp,true);
213
214 currentNode->n = (currentNode->n) + 1;
215
216 if (pname == NULL) {
217 // This is the final component to add.
218 currentNode->subcomp[(currentNode->n) - 1] = componentAlloc(firstComponent, level);
219 } else {
220 // We are adding an intermediate component. The trace level
221 // is not defined. An undefined trace level inherits the
222 // trace level of it's parent. However, we do not set that
223 // specifically here since that would inheritance to be a
224 // static, one-time, type of behavior.
225
226 currentNode->subcomp[(currentNode->n) - 1] = componentAlloc(firstComponent, PS_DEFAULT_TRACE_LEVEL);
227 }
228 currentNode = currentNode->subcomp[(currentNode->n) - 1];
229 }
230 }
231
232 return true;
233}
234
235/*****************************************************************************
236 psSetTraceLevel(): add the component named "comp" to the component tree,
237 if it is not already there, and set it's trace level to "level".
238
239 NOTE: We modified this so that the user may omit the leading "," in a
240 component name. Since the code was already implemented assuming the "."
241 was required, rather than change all that code, in this function, I
242 simply add a leading "." to the component name if there is none.
243
244 Input:
245 comp
246 level
247 Output:
248 none
249 Returns:
250 zero
251*****************************************************************************/
252int psTraceSetLevel(const char *comp, // component of interest
253 int level) // desired trace level
254{
255 char *compName = NULL;
256 int prevLevel = -1;
257
258 // If the root component tree does not exist, then initialize it.
259 if (cRoot == NULL) {
260 initTrace();
261 }
262
263 // If the component name has no leading dot, then supply it.
264 if (comp[0] != '.') {
265 compName = (char *) psAlloc(10 + strlen(comp));
266 strcpy(compName, ".");
267 compName = strcat(compName, comp);
268 } else {
269 compName = (char *) comp;
270 }
271 prevLevel = psTraceGetLevel(compName);
272 // Add the new component to the component tree.
273 if ( !componentAdd(compName, level) ) {
274 psError(PS_ERR_UNKNOWN, false,
275 PS_ERRORTEXT_psTrace_FAILED_TO_ADD_COMPONENT,
276 level,
277 compName);
278
279 if (comp[0] != '.') {
280 psFree(compName);
281 }
282 // return false;
283 return -1;
284 }
285
286 if (comp[0] != '.') {
287 psFree(compName);
288 }
289
290 // return true;
291 return prevLevel;
292}
293
294/*****************************************************************************
295 doGetTraceLevel()
296 This function recursively searches the root component tree for the
297 component named "name", which is supplied by a parameter. If it
298 finds that component, it returns the level of that component.
299 Otherwise, it returns ???.
300
301 NOTE: We modified this so that the user may omit the leading "," in a
302 component name. Since the code was already implemented assuming the "."
303 was required, rather than change all that code, in this function, I
304 simply add a leading "." to the component name if there is none.
305
306 Inputs:
307 name:
308 Outputs:
309 none
310 Returns:
311 The trace level of the "name" component.
312 *****************************************************************************/
313static psS32 doGetTraceLevel(const char *aname)
314{
315 char name[strlen(aname) + 1]; // need a writeable copy: for strsep()
316 char *pname = name;
317 char *firstComponent = NULL; // first component of name
318 p_psComponent* currentNode = cRoot;
319 psS32 i = 0;
320 psS32 defaultLevel = 0;
321
322 if (NULL == currentNode) {
323 return (PS_UNKNOWN_TRACE_LEVEL);
324 }
325
326 if (strcmp(".", aname) == 0) {
327 return (cRoot->level);
328 }
329
330 if (aname[0] != '.') {
331 return (PS_UNKNOWN_TRACE_LEVEL);
332 }
333
334 defaultLevel = cRoot->level;
335 strcpy(name, aname);
336 pname = name+1;
337 while (pname != NULL) {
338 firstComponent = pname;
339 pname = strchr(firstComponent, '.');
340 if (pname != NULL) {
341 *pname = '\0';
342 pname++;
343 }
344 for (i = 0; i < currentNode->n; i++) {
345 if (NULL == currentNode->subcomp[i]) {
346 psLogMsg("p_psTraceReset", PS_LOG_WARN,
347 PS_ERRORTEXT_psTrace_NULL_SUBCOMPONENT,
348 i, currentNode->name);
349 }
350
351 if (strcmp(currentNode->subcomp[i]->name, firstComponent) == 0) {
352 currentNode = currentNode->subcomp[i];
353 // For level inheritance purpose, we save the level of this
354 // component if it is not DEFAULT.
355 if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
356 defaultLevel = currentNode->level;
357 }
358 // Determine if this is the last component:
359 if (pname == NULL) {
360 if (currentNode->level != PS_DEFAULT_TRACE_LEVEL) {
361 return (currentNode->level);
362 } else {
363 return(defaultLevel);
364 }
365 }
366 }
367 }
368 }
369 return(defaultLevel);
370}
371
372/*****************************************************************************
373 psTraceLevelGet()
374 Return a trace level of "name" in the root component tree. If the
375 exact string of components in "name" does not exist in the root
376 tree, we return the deepest level of the match.
377 Input:
378 name
379 Output:
380 none
381 Return:
382 The level of "name" in the root component tree.
383 *****************************************************************************/
384int psTraceGetLevel(const char *name)
385{
386 char *compName = NULL;
387 psS32 traceLevel;
388
389 if (cRoot == NULL) {
390 return (PS_UNKNOWN_TRACE_LEVEL);
391 }
392
393 // If the component name has no leading dot, then supply it.
394 if (name[0] != '.') {
395 compName = (char *) psAlloc(10 + strlen(name));
396 strcpy(compName, ".");
397 compName = strcat(compName, name);
398 traceLevel = doGetTraceLevel(compName);
399 psFree(compName);
400 } else {
401 // Search the component root tree, determine the trace level.
402 traceLevel = doGetTraceLevel(name);
403 }
404
405 // XXX: The default trace level is currently set at -1, which is not a
406 // valid trace level. This is convenient in determining whether or not
407 // a component should inherit the trace level from parent nodes. However,
408 // it's not clear that -1 should ever be returned by this function.
409 // The SDR is unclear on this point and we should probably request IfA
410 // comment.
411 if (traceLevel == PS_DEFAULT_TRACE_LEVEL) {
412 traceLevel = PS_THE_OTHER_DEFAULT_TRACE_LEVEL;
413 }
414
415 return(traceLevel);
416}
417
418/*****************************************************************************
419 doPrintTraceLevels()
420 This function recursively searches the component tree supplied by the
421 parameter "comp" and prints the name and level of each component.
422 Inputs:
423 comp: a node in the component tree.
424 level: the level of that node
425 Outputs:
426 none
427 Returns:
428 null
429 *****************************************************************************/
430static void doPrintTraceLevels(const p_psComponent* comp,
431 psS32 depth,
432 psS32 defLevel)
433{
434 char line[1024];
435 psS32 i = 0;
436
437 // XXX EAM : probably should just return if traceFD < 1
438 if (traceFD < 1) return;
439
440 if (comp->name[0] == '\0') {
441 return;
442 } else {
443 if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
444 // fprintf(traceFP,"%*s%-*s %d\n", depth, "", 20 - depth, comp->name, defLevel);
445 sprintf(line,"%*s%-*s %d\n", depth, "", 20 - depth, comp->name, defLevel);
446 write (traceFD, line, strlen(line));
447 } else {
448 // fprintf(traceFP, "%*s%-*s %d\n", depth, "", 20 - depth, comp->name, comp->level);
449 sprintf(line, "%*s%-*s %d\n", depth, "", 20 - depth, comp->name, comp->level);
450 write (traceFD, line, strlen(line));
451 }
452 }
453
454 for (i = 0; i < comp->n; i++) {
455 if (comp->level == PS_DEFAULT_TRACE_LEVEL) {
456 doPrintTraceLevels(comp->subcomp[i], depth + 1, defLevel);
457 } else {
458 doPrintTraceLevels(comp->subcomp[i], depth + 1, comp->level);
459 }
460 }
461}
462
463
464/*****************************************************************************
465psPrintTraceLevels(): Simply print all the trace levels in the trace level
466component tree.
467Inputs:
468 none
469Outputs:
470 none
471Returns:
472 null
473*****************************************************************************/
474void psTracePrintLevels(void)
475{
476 if (cRoot == NULL) {
477 return;
478 }
479
480 doPrintTraceLevels(cRoot, 0, PS_THE_OTHER_DEFAULT_TRACE_LEVEL);
481}
482
483void psTraceV(const char *comp,
484 int level,
485 const char *format,
486 va_list ap)
487{
488 // char *fmt = NULL;
489 // va_list ap;
490 static psS32 first = 1; // Flag for calling gethostname()
491 static char hostname[256 + 1];
492 char clevel = 0; // letter-name for level
493 psS32 i = 0;
494 char head[256 + 2]; // the added two are for the ending | and \0
495 char *head_ptr = head; // where we've got to in head
496 psS32 maxLength = 256;
497 time_t clock = time(NULL); // The current time.
498 struct tm *utc = gmtime(&clock); // The current gm time.
499
500 // XXX EAM : fd < 1 does not print messages
501 if (traceFD < 1) return;
502
503 if (NULL == comp) {
504 psError(PS_ERR_BAD_PARAMETER_NULL, true,
505 PS_ERRORTEXT_psTrace_NULL_TRACETREE,
506 __func__);
507 return;
508 }
509
510 if (first) {
511 first = 0;
512 gethostname(hostname, 256);
513 }
514 switch (level) {
515 case PS_LOG_ABORT:
516 clevel = 'A';
517 break;
518
519 case PS_LOG_ERROR:
520 clevel = 'E';
521 break;
522
523 case PS_LOG_WARN:
524 clevel = 'W';
525 break;
526
527 case PS_LOG_INFO:
528 clevel = 'I';
529 break;
530
531 case 4:
532 case 5:
533 case 6:
534 case 7:
535 case 8:
536 case 9:
537 clevel = level + '0';
538 break;
539
540 default:
541 psTrace("utils.logMsg", 2, "Invalid logMsg level: %d (%s)\n", level, format);
542 level = (level < 0) ? 0 : 9;
543 clevel = level + '0';
544 break;
545 }
546
547 // Only display this message if it's trace level is less than the level
548 // of it's associatedcomponent.
549 if (level <= psTraceGetLevel(comp)) {
550 // va_start(ap, format);
551
552 // Create the various log fields...
553 if (traceTime) {
554 maxLength -= snprintf(head_ptr, maxLength, "%4d:%02d:%02d %02d:%02d:%02dZ",
555 utc->tm_year + 1900, utc->tm_mon + 1, utc->tm_mday,
556 utc->tm_hour, utc->tm_min, utc->tm_sec) - 1;
557 head_ptr += strlen(head_ptr);
558 }
559 // Hostname should be 20 characters.
560 if (traceHost) {
561 if (head_ptr > head) {
562 *head_ptr++ = '|';
563 }
564 maxLength -= snprintf(head_ptr, maxLength, "%-20s", hostname);
565 head_ptr += strlen(head_ptr);
566 }
567 if (traceLevel) {
568 if (head_ptr > head) {
569 *head_ptr++ = '|';
570 }
571 maxLength -= snprintf(head_ptr, maxLength, "%c", clevel);
572 head_ptr += strlen(head_ptr);
573 }
574 if (traceName) {
575 if (head_ptr > head) {
576 *head_ptr++ = '|';
577 }
578 maxLength -= snprintf(head_ptr, maxLength, "%s", comp);
579
580 head_ptr += strlen(head_ptr);
581 }
582
583 if (head_ptr > head) {
584 *head_ptr++ = '\n';
585 } else if (!traceMsg) { // no output desired
586 return;
587 }
588 *head_ptr = '\0';
589
590
591 // fputs(head, traceFP);
592 write (traceFD, head, strlen(head));
593
594 if (traceMsg) {
595 char line[1024];
596
597 // The following functions get the variable list of parameters with
598 // which this function was called, and print them to the standard
599 // output.
600 // fmt = va_arg(ap, char *);
601
602 // We indent each message one space for each level of the message.
603 for (i = 0; i < level; i++) {
604 // fprintf(traceFP, " ");
605 // fprintf(traceFP, "%s", format);
606 // sprintf(line, "%s", format);
607 write (traceFD, " ", 1);
608 }
609 // vfprintf(traceFP, fmt, ap);
610 vsprintf(line, format, ap);
611 write (traceFD, line, strlen(line));
612
613 // vfprintf(traceFP, format, ap);
614 // va_end(ap);
615 /* char msg[1024];
616 char* msgPtr;
617 vsnprintf(msg,1024, format, ap); // create message
618
619 // detect multiple lines in message and indent each line by 4 spaces.
620 char* line = strtok_r(msg,"\n",&msgPtr);
621 while (line != NULL) {
622 fprintf(logDest," %s\n",line);
623 line = strtok_r(NULL,"\n",&msgPtr);
624 }
625 */
626 } else {
627 // fputc('\n', traceFP);
628 write(traceFD, "\n", 1);
629 }
630
631
632 }
633
634}
635
636/*****************************************************************************
637p_psTrace(): we display the trace message to standard output if the trace
638level of that message, supplied by the parameter "level" is higher than the
639trace level that is currently associated with the component named by the
640parameter "comp".
641Input:
642 comp
643 level
644 ... a printf-style output string.
645Output:
646 none
647Return:
648 null
649 *****************************************************************************/
650void p_psTrace(const char *comp, // component being traced
651 int level, // desired trace level
652 const char *format,
653 ...) // arguments
654{
655 va_list ap;
656 va_start(ap, format);
657 psTraceV(comp, level, format, ap);
658 va_end(ap);
659 // fflush(traceFP);
660 // XXX EAM : what is fd-appropriate equivalent?
661}
662
663// XXX EAM : I've added code to close the old traceFP (safely)
664void psTraceSetDestination(int fd)
665{
666
667
668 /*
669 bool special;
670
671 // XXX EAM perhaps return an error?
672 if (fp == NULL) {
673 return;
674 }
675
676 // cannot close traceFP if one of the special FILE ptrs
677 special = (traceFP == NULL);
678 special |= (traceFP == stdin);
679 special |= (traceFP == stdout);
680 special |= (traceFP == stderr);
681
682 if (!special) {
683 fclose (traceFP);
684 }
685 traceFP = fp;
686 */
687 if (fd < 1) {
688 traceFD = 0;
689 return;
690 }
691 if (fd == 1) {
692 traceFD = STDOUT_FILENO;
693 } else if (fd == 2) {
694 traceFD = STDERR_FILENO;;
695 } else if (fd > 2) {
696 close (traceFD);
697 traceFD = fd;
698 }
699}
700
701int psTraceGetDestination()
702{
703 return traceFD;
704}
705
706/*****************************************************************************
707psTraceSetFormat(): Set the format of psTrace output. More precisely,
708 provide a string consisting of the letters {H (host), L (level), M
709 (message), N (name), T (time)}. The default is "HLMNT". This string
710 determines whether or not they associated type of information will be
711 included in message traces. It does not determine the order in which that
712 information will appear (that order is fixed).
713
714Input:
715 fmt: a string specifying the format.
716Return:
717 NULL.
718 *****************************************************************************/
719bool psTraceSetFormat(const char *format)
720{
721 // assume none.
722 traceHost = false;
723 traceLevel = false;
724 traceMsg = false;
725 traceName = false;
726 traceTime = false;
727
728 // if fmt is NULL, no logging is desired.
729 if (format == NULL) {
730 return false;
731 }
732
733 // XXX: What is the purpose of this conditional.
734 if (strlen(format) == 0) {
735 format = "THLNM";
736 }
737 // Step through each character in the format string. For each letter
738 // in that string, set the appropriate logging.
739
740 for (const char *ptr = format; *ptr != '\0'; ptr++) {
741 switch (*ptr) {
742 case 'H':
743 case 'h':
744 traceHost = true;
745 break;
746 case 'L':
747 case 'l':
748 traceLevel = true;
749 break;
750 case 'M':
751 case 'm':
752 traceMsg = true;
753 break;
754 case 'N':
755 case 'n':
756 traceName = true;
757 break;
758 case 'T':
759 case 't':
760 traceTime = true;
761 break;
762 default:
763 psError(PS_ERR_BAD_PARAMETER_VALUE, true,
764 PS_ERRORTEXT_psLogMsg_UNKNOWN_KEY, *ptr);
765 return false;
766 }
767 }
768
769 // XXX: If one must at least log error messages, why don't we set logMsg = true here?
770 if (!traceMsg) {
771 psTrace("utils.traceMsg", 1, "You must at least trace error messages (You chose \"%s\")", format);
772
773 }
774 return true;
775}
776
777
778
779#endif // #ifndef PS_NO_TRACE