1 /*
2 * Copyright (c) Tony Bybell 1999-2016.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 */
9
10 /* AIX may need this for alloca to work */
11 #if defined _AIX
12 #pragma alloca
13 #endif
14
15 #include "globals.h"
16 #include <config.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include "symbol.h"
21 #include "lxt.h"
22 #include "debug.h"
23 #include "bsearch.h"
24 #include "strace.h"
25 #include "translate.h"
26 #include "ptranslate.h"
27 #include "ttranslate.h"
28 #include "hierpack.h"
29 #include "analyzer.h"
30
31 #ifdef _MSC_VER
32 #define strcasecmp _stricmp
33 #endif
34
35 void UpdateTraceSelection(Trptr t);
36 int traverse_vector_nodes(Trptr t);
37
38 /*
39 * extract last n levels of hierarchy
40 */
hier_extract(char * pnt,int levels)41 char *hier_extract(char *pnt, int levels)
42 {
43 int i, len;
44 char ch, *pnt2, *esc;
45 char only_nums_so_far=1;
46
47 if(!pnt) return(NULL);
48
49 len=strlen(pnt);
50 if(!len) return(pnt);
51
52 if(levels<1) levels=1;
53 if((!GLOBALS->hier_ignore_escapes) &&(esc=strchr(pnt, '\\')))
54 {
55 return((levels==1) ? esc : pnt); /* shortcut out on escape IDs: level=1, esc char else all */
56 }
57
58 pnt2=pnt+len-1;
59 /* ch=*pnt2; */ /* scan-build : useless given loop below */
60
61 for(i=0;i<len;i++)
62 {
63 ch=*(pnt2--);
64 if((only_nums_so_far)&&(ch>='0')&&(ch<='9')) /* skip 1st set of signal.number hier from right if it exists */
65 {
66 continue;
67 /* nothing */
68 }
69 else
70 {
71 if(ch==GLOBALS->hier_delimeter)
72 {
73 if(!only_nums_so_far) levels--;
74 if(!levels)
75 {
76 pnt2+=2;
77 return(pnt2);
78 }
79 }
80 only_nums_so_far=0;
81 }
82 }
83
84 return(pnt); /* not as many levels as max, so give the full name.. */
85 }
86
updateTraceGroup(Trptr t)87 void updateTraceGroup(Trptr t)
88 {
89 /* t->t_match = NULL; */
90
91 if (t->t_prev)
92 {
93 if (IsGroupBegin(t->t_prev))
94 {
95 if (IsGroupEnd(t))
96 { /* empty group */
97 Trptr g_begin = t->t_prev;
98 t->t_grp = g_begin->t_grp;
99 t->t_match = g_begin;
100 g_begin->t_match = t;
101 }
102 else
103 { /* first trace in group */
104 t->t_grp = t->t_prev;
105 }
106 }
107 else
108 {
109 if (IsGroupEnd(t))
110 {
111 Trptr g_begin = t->t_prev->t_grp;
112 if(g_begin) /* scan-build */
113 {
114 t->t_grp = g_begin->t_grp;
115 t->t_match = g_begin;
116 g_begin->t_match = t;
117 }
118 }
119 else
120 {
121 t->t_grp = t->t_prev->t_grp;
122 }
123
124
125 }
126 }
127 else
128 { /* very first trace */
129 t->t_grp = NULL;
130 }
131
132 if ((t->t_grp) && IsSelected(t->t_grp))
133 {
134 t->flags |= TR_HIGHLIGHT;
135 }
136 }
137
CloseTrace(Trptr t)138 void CloseTrace(Trptr t)
139 {
140 GLOBALS->traces.dirty = 1;
141
142 if (IsGroupBegin(t))
143 {
144 t->flags |= TR_CLOSED;
145 if (t->t_match) { t->t_match->flags |= TR_CLOSED; };
146
147 if (!HasWave(t))
148 {
149 /* Group End */
150 if (t->t_match) { t->t_match->flags |= TR_COLLAPSED; };
151 }
152 else
153 {
154 /* Composite End */
155 if (t->t_match) { t->t_match->flags |= TR_COLLAPSED; };
156 }
157 }
158
159 if (IsGroupEnd(t))
160 {
161 t->flags |= TR_CLOSED;
162 if (t->t_match) { t->t_match->flags |= TR_CLOSED; };
163
164 if ((t->t_match) && !HasWave(t->t_match))
165 {
166 /* Group End */
167 t->flags |= TR_COLLAPSED;
168 }
169 else
170 {
171 /* Composite End */
172 t->flags |= TR_COLLAPSED;
173 }
174 }
175 }
176
177
OpenTrace(Trptr t)178 void OpenTrace(Trptr t)
179 {
180 GLOBALS->traces.dirty = 1;
181
182 if (IsGroupBegin(t) || IsGroupEnd(t))
183 {
184 t->flags &= ~TR_CLOSED;
185 if (t->t_match) { t->t_match->flags &= ~TR_CLOSED; };
186
187 if (!HasWave(t))
188 {
189 t->flags &= ~TR_COLLAPSED;
190 if(t->t_match) { t->t_match->flags &= ~TR_COLLAPSED; };
191 }
192 }
193 }
194
ClearTraces(void)195 void ClearTraces(void)
196 {
197 Trptr t = GLOBALS->traces.first;
198 while(t)
199 {
200 t->flags &= ~TR_HIGHLIGHT;
201 t=t->t_next;
202 }
203 GLOBALS->traces.dirty = 1;
204 }
205
ClearGroupTraces(Trptr t_grp)206 void ClearGroupTraces(Trptr t_grp)
207 {
208 if (IsGroupBegin(t_grp))
209 {
210 Trptr t = t_grp;
211 while(t)
212 {
213 t->flags &= ~TR_HIGHLIGHT;
214 if(t->t_match == t_grp) break;
215 t=t->t_next;
216 }
217 GLOBALS->traces.dirty = 1;
218 }
219 else
220 {
221 fprintf(stderr, "INTERNAL ERROR: ClearGroupTrace applied to non-group! Exiting.\n");
222 exit(255);
223 }
224 }
225
MarkTraceCursor(Trptr t_curs)226 void MarkTraceCursor(Trptr t_curs)
227 {
228 if(t_curs)
229 {
230 Trptr t=GLOBALS->traces.first;
231
232 while(t)
233 {
234 t->is_cursor = 0;
235 t=t->t_next;
236 }
237
238 t_curs->is_cursor = 1;
239 }
240 }
241
242 /*
243 * Add a trace to the display...
244 */
AddTrace(Trptr t)245 static void AddTrace( Trptr t )
246 {
247 GLOBALS->traces.dirty = 1;
248
249 t->t_fpdecshift = GLOBALS->default_fpshift;
250
251 if((GLOBALS->which_t_color > 0) && (GLOBALS->which_t_color <= WAVE_NUM_RAINBOW))
252 {
253 t->t_color = GLOBALS->which_t_color;
254 GLOBALS->which_t_color = 0;
255 }
256
257 if(GLOBALS->default_flags&TR_NUMMASK) t->flags=GLOBALS->default_flags;
258 else t->flags=(t->flags&TR_NUMMASK)|GLOBALS->default_flags;
259
260 if(!t->vector && GLOBALS->enum_nptrs_jrb)
261 {
262 JRB enum_nptr = jrb_find_vptr(GLOBALS->enum_nptrs_jrb, t->n.nd);
263 if(enum_nptr)
264 {
265 int e_filter = enum_nptr->val.ui;
266 if((e_filter > 0) && (e_filter <= GLOBALS->num_xl_enum_filter))
267 {
268 t->e_filter = e_filter;
269 if(!(GLOBALS->default_flags&TR_NUMMASK)) t->flags = (t->flags & (~TR_NUMMASK)) | TR_ENUM | TR_BIN; /* need to downgrade to bin to make visible */
270 }
271 }
272 }
273
274 if(GLOBALS->default_flags & TR_FTRANSLATED)
275 {
276 t->f_filter = GLOBALS->current_translate_file;
277 }
278 else
279 if(GLOBALS->default_flags & TR_PTRANSLATED)
280 {
281 t->p_filter = GLOBALS->current_translate_proc;
282 }
283
284 /* NOT an else! */
285 if(GLOBALS->default_flags & TR_TTRANSLATED)
286 {
287 t->t_filter = GLOBALS->current_translate_ttrans;
288 if(t->t_filter)
289 {
290 if(!t->vector)
291 {
292 bvptr v;
293 TraceFlagsType cache_hi = t->flags & TR_HIGHLIGHT;
294
295 t->flags |= TR_HIGHLIGHT;
296 v = combine_traces(1, t); /* down: make single signal a vector */
297 if(v)
298 {
299 v->transaction_nd = t->n.nd; /* cache for savefile, disable */
300 t->vector = 1;
301 t->n.vec = v; /* splice in */
302 }
303
304 t->flags &= ~TR_HIGHLIGHT;
305 t->flags |= cache_hi;
306 }
307
308 if(GLOBALS->ttranslate_args)
309 {
310 t->transaction_args = strdup_2(GLOBALS->ttranslate_args);
311 }
312 else
313 {
314 t->transaction_args = NULL;
315 }
316
317 traverse_vector_nodes(t);
318 }
319 else
320 {
321 t->flags &= ~TR_TTRANSLATED; /* malformed filter syntax? should never have "which" of zero here */
322 }
323 }
324
325 if (IsGroupBegin(t)) {
326 GLOBALS->group_depth = GLOBALS->group_depth + 1;
327 }
328
329 if (IsGroupEnd(t)) {
330 if (GLOBALS->group_depth == 0) {
331 fprintf(stderr, "ERROR: Group End encountered with no matching start. Ignoring.\n");
332 t->flags &= ~TR_GRP_END;
333 } else {
334 GLOBALS->group_depth = GLOBALS->group_depth - 1;
335 }
336 }
337
338 if(GLOBALS->shift_timebase_default_for_add)
339 t->shift=GLOBALS->shift_timebase_default_for_add;
340
341 if(!GLOBALS->strace_ctx->shadow_active)
342 {
343 if( GLOBALS->traces.first == NULL )
344 {
345 t->t_next = t->t_prev = NULL;
346 GLOBALS->traces.first = GLOBALS->traces.last = t;
347 }
348 else
349 {
350 t->t_next = NULL;
351 t->t_prev = GLOBALS->traces.last;
352 GLOBALS->traces.last->t_next = t;
353 GLOBALS->traces.last = t;
354 }
355 GLOBALS->traces.total++;
356 updateTraceGroup(GLOBALS->traces.last);
357 }
358 else /* hide offscreen */
359 {
360 struct strace *st = calloc_2(1, sizeof(struct strace));
361 st->next = GLOBALS->strace_ctx->shadow_straces;
362 st->value = GLOBALS->strace_ctx->shadow_type;
363 st->trace = t;
364
365 st->string = GLOBALS->strace_ctx->shadow_string; /* copy string over */
366 GLOBALS->strace_ctx->shadow_string = NULL;
367
368 GLOBALS->strace_ctx->shadow_straces = st;
369 }
370 }
371
372
373 /*
374 * Add a blank trace to the display...
375 */
precondition_string(char * s)376 static char *precondition_string(char *s)
377 {
378 int len=0;
379 char *s2;
380
381 if(!s) return(NULL);
382 s2=s;
383 while((*s2)&&((*s2)!='\n')&&((*s2)!='\r')) /* strip off ending CR/LF */
384 {
385 len++;
386 s2++;
387 }
388 if(!len) return(NULL);
389 s2=(char *)calloc_2(1,len+1);
390 memcpy(s2,s,len);
391 return(s2);
392 }
393
AddBlankTrace(char * commentname)394 int AddBlankTrace(char *commentname)
395 {
396 Trptr t;
397 char *comment;
398 TraceFlagsType flags_filtered;
399
400 if( (t = (Trptr) calloc_2( 1, sizeof( TraceEnt ))) == NULL )
401 {
402 fprintf( stderr, "Out of memory, can't add blank trace to analyzer\n");
403 return( 0 );
404 }
405 AddTrace(t);
406 /* Keep only flags that make sense for a blank trace. */
407 flags_filtered = TR_BLANK | (GLOBALS->default_flags & (TR_CLOSED|
408 TR_GRP_BEGIN|
409 TR_GRP_END|
410 TR_COLLAPSED|
411 TR_ANALOG_BLANK_STRETCH));
412 t->flags = flags_filtered;
413 if(t->flags & TR_ANALOG_BLANK_STRETCH)
414
415 {
416 t->flags &= ~TR_BLANK;
417 }
418
419 if((comment=precondition_string(commentname)))
420 {
421 t->name = comment;
422 }
423
424 return(1);
425 }
426
427
428 /*
429 * Insert a blank [or comment] trace into the display...
430 */
InsertBlankTrace(char * comment,TraceFlagsType different_flags)431 int InsertBlankTrace(char *comment, TraceFlagsType different_flags)
432 {
433 TempBuffer tb;
434 char *comm;
435 Trptr t;
436
437 if( (t = (Trptr) calloc_2( 1, sizeof( TraceEnt ))) == NULL )
438 {
439 fprintf( stderr, "Out of memory, can't insert blank trace to analyzer\n");
440 return( 0 );
441 }
442
443 GLOBALS->traces.dirty = 1;
444
445 if(!different_flags)
446 {
447 t->flags=TR_BLANK;
448 }
449 else
450 {
451 t->flags = different_flags;
452 }
453
454 if((comm=precondition_string(comment)))
455 {
456 t->name = comm;
457 }
458
459 if(!GLOBALS->traces.first)
460 {
461 GLOBALS->traces.first=GLOBALS->traces.last=t;
462 GLOBALS->traces.total=1;
463 return(1);
464 }
465 else
466 {
467 tb.buffer=GLOBALS->traces.buffer;
468 tb.bufferlast=GLOBALS->traces.bufferlast;
469 tb.buffercount=GLOBALS->traces.buffercount;
470
471 GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=t;
472 GLOBALS->traces.buffercount=1;
473 PasteBuffer();
474
475 GLOBALS->traces.buffer=tb.buffer;
476 GLOBALS->traces.bufferlast=tb.bufferlast;
477 GLOBALS->traces.buffercount=tb.buffercount;
478
479 return(1);
480 }
481 }
482
483
484 /*
485 * Adds a single bit signal to the display...
486 */
AddNodeTraceReturn(nptr nd,char * aliasname,Trptr * tret)487 int AddNodeTraceReturn(nptr nd, char *aliasname, Trptr *tret)
488 {
489 Trptr t;
490 hptr histpnt;
491 hptr *harray;
492 int histcount;
493 int i;
494
495 if(!nd) return(0); /* passed it a null node ptr by mistake */
496 if(nd->mv.mvlfac) import_trace(nd);
497
498 GLOBALS->signalwindow_width_dirty=1;
499 GLOBALS->traces.dirty = 1;
500
501 if( (t = (Trptr) calloc_2( 1, sizeof( TraceEnt ))) == NULL )
502 {
503 fprintf( stderr, "Out of memory, can't add to analyzer\n" );
504 return( 0 );
505 }
506
507 if(!nd->harray) /* make quick array lookup for aet display */
508 {
509 histpnt=&(nd->head);
510 histcount=0;
511
512 while(histpnt)
513 {
514 histcount++;
515 histpnt=histpnt->next;
516 }
517
518 nd->numhist=histcount;
519
520 if(!(nd->harray=harray=(hptr *)malloc_2(histcount*sizeof(hptr))))
521 {
522 fprintf( stderr, "Out of memory, can't add to analyzer\n" );
523 free_2(t);
524 return(0);
525 }
526
527 histpnt=&(nd->head);
528 for(i=0;i<histcount;i++)
529 {
530 *harray=histpnt;
531 harray++;
532 histpnt=histpnt->next;
533 }
534 }
535
536 if(aliasname)
537 {
538 char *alias;
539
540 t->name_full = alias =(char *)malloc_2(strlen(aliasname)+1);
541 strcpy(alias,aliasname);
542 t->name = t->name_full;
543 if(GLOBALS->hier_max_level)
544 t->name = hier_extract(t->name_full, GLOBALS->hier_max_level);
545 }
546 else
547 {
548 if(!GLOBALS->hier_max_level)
549 {
550 int flagged = HIER_DEPACK_ALLOC;
551
552 t->name = hier_decompress_flagged(nd->nname, &flagged);
553 t->is_depacked = (flagged != 0);
554 }
555 else
556 {
557 int flagged = HIER_DEPACK_ALLOC;
558 char *tbuff = hier_decompress_flagged(nd->nname, &flagged);
559 if(!flagged)
560 {
561 t->name = hier_extract(nd->nname, GLOBALS->hier_max_level);
562 }
563 else
564 {
565 t->name = strdup_2(hier_extract(tbuff, GLOBALS->hier_max_level));
566 free_2(tbuff);
567 t->is_depacked = 1;
568 }
569 }
570 }
571
572 if(nd->extvals) /* expansion vectors */
573 {
574 int n;
575
576 n = nd->msi - nd->lsi;
577 if(n<0)n=-n;
578 n++;
579
580 switch(nd->vartype)
581 {
582 case ND_VCD_INTEGER:
583 case ND_VCD_PARAMETER:
584 case ND_SV_INT:
585 case ND_SV_SHORTINT:
586 case ND_SV_LONGINT: t->flags = TR_SIGNED | TR_RJUSTIFY;
587 break;
588
589 default: t->flags = (( n > 3 )||( n < -3 )) ? TR_HEX|TR_RJUSTIFY : TR_BIN|TR_RJUSTIFY;
590 break;
591 }
592 }
593 else
594 {
595 t->flags |= TR_BIN; /* binary */
596 }
597 t->vector = FALSE;
598 t->n.nd = nd;
599 if(tret) *tret = t; /* for expand */
600 AddTrace( t );
601 return( 1 );
602 }
603
604
605 /* single node */
AddNode(nptr nd,char * aliasname)606 int AddNode(nptr nd, char *aliasname)
607 {
608 return(AddNodeTraceReturn(nd, aliasname, NULL));
609 }
610
611
612 /* add multiple nodes (if array) */
AddNodeUnroll(nptr nd,char * aliasname)613 int AddNodeUnroll(nptr nd, char *aliasname)
614 {
615 #ifdef WAVE_ARRAY_SUPPORT
616 if(nd->array_height <= 1)
617 #endif
618 {
619 return(AddNodeTraceReturn(nd, aliasname, NULL));
620 }
621 #ifdef WAVE_ARRAY_SUPPORT
622 else
623 {
624 unsigned int i;
625 int rc = 1;
626
627 for(i=0;i<nd->array_height;i++)
628 {
629 rc |= AddNodeTraceReturn(nd+i, aliasname, NULL);
630 }
631 return(rc);
632 }
633 #endif
634 }
635
636
637 /*
638 * Adds a vector to the display...
639 */
AddVector(bvptr vec,char * aliasname)640 int AddVector(bvptr vec, char *aliasname)
641 {
642 Trptr t;
643 int n;
644
645 if(!vec) return(0); /* must've passed it a null pointer by mistake */
646
647 GLOBALS->signalwindow_width_dirty=1;
648 GLOBALS->traces.dirty = 1;
649
650 n = vec->nbits;
651 t = (Trptr) calloc_2(1, sizeof( TraceEnt ) );
652 if( t == NULL )
653 {
654 fprintf( stderr, "Out of memory, can't add %s to analyzer\n",
655 vec->bvname );
656 return( 0 );
657 }
658
659 if (aliasname)
660 {
661 t->name_full = strdup_2(aliasname);
662 t->name = t->name_full;
663 }
664 else
665 {
666 t->name = vec->bvname;
667 }
668
669 if(GLOBALS->hier_max_level)
670 t->name = hier_extract(t->name, GLOBALS->hier_max_level);
671
672 t->flags = ( n > 3 ) ? TR_HEX|TR_RJUSTIFY : TR_BIN|TR_RJUSTIFY;
673 t->vector = TRUE;
674 t->n.vec = vec;
675 AddTrace( t );
676 return( 1 );
677 }
678
679
680 /*
681 * Free up a trace's mallocs...
682 */
FreeTrace(Trptr t)683 void FreeTrace(Trptr t)
684 {
685 GLOBALS->traces.dirty = 1;
686
687 if(GLOBALS->starting_unshifted_trace == t)
688 {
689 GLOBALS->starting_unshifted_trace = NULL; /* for new "standard" clicking routines */
690 }
691
692 if(GLOBALS->strace_ctx->straces)
693 {
694 struct strace_defer_free *sd = calloc_2(1, sizeof(struct strace_defer_free));
695 sd->next = GLOBALS->strace_ctx->strace_defer_free_head;
696 sd->defer = t;
697
698 GLOBALS->strace_ctx->strace_defer_free_head = sd;
699 return;
700 }
701
702 if(t->vector)
703 {
704 bvptr bv, bv2;
705 int i;
706
707 bv=t->n.vec;
708 /* back out allocation to revert (if any) */
709 if(bv->transaction_cache)
710 {
711 t->n.vec = bv->transaction_cache;
712
713 while(bv)
714 {
715 bv2 = bv->transaction_chain;
716 if(bv->bvname) { free_2(bv->bvname); }
717
718 for(i=0;i<bv->numregions;i++)
719 {
720 free_2(bv->vectors[i]);
721 }
722
723 free_2(bv);
724 bv = bv2;
725 }
726
727 bv=t->n.vec;
728 }
729
730 /* normal vector deallocation */
731 for(i=0;i<bv->numregions;i++)
732 {
733 if(bv->vectors[i]) free_2(bv->vectors[i]);
734 }
735
736 if(bv->bits)
737 {
738 if(bv->bits->name) free_2(bv->bits->name);
739 if(bv->bits->attribs) free_2(bv->bits->attribs);
740 for(i=0;i<bv->nbits;i++)
741 {
742 DeleteNode(bv->bits->nodes[i]);
743 }
744 free_2(bv->bits);
745 }
746
747 if(bv->bvname) free_2(bv->bvname);
748 if(t->n.vec) free_2(t->n.vec);
749 }
750 else
751 {
752 if(t->n.nd && t->n.nd->expansion)
753 {
754 DeleteNode(t->n.nd);
755 }
756 }
757
758 if(t->is_depacked) free_2(t->name);
759 if(t->asciivalue) free_2(t->asciivalue);
760 if(t->name_full) free_2(t->name_full);
761 if(t->transaction_args) free_2(t->transaction_args);
762 free_2( t );
763 }
764
765
766 /*
767 * Remove a trace from the display and optionally
768 * deallocate its memory usage...
769 */
RemoveTrace(Trptr t,int dofree)770 void RemoveTrace( Trptr t, int dofree )
771 {
772 GLOBALS->traces.dirty = 1;
773 GLOBALS->traces.total--;
774 if( t == GLOBALS->traces.first )
775 {
776 GLOBALS->traces.first = t->t_next;
777 if( t->t_next )
778 t->t_next->t_prev = NULL;
779 else
780 GLOBALS->traces.last = NULL;
781 }
782 else
783 {
784 if(t->t_prev)
785 {
786 t->t_prev->t_next = t->t_next;
787 }
788 else
789 {
790 /* this code should likely *never* execute as if( t == GLOBALS->traces.first ) above should catch this */
791 /* there is likely a problem elsewhere in the code! */
792
793 Trptr t2 = GLOBALS->traces.first = t->t_next;
794 GLOBALS->traces.total = 0;
795 while(t2)
796 {
797 t2 = t2->t_next;
798 GLOBALS->traces.total++;
799 }
800 }
801
802
803 if( t->t_next )
804 t->t_next->t_prev = t->t_prev;
805 else
806 GLOBALS->traces.last = t->t_prev;
807 }
808
809 if(dofree)
810 {
811 FreeTrace(t);
812 }
813 }
814
815
816 /*
817 * Deallocate the cut/paste buffer...
818 */
FreeCutBuffer(void)819 void FreeCutBuffer(void)
820 {
821 Trptr t, t2;
822
823 t=GLOBALS->traces.buffer;
824
825 while(t)
826 {
827 t2=t->t_next;
828 FreeTrace(t);
829 t=t2;
830 }
831
832 GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
833 GLOBALS->traces.buffercount=0;
834 }
835
836
837 /*
838 * Cut highlighted traces from the main screen
839 * and throw them in the cut buffer. If anything's
840 * in the cut buffer, deallocate it first...
841 */
CutBuffer(void)842 Trptr CutBuffer(void)
843 {
844 Trptr t, tnext;
845 Trptr first=NULL, current=NULL;
846
847 GLOBALS->shift_click_trace=NULL; /* so shift-clicking doesn't explode */
848
849 t=GLOBALS->traces.first;
850 while(t)
851 {
852 if((t->flags)&(TR_HIGHLIGHT)) break;
853 t=t->t_next;
854 }
855 if(!t) return(NULL); /* keeps a double cut from blowing out the buffer */
856
857 GLOBALS->signalwindow_width_dirty=1;
858 GLOBALS->traces.dirty = 1;
859
860 FreeCutBuffer();
861
862 t=GLOBALS->traces.first;
863 while(t)
864 {
865 tnext=t->t_next;
866 if(IsSelected(t) || (t->t_grp && IsSelected(t->t_grp)))
867 {
868 /* members of closed groups may not be highlighted */
869 /* so propogate highlighting here */
870 t->flags |= TR_HIGHLIGHT;
871 GLOBALS->traces.bufferlast=t;
872 GLOBALS->traces.buffercount++;
873
874 /* t->flags&=(~TR_HIGHLIGHT); */
875 RemoveTrace(t, 0);
876 if(!current)
877 {
878 first=current=t;
879 t->t_prev=NULL;
880 t->t_next=NULL;
881 }
882 else
883 {
884 current->t_next=t;
885 t->t_prev=current;
886 current=t;
887 t->t_next=NULL;
888 }
889 }
890 t=tnext;
891 }
892
893 return(GLOBALS->traces.buffer=first);
894 }
895
896
897 /*
898 * Delete highlighted traces from the main screen
899 * and throw them away. Do not affect existing cut buffer...
900 */
DeleteBuffer(void)901 int DeleteBuffer(void)
902 {
903 Trptr t, tnext;
904 Trptr current=NULL;
905 Trptr buffer; /* cut/copy buffer of traces */
906 Trptr bufferlast; /* last element of bufferchain */
907 int buffercount; /* number of traces in buffer */
908 int num_deleted;
909
910 GLOBALS->shift_click_trace=NULL; /* so shift-clicking doesn't explode */
911
912 t=GLOBALS->traces.first;
913 while(t)
914 {
915 if((t->flags)&(TR_HIGHLIGHT)) break;
916 t=t->t_next;
917 }
918 if(!t) return(0); /* nothing to do */
919
920 GLOBALS->signalwindow_width_dirty=1;
921 GLOBALS->traces.dirty = 1;
922
923 buffer = GLOBALS->traces.buffer; /* copy cut buffer to make re-entrant */
924 bufferlast = GLOBALS->traces.bufferlast;
925 buffercount = GLOBALS->traces.buffercount;
926
927 GLOBALS->traces.buffer = NULL;
928 GLOBALS->traces.bufferlast = NULL;
929 GLOBALS->traces.buffercount = 0;
930
931 t=GLOBALS->traces.first;
932 while(t)
933 {
934 tnext=t->t_next;
935 if(IsSelected(t) || (t->t_grp && IsSelected(t->t_grp)))
936 {
937 /* members of closed groups may not be highlighted */
938 /* so propogate highlighting here */
939 t->flags |= TR_HIGHLIGHT;
940 GLOBALS->traces.bufferlast=t;
941 GLOBALS->traces.buffercount++;
942
943 /* t->flags&=(~TR_HIGHLIGHT); */
944 RemoveTrace(t, 0);
945 if(!current)
946 {
947 current=t;
948 t->t_prev=NULL;
949 t->t_next=NULL;
950 }
951 else
952 {
953 current->t_next=t;
954 t->t_prev=current;
955 current=t;
956 t->t_next=NULL;
957 }
958 }
959 t=tnext;
960 }
961
962 num_deleted = GLOBALS->traces.buffercount;
963 FreeCutBuffer();
964
965 GLOBALS->traces.buffer = buffer; /* restore cut buffer */
966 GLOBALS->traces.bufferlast = bufferlast;
967 GLOBALS->traces.buffercount = buffercount;
968
969 return(num_deleted);
970 }
971
972
973 /*
974 * Paste the cut buffer into the main display one and
975 * mark the cut buffer empty...
976 */
PasteBuffer(void)977 Trptr PasteBuffer(void)
978 {
979 Trptr t, tinsert=NULL, tinsertnext;
980 int count;
981 Trptr prev;
982
983 if(!GLOBALS->traces.buffer) return(NULL);
984
985 GLOBALS->signalwindow_width_dirty=1;
986 GLOBALS->traces.dirty = 1;
987
988 if(!(t=GLOBALS->traces.first))
989 {
990 t=GLOBALS->traces.last=GLOBALS->traces.first=GLOBALS->traces.buffer;
991 prev = NULL;
992 while(t)
993 {
994 t->t_prev = prev; /* defensive re-link move */
995 prev = t;
996 GLOBALS->traces.last=t;
997 GLOBALS->traces.total++;
998 t=t->t_next;
999 }
1000
1001 GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
1002 GLOBALS->traces.buffercount=0;
1003
1004 return(GLOBALS->traces.first);
1005 }
1006
1007 while(t)
1008 {
1009 if(t->flags&TR_HIGHLIGHT)
1010 {
1011 tinsert=t;
1012 }
1013 t=t->t_next;
1014 }
1015
1016
1017 if(!tinsert) tinsert=GLOBALS->traces.last;
1018
1019 if(IsGroupBegin(tinsert) && IsClosed(tinsert) && IsCollapsed(tinsert->t_match))
1020 tinsert=tinsert->t_match;
1021
1022 tinsertnext=tinsert->t_next;
1023 tinsert->t_next=GLOBALS->traces.buffer;
1024 GLOBALS->traces.buffer->t_prev=tinsert;
1025 GLOBALS->traces.bufferlast->t_next=tinsertnext;
1026 GLOBALS->traces.total+=GLOBALS->traces.buffercount;
1027
1028 if(!tinsertnext)
1029 {
1030 GLOBALS->traces.last=GLOBALS->traces.bufferlast;
1031 }
1032 else
1033 {
1034 tinsertnext->t_prev=GLOBALS->traces.bufferlast;
1035 }
1036
1037 GLOBALS->traces.scroll_top = GLOBALS->traces.buffer;
1038 GLOBALS->traces.scroll_bottom = GLOBALS->traces.bufferlast;
1039
1040 if(GLOBALS->traces.first)
1041 {
1042 t = GLOBALS->traces.first;
1043 t->t_grp = NULL;
1044 while(t)
1045 {
1046 updateTraceGroup(t);
1047 t->flags &= ~TR_HIGHLIGHT;
1048 t=t->t_next;
1049 }
1050 }
1051
1052 count = 0;
1053
1054 if (GLOBALS->traces.buffer)
1055 {
1056 t = GLOBALS->traces.buffer;
1057 while(t)
1058 {
1059 t->flags |= TR_HIGHLIGHT;
1060 t=t->t_next;
1061 count++;
1062 if (count == GLOBALS->traces.buffercount) break;
1063 }
1064 }
1065
1066 /* clean out the buffer */
1067 GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
1068 GLOBALS->traces.buffercount=0;
1069
1070 /* defensive re-link */
1071 t=GLOBALS->traces.first;
1072 prev = NULL;
1073 while(t)
1074 {
1075 t->t_prev = prev;
1076 prev = t;
1077 t=t->t_next;
1078 }
1079
1080 return(GLOBALS->traces.first);
1081 }
1082
1083
1084 /*
1085 * Prepend the cut buffer into the main display one and
1086 * mark the cut buffer empty...
1087 */
PrependBuffer(void)1088 Trptr PrependBuffer(void)
1089 {
1090 Trptr t, prev = NULL;
1091 int count;
1092
1093 if(!GLOBALS->traces.buffer) return(NULL);
1094
1095 GLOBALS->signalwindow_width_dirty=1;
1096 GLOBALS->traces.dirty = 1;
1097
1098 t=GLOBALS->traces.buffer;
1099
1100 while(t)
1101 {
1102 t->t_prev = prev; /* defensive re-link move */
1103 prev=t;
1104 t->flags&=(~TR_HIGHLIGHT);
1105 GLOBALS->traces.total++;
1106 t=t->t_next;
1107 }
1108
1109 if((prev->t_next=GLOBALS->traces.first))
1110 {
1111 /* traces.last current value is ok as it stays the same */
1112 GLOBALS->traces.first->t_prev=prev; /* but we need the reverse link back up */
1113 }
1114 else
1115 {
1116 GLOBALS->traces.last=prev;
1117 }
1118
1119 GLOBALS->traces.first=GLOBALS->traces.buffer;
1120
1121 if(GLOBALS->traces.first)
1122 {
1123 t = GLOBALS->traces.first;
1124 t->t_grp = NULL;
1125 while(t)
1126 {
1127 updateTraceGroup(t);
1128 t->flags &= ~TR_HIGHLIGHT;
1129 t=t->t_next;
1130 }
1131 }
1132
1133 count = 0;
1134
1135 if (GLOBALS->traces.buffer)
1136 {
1137 t = GLOBALS->traces.buffer;
1138 while(t)
1139 {
1140 t->flags |= TR_HIGHLIGHT;
1141 t=t->t_next;
1142 count++;
1143 if (count == GLOBALS->traces.buffercount) break;
1144 }
1145 }
1146
1147 /* clean out the buffer */
1148 GLOBALS->traces.buffer=GLOBALS->traces.bufferlast=NULL;
1149 GLOBALS->traces.buffercount=0;
1150
1151 /* defensive re-link */
1152 t=GLOBALS->traces.first;
1153 prev = NULL;
1154 while(t)
1155 {
1156 t->t_prev = prev;
1157 prev = t;
1158 t=t->t_next;
1159 }
1160
1161 return(GLOBALS->traces.first);
1162 }
1163
1164
1165 /*
1166 * avoid sort/rvs manipulations if there are group traces (for now)
1167 */
groupsArePresent(void)1168 static int groupsArePresent(void)
1169 {
1170 Trptr t;
1171 int i, rc = 0;
1172
1173 t=GLOBALS->traces.first;
1174 for(i=0;i<GLOBALS->traces.total;i++)
1175 {
1176 if(!t)
1177 {
1178 fprintf(stderr, "INTERNAL ERROR: traces.total vs traversal mismatch! Exiting.\n");
1179 exit(255);
1180 }
1181
1182 if((t->t_grp)||(t->t_match)||(t->flags & TR_GRP_MASK))
1183 {
1184 rc = 1; break;
1185 }
1186
1187 t=t->t_next;
1188 }
1189
1190 return(rc);
1191 }
1192
1193
1194 /*************************************************************/
1195
1196
1197 /*
1198 * sort on tracename pointers (alpha/caseins alpha/sig sort full_reverse)
1199 */
tracenamecompare(const void * s1,const void * s2)1200 static int tracenamecompare(const void *s1, const void *s2)
1201 {
1202 char *str1, *str2;
1203
1204 str1=(*((Trptr *)s1))->name;
1205 str2=(*((Trptr *)s2))->name;
1206
1207 if((!str1) || (!*str1)) /* force blank lines to go to bottom */
1208 {
1209 if((!str2) || (!*str2))
1210 {
1211 return(0);
1212 }
1213 else
1214 {
1215 return(1);
1216 }
1217 }
1218 else
1219 if((!str2) || (!*str2))
1220 {
1221 return(-1); /* str1==str2==zero case is covered above */
1222 }
1223
1224 return(strcmp(str1, str2));
1225 }
1226
1227
traceinamecompare(const void * s1,const void * s2)1228 static int traceinamecompare(const void *s1, const void *s2)
1229 {
1230 char *str1, *str2;
1231
1232 str1=(*((Trptr *)s1))->name;
1233 str2=(*((Trptr *)s2))->name;
1234
1235 if((!str1) || (!*str1)) /* force blank lines to go to bottom */
1236 {
1237 if((!str2) || (!*str2))
1238 {
1239 return(0);
1240 }
1241 else
1242 {
1243 return(1);
1244 }
1245 }
1246 else
1247 if((!str2) || (!*str2))
1248 {
1249 return(-1); /* str1==str2==zero case is covered above */
1250 }
1251
1252 return(strcasecmp(str1, str2));
1253 }
1254
tracesignamecompare(const void * s1,const void * s2)1255 static int tracesignamecompare(const void *s1, const void *s2)
1256 {
1257 char *str1, *str2;
1258
1259 str1=(*((Trptr *)s1))->name;
1260 str2=(*((Trptr *)s2))->name;
1261
1262 if((!str1) || (!*str1)) /* force blank lines to go to bottom */
1263 {
1264 if((!str2) || (!*str2))
1265 {
1266 return(0);
1267 }
1268 else
1269 {
1270 return(1);
1271 }
1272 }
1273 else
1274 if((!str2) || (!*str2))
1275 {
1276 return(-1); /* str1==str2==zero case is covered above */
1277 }
1278
1279 return(sigcmp(str1, str2));
1280 }
1281
1282
1283 /*
1284 * alphabetization/reordering of traces
1285 */
TracesReorder(int mode)1286 int TracesReorder(int mode)
1287 {
1288 Trptr t, prev = NULL;
1289 Trptr *tsort, *tsort_pnt;
1290 #ifdef WAVE_HIERFIX
1291 char *subst, ch;
1292 #endif
1293 int i;
1294 int (*cptr)(const void*, const void*);
1295
1296 if(!GLOBALS->traces.total) return(0);
1297 GLOBALS->traces.dirty = 1;
1298
1299 t=GLOBALS->traces.first;
1300 tsort=tsort_pnt=wave_alloca(sizeof(Trptr)*GLOBALS->traces.total);
1301 memset(tsort_pnt, 0, sizeof(Trptr)*GLOBALS->traces.total);
1302
1303 for(i=0;i<GLOBALS->traces.total;i++)
1304 {
1305 if(!t)
1306 {
1307 fprintf(stderr, "INTERNAL ERROR: traces.total vs traversal mismatch! Exiting.\n");
1308 exit(255);
1309 }
1310 *(tsort_pnt++)=t;
1311
1312 #ifdef WAVE_HIERFIX
1313 if((subst=t->name))
1314 while((ch=(*subst)))
1315 {
1316 if(ch==GLOBALS->hier_delimeter) { *subst=VCDNAM_HIERSORT; } /* forces sort at hier boundaries */
1317 subst++;
1318 }
1319 #endif
1320
1321 t=t->t_next;
1322 }
1323
1324 switch(mode)
1325 {
1326 case TR_SORT_INS: cptr=traceinamecompare; break;
1327 case TR_SORT_NORM: cptr=tracenamecompare; break;
1328 case TR_SORT_LEX: cptr=tracesignamecompare; break;
1329 default: cptr=NULL; break;
1330 }
1331
1332 if((cptr) && (!groupsArePresent()))
1333 {
1334 qsort(tsort, GLOBALS->traces.total, sizeof(Trptr), cptr);
1335 }
1336 else /* keep groups segregated off on the side and sort names + (indirect pointer to) top-level groups */
1337 {
1338 Trptr *tsort_reduced = wave_alloca(sizeof(Trptr)*GLOBALS->traces.total);
1339 int num_reduced = 0;
1340 int j;
1341
1342 memset(tsort_reduced, 0, sizeof(Trptr)*GLOBALS->traces.total);
1343
1344 for(i=0;i<GLOBALS->traces.total;i++)
1345 {
1346 if(tsort[i]->flags & TR_GRP_BEGIN)
1347 {
1348 int cnt = 0;
1349
1350 for(j=i;j<GLOBALS->traces.total;j++)
1351 {
1352 if(tsort[j]->flags & TR_GRP_BEGIN) { cnt++; }
1353 else if(tsort[j]->flags & TR_GRP_END) { cnt--; }
1354
1355 if(!cnt)
1356 {
1357 tsort_reduced[num_reduced] = calloc_2(1, sizeof(struct TraceEnt));
1358 tsort_reduced[num_reduced]->name = tsort[i]->name;
1359 tsort_reduced[num_reduced]->is_sort_group = 1;
1360 tsort_reduced[num_reduced]->t_grp = tsort[i];
1361
1362 tsort[j]->t_next = NULL;
1363 num_reduced++;
1364
1365 i = j; break;
1366 }
1367 }
1368 }
1369 else
1370 {
1371 tsort_reduced[num_reduced++] = tsort[i];
1372 }
1373 }
1374
1375 if(num_reduced)
1376 {
1377 if(mode == TR_SORT_RVS) /* reverse of current order */
1378 {
1379 for(i=0;i<=(num_reduced/2);i++)
1380 {
1381 Trptr t_tmp = tsort_reduced[i];
1382
1383 j = num_reduced-i-1;
1384 tsort_reduced[i] = tsort_reduced[j];
1385 tsort_reduced[j] = t_tmp;
1386 }
1387 }
1388 else
1389 {
1390 if(cptr)
1391 {
1392 qsort(tsort_reduced, num_reduced, sizeof(Trptr), cptr);
1393 }
1394 }
1395 }
1396
1397 i = 0;
1398 for(j=0;j<num_reduced;j++)
1399 {
1400 if(!tsort_reduced[j]->is_sort_group)
1401 {
1402 tsort[i++] = tsort_reduced[j];
1403 }
1404 else
1405 {
1406 Trptr trav = tsort_reduced[j]->t_grp;
1407 free_2(tsort_reduced[j]);
1408 while(trav)
1409 {
1410 tsort[i++] = trav;
1411 trav = trav->t_next;
1412 }
1413 }
1414 }
1415 }
1416
1417 tsort_pnt=tsort;
1418 for(i=0;i<GLOBALS->traces.total;i++)
1419 {
1420 t=*(tsort_pnt++);
1421
1422 if(!i)
1423 {
1424 GLOBALS->traces.first=t;
1425 t->t_prev=NULL;
1426 }
1427 else
1428 {
1429 prev->t_next=t;
1430 t->t_prev=prev;
1431 }
1432
1433 prev=t;
1434
1435 #ifdef WAVE_HIERFIX
1436 if((subst=t->name))
1437 while((ch=(*subst)))
1438 {
1439 if(ch==VCDNAM_HIERSORT) { *subst=GLOBALS->hier_delimeter; } /* restore hier */
1440 subst++;
1441 }
1442 #endif
1443 }
1444
1445 GLOBALS->traces.last=prev;
1446 if(prev) { prev->t_next=NULL; } /* scan-build */
1447
1448 return(1);
1449 }
1450
1451
GiveNextTrace(Trptr t)1452 Trptr GiveNextTrace(Trptr t)
1453 {
1454 if(!t) return(t); /* should not happen */
1455
1456 /* if(t->name) { printf("NEXT: %s %x\n", t->name, t->flags); } */
1457 UpdateTraceSelection(t);
1458 if (IsGroupBegin(t) && IsClosed(t))
1459 {
1460 Trptr next = t->t_match;
1461 if (next)
1462 return (IsCollapsed(next) ? GiveNextTrace(next) : next);
1463 return NULL;
1464 }
1465 else
1466 {
1467 Trptr next = t->t_next;
1468 if (next)
1469 return (IsCollapsed(next) ? GiveNextTrace(next) : next);
1470 return NULL;
1471 }
1472 }
1473
GivePrevTraceSkipUpdate(Trptr t,int skip)1474 static Trptr GivePrevTraceSkipUpdate(Trptr t, int skip)
1475 {
1476 if(!t) return(t); /* should not happen */
1477
1478 /* if(t->name) { printf("PREV: %s\n", t->name); } */
1479 if(!skip) { UpdateTraceSelection(t); }
1480 if (IsGroupEnd(t) && IsClosed(t))
1481 {
1482 Trptr prev = t->t_match;
1483 if (prev)
1484 return (IsCollapsed(prev) ? GivePrevTrace(prev) : prev);
1485 return NULL;
1486 }
1487 else
1488 {
1489 Trptr prev = t->t_prev;
1490 if (prev)
1491 return (IsCollapsed(prev) ? GivePrevTrace(prev) : prev);
1492 return NULL;
1493 }
1494 }
1495
GivePrevTrace(Trptr t)1496 Trptr GivePrevTrace(Trptr t)
1497 {
1498 return(GivePrevTraceSkipUpdate(t, 0));
1499 }
1500
1501
1502 /* propogate selection info down into groups */
UpdateTraceSelection(Trptr t)1503 void UpdateTraceSelection(Trptr t)
1504 {
1505 if ((t->t_match) && (IsGroupBegin(t) || IsGroupEnd(t)) && (IsSelected(t) || IsSelected(t->t_match)))
1506 {
1507 t->flags |= TR_HIGHLIGHT;
1508 t->t_match->flags |= TR_HIGHLIGHT;
1509 }
1510 else
1511 if ((t->t_grp) && IsSelected(t->t_grp))
1512 {
1513 t->flags |= TR_HIGHLIGHT;
1514 }
1515 else
1516 if((t->flags & (TR_BLANK|TR_ANALOG_BLANK_STRETCH)) && (GLOBALS->num_ttrans_filters)) /* seek to real xact trace if present... */
1517 {
1518 if(!(t->flags & TR_HIGHLIGHT))
1519 {
1520 Trptr tscan = t;
1521 int bcnt = 0;
1522 while((tscan) && (tscan = tscan->t_prev)) /* && branch formerly was (tscan = GivePrevTraceSkipUpdate(tscan, 1)): overkill as blank traces in transactions don't nest into groups */
1523 {
1524 if(!(tscan->flags & (TR_BLANK|TR_ANALOG_BLANK_STRETCH)))
1525 {
1526 if(tscan->flags & TR_TTRANSLATED)
1527 {
1528 break; /* found it */
1529 }
1530 else
1531 {
1532 tscan = NULL;
1533 }
1534 }
1535 else
1536 {
1537 bcnt++; /* bcnt is number of blank traces */
1538 }
1539 }
1540
1541 if((tscan)&&(tscan->vector)&&(IsSelected(tscan)))
1542 {
1543 bvptr bv = tscan->n.vec;
1544 do
1545 {
1546 bv = bv->transaction_chain; /* correlate to blank trace */
1547 } while(bv && (bcnt--));
1548 if(bv)
1549 {
1550 t->flags |= TR_HIGHLIGHT;
1551 }
1552 }
1553 }
1554 }
1555 }
1556
1557
UpdateTracesVisible(void)1558 int UpdateTracesVisible(void)
1559 {
1560 Trptr t = GLOBALS->traces.first;
1561 int cnt = 0;
1562
1563 while(t)
1564 {
1565 t = GiveNextTrace(t);
1566 cnt++;
1567 }
1568
1569 GLOBALS->traces.visible = cnt;
1570 return(cnt);
1571 }
1572
1573 /* where is trace t_in in the list of displayable traces */
GetTraceNumber(Trptr t_in)1574 int GetTraceNumber(Trptr t_in)
1575 {
1576 Trptr t = GLOBALS->traces.first;
1577 int i = 0;
1578 int num = -1;
1579
1580 while(t)
1581 {
1582 if (t == t_in)
1583 {
1584 num = i;
1585 break;
1586 }
1587 i++;
1588 t = GiveNextTrace(t);
1589 }
1590
1591 return(num);
1592 }
1593
IsShadowed(Trptr t)1594 unsigned IsShadowed( Trptr t )
1595 {
1596
1597 if (t->t_grp)
1598 {
1599 if (HasWave(t->t_grp))
1600 {
1601 return IsSelected(t->t_grp);
1602 }
1603 else
1604 {
1605 return IsShadowed(t->t_grp);
1606 }
1607 }
1608
1609 return 0;
1610 }
1611
GetFullName(Trptr t,int * was_packed)1612 char* GetFullName( Trptr t, int *was_packed )
1613 {
1614 if (HasAlias(t) || !HasWave(t))
1615 {
1616 return (t->name_full);
1617 }
1618 else if (t->vector)
1619 {
1620 return (t->n.vec->bvname);
1621
1622 }
1623 else
1624 {
1625 return (hier_decompress_flagged(t->n.nd->nname, was_packed));
1626 }
1627 }
1628
1629
1630 /*
1631 * sanity checking to make sure there are not any group open/close mismatches
1632 */
EnsureGroupsMatch(void)1633 void EnsureGroupsMatch(void)
1634 {
1635 Trptr t = GLOBALS->traces.first;
1636 Trptr last_good = t;
1637 Trptr t2;
1638 int oc_cnt = 0;
1639 int underflow_sticky = 0;
1640 /* Trptr tkill_undeflow = NULL; */ /* scan-build */
1641
1642 while(t)
1643 {
1644 if(t->flags & TR_GRP_MASK)
1645 {
1646 if(t->flags & TR_GRP_BEGIN)
1647 {
1648 oc_cnt++;
1649 }
1650 else
1651 if(t->flags & TR_GRP_END)
1652 {
1653 oc_cnt--;
1654 if(oc_cnt == 0)
1655 {
1656 if(!underflow_sticky)
1657 {
1658 last_good = t->t_next;
1659 }
1660 }
1661 }
1662
1663 if(oc_cnt < 0)
1664 {
1665 /*
1666 if(!underflow_sticky)
1667 {
1668 tkill_undeflow = t;
1669 }
1670 */ /* scan-build */
1671 underflow_sticky = 1;
1672 }
1673 }
1674 else
1675 {
1676 if((oc_cnt == 0) && (!underflow_sticky))
1677 {
1678 last_good = t->t_next;
1679 }
1680 }
1681
1682 t = t->t_next;
1683 }
1684
1685 if((underflow_sticky) || (oc_cnt > 0))
1686 {
1687 t = last_good;
1688 while(t)
1689 {
1690 t2 = t->t_next;
1691 RemoveTrace(t, 0); /* conservatively don't set "dofree", if there is a reload memory will reclaim */
1692 t = t2;
1693 }
1694 }
1695 }
1696
1697