DebStream.cc 23.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
// (C) Copyright 2014 by Autodesk, Inc.
//
// The information contained herein is confidential, proprietary
// to Autodesk,  Inc.,  and considered a trade secret as defined
// in section 499C of the penal code of the State of California.
// Use of  this information  by  anyone  other  than  authorized
// employees of Autodesk, Inc.  is granted  only under a written
// non-disclosure agreement,  expressly  prescribing  the  scope
// and manner of such use.

11
#include "DebUtils.hh"
12
13
14

#ifdef DEB_ON

15
16
17
18
19
#include <string>
#include <fstream>
#include <time.h>
#include <vector>
#include <iostream>
20
#include <map>
21

22
23
namespace { // LOCAL_PROC
bool is_html_filename(const char* const str)
24
{
25
  if (str == nullptr) return false;
26
  const char* dot = strrchr(str, '.');
27
28
  if (dot == nullptr) return false;
  ++dot;
29
  return (!strncmp(dot, "htm", 3)) || (!strncmp(dot, "HTM", 3)) ;
30
}
31
}
32

33
namespace Debug {
34
35

class FunctionCallSequence
36
{
37
  std::string func_name_;
38
  std::vector<Enter*> debs_;  // These may not have sequential counts when multithreaded.
39
40

public:
41
  FunctionCallSequence(const char* _func_name, Enter* _deb)
42
    : func_name_(_func_name)
43
  {
44
45
46
    debs_.push_back(_deb);
  }
  ~FunctionCallSequence() {}
47

48
  bool add(const char* _func_name, Enter* _deb)
49
50
  {
    if (func_name_ == _func_name)
51
    {
52
      debs_.push_back(_deb);
53
      return true;
54
    }
55
56
    return false;
  }
57

58
59
60
  bool pop()
  {
    if (debs_.size() > 1)
61
    {
62
63
      debs_.pop_back();
      return true;
64
    }
65
66
67
    debs_.clear();
    return false;
  }
68

69
70
71
72
73
  int number_calls() const
  {
    if (!this) return 0;
    return (int)debs_.size();
  }
74

75
76
77
78
79
80
  int count(int i = 0) const
  {
    int num = number_calls();
    if (i < num) return debs_[num - 1 - i]->count_;
    return -1;
  }
81

82
83
84
85
  const char* name() const
  {
    return func_name_.c_str();
  }
86

87
88
89
90
91
92
  // Replace interior of < > in function name with . for brevity
  void compact_name(std::string& str) const
  {
    int cnt = 0;
    const char* ptr = func_name_.c_str();
    while (ptr && (*ptr != '\0'))
93
    {
94
95
96
97
      char c = *ptr;
      if (c == '>') --cnt;
      if (cnt == 0) str.append(1, c);
      if (c == '<')
98
      {
99
100
        if (cnt == 0) str.append(".");
        ++cnt;
101
      }
102
      ++ptr;
103
    }
104
  }
105

106
107
108
109
110
111
112
  // Get single call stack element string
  void get(std::string& _str, const bool _strip_angled, bool _with_counts) const
  {
    if (_strip_angled) compact_name(_str);
    else _str.append(name());
    _str.append("[");
    if (_with_counts)
113
    {
114
115
116
117
118
119
120
      int num = number_calls();
      int prev = -2;
      int seq_cnt = 0;
      for (int i = 0; i < num; ++i)
      {
        int cnt = debs_[i]->count_;
        if (cnt != prev + 1)
121
        {
122
          char buffer[64];
123

124
125
126
127
          if (seq_cnt > 0)
          {
            _str.append("-");
            sprintf_s(buffer, sizeof(buffer), "%i", prev);
128
            _str.append(buffer);
129
          }
130
131
132
133
134
135
136
137
138
139
140
141
142
143
          if (i > 0) _str.append(",");
          sprintf_s(buffer, sizeof(buffer), "%i", cnt);
          _str.append(buffer);
          seq_cnt = 0;
        }
        else
          ++seq_cnt;
        prev = cnt;
      } // endfor i
    } // endif _with_counts
    else
      _str.append("*");
    _str.append("]");
  } // endfunc get
144

145
  void get_indent(std::string& _str, File* _dfile, bool _is_html);
146

147
}; // endclass FunctionCallSequence
148
149


150
151
152
153
154
155
156
class CallStack
{
  std::vector<FunctionCallSequence> calls_;
  int depth_;
public:
  CallStack() : depth_(0) {}
  ~CallStack() {}
157

158
  void add(const char* _func_name, Enter* _deb)
159
160
161
162
163
  {
    if (calls_.empty() || !calls_.back().add(_func_name, _deb))
      calls_.push_back(FunctionCallSequence(_func_name, _deb));
    ++depth_;
  }
164

165
166
167
168
169
170
  void pop()
  {
    if (!calls_.back().pop())
      calls_.pop_back();
    --depth_;
  }
171

172
173
174
175
176
177
  const FunctionCallSequence* call(int _up = 0)
  {
    int num = (int)calls_.size();
    if (_up < num) return &calls_[num - 1 - _up];
    return nullptr;
  }
178

179
180
181
182
183
  // Read a particular call stack element
  bool read(int _up, const char*& _funcname, int& _count)
  {
    const FunctionCallSequence* fcs = call(_up);
    if (fcs != nullptr)
184
    {
185
186
187
      _funcname = fcs->name();
      _count = fcs->count(0); // Return most recent deb_enter_count
      return true;
188
    }
189
190
    return false;
  }
191

192
193
194
195
196
197
  // Get a full call stack sting.
  // returns number of call stack function elements
  int get(std::string& _str, bool _with_counts = true) const
  {
    int num = (int)calls_.size();
    for (int i = 0; i < num; ++i)
198
    {
199
200
      if (i > 0) _str.append("->");
      calls_[i].get(_str, true, _with_counts);
201
    }
202
203
    return num;
  }
204

205
206
207
208
  int depth() const
  {
    return depth_;
  }
209

210
  bool get_indent(std::string& _str, File* _dfile, const bool is_html);
211
}; // endclass CallStack
212

213
214


215
class File
216
217
{
private:
218
219
220
221
222
  class module_stats
  {
  public:
    int lev_; // Level set for this module
    int col_; // Colour (24 bit RGB, MSB=Red) for this module's DEB_out
223
    module_stats(int _lev = 0) : lev_(_lev), col_(0x808080) {}
224
225
226
227
228
229
    ~module_stats() {}
  };

  std::map<std::string, module_stats> module_map_;
  typedef std::map<std::string, module_stats>::iterator module_map_itr;
  typedef std::map<std::string, module_stats>::const_iterator const_module_map_itr;
230

231
  Stream::StreamType type_;
232
233
  int lev_;
  int num_flush_;
234
  int priority_; // Last permission granted
235
  int indent_size_;
236

237
238
  bool at_line_start_;

239
240
241
242
  std::string current_;
  std::string output_;
  std::string file_name_;
  std::fstream file_stream_;
243
244

  std::string double_format_;
245
  //std::string indent_string_;
246
  Stream* deb_stream_;
247
248
249
250
  CallStack call_stack_;

public:

251
252
253
254
  CallStack& call_stack()
  {
    return call_stack_;
  }
255

256
257
  bool is_kept_open() const
  {
258
    return 0 != (type_ & Stream::StreamType::KEEP_OPEN);
259
260
261
  }
  bool is_html() const
  {
262
    return 0 != (type_ & Stream::StreamType::HTML);
263
264
265
  }
  bool is_retained() const
  {
266
    return 0 != (type_ & Stream::StreamType::RETAIN);
267
268
269
  }
  bool is_appended() const
  {
270
    return 0 != (type_ & Stream::StreamType::APPEND);
271
  }
272
  // Only applies to HTML DEB_out
273
274
275
276
277
278
279
280
  bool is_white_on_black() const
  {
    return true;
  }
  int indent_size()
  {
    return indent_size_;
  }
281

282
283
284
285
286
287
288
289
  bool file_is_open() const
  {
    return file_stream_.is_open();
  }
  int priority() const
  {
    return priority_;
  }
290

291
292
293
294
295
296
297
298
  bool fork_to_cout()
  {
    return false;
  }
  bool fork_to_cerr()
  {
    return true;
  }
299
300


301
  const char* file_name() const
302
  {
303
    if (this && (!file_name_.empty())) return file_name_.c_str();
304
305
    return nullptr;
  }
306

307
308
309
310
311
312
  void clear()
  {
    current_.clear();
    output_.clear();
    file_name_.clear();
  }
313

314
315
316
317
318
319
320
  char prev_char() const
  {
    if (!current_.empty()) return current_.back();
    if (!output_.empty()) return output_.back();
    return '\0';
  }

321

322
  void indent(bool _full_text)
323
  {
324
    std::string str;
325
    if (call_stack().get_indent(str, this,  _full_text && is_html()))
326
      current_.append(str);
327
328
  }

329

330
  void line_break(bool _with_indent = false)
331
  {
332
    if (is_html()) current_.append("<br>");   // Don't bother with matching </br>
333
334
    current_.append("\n", 1);

335
336
    if (_with_indent) indent(false);
    else at_line_start_ = true;
337

338
339
  }

340
341
342
343
344
345
  void set_level(int _lev)
  {
    lev_ = _lev;
  }

  void print_direct(const std::string& _s)
346
347
348
349
350
  {
    current_.append(_s);
  }


351
352
  void print(const char _c)
  {
353
    if (_c == '\n')
354
355
356
357
358
    {
      line_break();
      return;
    }

359
360
    if (at_line_start_)
    {
361
      indent(true);
362
363
364
365
      at_line_start_ = false;
    }


366
367
368
    if (is_html())
    {
      // translate the esoteric characters used in IGM DEB_out
369

370
      if (_c == '')  // -62
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
        return;

      if (_c == '') // 167 = -89
      {
        current_.append("&sect;");
        return;
      }

      if (_c == '') // -80
      {
        current_.append("&deg;");
        return;
      }
    }
    current_.append(&_c, 1);
386
387
  }

388
389
390
391
392
393

  void print_to_ostream(const char* const _s, std::ostream& os)
  {
    os << _s;
  }

394
  void print(const char* const _s, bool _fork = true)
395
396
397
  {
    if (_s != nullptr)
    {
398
      for (int i = 0; ; ++i)
399
400
      {
        const char c = _s[i];
401
        if (c == '\0') break;
402
403
404
405
        print(c);
      }
      if (_fork)
      {
406
        if (fork_to_cout())
407
          print_to_ostream(_s, std::cout);
408
        if (fork_to_cerr())
409
          print_to_ostream(_s, std::cerr);
410
      }
411
    }
412
413
414
415
416
417
418
419
  }

  void print(int _i)
  {
    char buffer[64];
    sprintf_s(buffer, sizeof(buffer), "%i", _i);
    print(buffer);
  }
420

421

422
  const char* double_format() const
423
424
425
426
427
428
  {
    if (double_format_.empty())
      return "%.17g";
    return double_format_.c_str();
  }

429
  void set_double_format(const char* const str)
430
  {
431
    if (str == nullptr)
432
433
434
435
436
437
      double_format_.clear();
    else
      double_format_ = str;
  }


438
439
440
  void print(double _d)
  {
    char buffer[64];
441
    sprintf_s(buffer, sizeof(buffer), double_format(), _d);
442
443
444
    print(buffer);
  }

445
  void print(const Command& _co)
446
  {
447
448
    switch (_co.com())
    {
449
    case Command::END :
450
      if (is_html()) print_direct("</FONT>");
451
      break;
452
    case Command::END_ERR :
453
454
455
    // Powerdown DEB_error font
    // if (is_html()) print_direct("</BLINK>");
    // fall through
456
    case Command::END_LF :
457
      if (is_html()) print_direct("</FONT>");
458
459
460
461
462
463
464
465
466
      line_break(); 
      
      // line_break() does not fork to cout or cerr
      // so do so explicitly.
      if (fork_to_cout())
         std::cout << "\n";
      if (fork_to_cerr())
         std::cerr << "\n";

467
468
469
470
      break;
    }
  }

471
  Stream& stream()
472
473
474
  {
    return *deb_stream_;
  }
475

476
477
  // Append current asctime to given string
  bool add_time(std::string& str)
478
479
  {
    time_t rawtime;
480
    time(&rawtime);
481
    struct tm timeinfo;
482
    errno_t err = localtime_s(&timeinfo, &rawtime);
483
484
485
486
487
488
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
489
        str.append(buffer);
490
491
492
493
494
495
        return true;
      }
    }
    return false;
  }

496

497
#if 1
498
  bool hover(std::string& _str, const std::string& _hover, const bool _open)
499
500
501
502
  {
    if (is_html())
    {
      char buffer[1024];
503
504
      if (_open)  sprintf_s(buffer, sizeof(buffer),
                              "<span title=\"%s\">", _hover.c_str());
505
506
507
508
509
510
511
512
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

513
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
514
515
516
517
518
519
520
521
522
523
524
525
  {
    if (is_html())
    {
      char buffer[1024];
      if (_open)  sprintf_s(buffer, sizeof(buffer), "<A name=\"%08X_%s\">", _id, _tag);
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }

526
  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
527
528
529
530
531
532
533
534
535
536
  {
    if (is_html())
    {
      char buffer[2048];
      if (_open)
      {
        // HTML title hover text is cropped to 64 char in Firefox but displays
        // OK in Chrome. We could use javascript to avoid this limit but HTML
        // is simpler.
        if (_hover.empty()) sprintf_s(buffer, sizeof(buffer),
537
538
539
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
540
541
542
543
544
545
546
547
548
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


549
550
551
  void header(std::string& str)
  {
    if (is_html())
552
553
554
555
556
557
558
559
560
    {
      str.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\">");
      str.append("\n<HTML><HEAD>");
      str.append("\n<TITLE>ReForm DEB_out");
      str.append("</TITLE>");
      // javascript lib loads go here
      // stylesheet loads go here
      // within HEAD javascript goes here
      str.append("\n</HEAD>");
561
      if (is_white_on_black())
562
      {
563
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
564
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
565
566
567
568
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
569
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
570
571
572
573
574
575
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
576
      if (!file_name_.empty())
577
578
579
580
      {
        str.append(file_name_);
        str.append(" opened ");
      }
581
      add_time(str);
582
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
583
584
585
586
587
#ifdef DEB_SHOW_ON
      // Add link in deb_out.htm to deb_show.htm
      if (is_html())
        str.append(" :: <A HREF=\"ReForm_deb_show.htm\">ReForm_deb_show.htm</A><BR>\n");
#endif
588
589
      if (is_html()) str.append("<BR>");
      str.append("\n");
590
    }
591
   }
592
593
594
595
596
597

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
598
      std::string str("\n");
599
      if (!file_name_.empty()) str.append(file_name_);
600
      str.append(" Closed: ");
601
602
      add_time(str);
      stream() << str << "\n";
603
604
605
    }

    if (is_html())
606
      stream().print("\n</BODY></HTML>", false);
607
  }
608

609
610
611
612
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
613

614
615
616
  int flush()
  {
    int res = 0;
617
    if (!current_.empty())
618
    {
619
620
      const char* fname = file_name();
      if ((fname != nullptr) || file_is_open())
621
      {
622
        if (!file_is_open())
623
        {
624
625
626
          file_stream_.open(fname,
                            std::fstream::out | ((is_appended() && !is_first_flush()) ?
                                                 std::fstream::app  :  std::fstream::trunc));
627
628
629
630
631
        }

        if (file_stream_.is_open())
        {
          std::string hdr;
632
633
634
          if (!is_appended())
          {
            // Reoutput entire file
635
636
637
638
639
640
            header(hdr);
            output_.append(hdr);
            file_stream_ << output_;
          }
          else
          {
641
            if (is_first_flush())
642
643
            {
              header(hdr);
644
645
              if (is_retained())
                output_.append(hdr);
646
647
648
649
650
651
              file_stream_ << hdr;
            }
            ++num_flush_;
          }
          file_stream_ << current_;

652
          if (is_retained())
653
654
            output_.append(current_);

655
656
          current_.clear();
          if (!is_kept_open())
657
658
659
660
661
662
            file_stream_.close();
        } // endif fs.is_open
        else
          res = -1;
      }
    } // endif current empty
663
    return res;
664
665
  }  // endfunc flush

666
667
668
669
  // Use with extreme caution.
  const char * string_out() const
 { return current_.c_str(); }

670
671
672
673
674
675
676
  void close()
  {
    footer();
    flush();
  }


677
  void set_file_name(const char* _name)
678
  {
679
    file_name_ = _name ? _name : "";
680
    if (is_html_filename(_name))
681
      type_ = (Stream::StreamType)(type_ | Stream::StreamType::HTML);
682
  }
683
684

  void set_module_level(const char* const _module, const int _lev)
Ian Bell's avatar