DebStream.cc 24.2 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
#include "Base/Utils/ThrowError.hh"
13
14
15

#ifdef DEB_ON

16
17
18
19
20
#include <string>
#include <fstream>
#include <time.h>
#include <vector>
#include <iostream>
21
#include <map>
22
23
24
25
#include <memory>
#include <list>
#include <map>
#include <sstream>
26

27
28
namespace { // LOCAL_PROC
bool is_html_filename(const char* const str)
29
{
30
  if (str == nullptr) return false;
31
  const char* dot = strrchr(str, '.');
32
33
  if (dot == nullptr) return false;
  ++dot;
34
  return (!strncmp(dot, "htm", 3)) || (!strncmp(dot, "HTM", 3)) ;
35
}
36
}
37

38
namespace Debug {
39
40

class FunctionCallSequence
41
{
42
  std::string func_name_;
43
  std::vector<Enter*> debs_;  // These may not have sequential counts when multithreaded.
44
45

public:
46
  FunctionCallSequence(const char* _func_name, Enter* _deb)
47
    : func_name_(_func_name)
48
  {
49
50
51
    debs_.push_back(_deb);
  }
  ~FunctionCallSequence() {}
52

53
  bool add(const char* _func_name, Enter* _deb)
54
55
  {
    if (func_name_ == _func_name)
56
    {
57
      debs_.push_back(_deb);
58
      return true;
59
    }
60
61
    return false;
  }
62

63
64
65
  bool pop()
  {
    if (debs_.size() > 1)
66
    {
67
68
      debs_.pop_back();
      return true;
69
    }
70
71
72
    debs_.clear();
    return false;
  }
73

74
75
76
77
78
  int number_calls() const
  {
    if (!this) return 0;
    return (int)debs_.size();
  }
79

80
81
82
83
84
85
  int count(int i = 0) const
  {
    int num = number_calls();
    if (i < num) return debs_[num - 1 - i]->count_;
    return -1;
  }
86

87
88
89
90
  const char* name() const
  {
    return func_name_.c_str();
  }
91

92
93
94
95
96
97
  // 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'))
98
    {
99
100
101
102
      char c = *ptr;
      if (c == '>') --cnt;
      if (cnt == 0) str.append(1, c);
      if (c == '<')
103
      {
104
105
        if (cnt == 0) str.append(".");
        ++cnt;
106
      }
107
      ++ptr;
108
    }
109
  }
110

111
112
113
114
115
116
117
  // 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)
118
    {
119
120
121
122
123
124
125
      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)
126
        {
127
          char buffer[64];
128

129
130
131
132
          if (seq_cnt > 0)
          {
            _str.append("-");
            sprintf_s(buffer, sizeof(buffer), "%i", prev);
133
            _str.append(buffer);
134
          }
135
136
137
138
139
140
141
142
143
144
145
146
147
148
          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
149

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

152
}; // endclass FunctionCallSequence
153
154


155
156
157
158
159
160
161
class CallStack
{
  std::vector<FunctionCallSequence> calls_;
  int depth_;
public:
  CallStack() : depth_(0) {}
  ~CallStack() {}
162

163
  void add(const char* _func_name, Enter* _deb)
164
165
166
167
168
  {
    if (calls_.empty() || !calls_.back().add(_func_name, _deb))
      calls_.push_back(FunctionCallSequence(_func_name, _deb));
    ++depth_;
  }
169

170
171
172
173
174
175
  void pop()
  {
    if (!calls_.back().pop())
      calls_.pop_back();
    --depth_;
  }
176

177
  const FunctionCallSequence* call(int _up = 0) const
178
179
180
181
182
  {
    int num = (int)calls_.size();
    if (_up < num) return &calls_[num - 1 - _up];
    return nullptr;
  }
183

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

197
198
199
200
201
202
  // 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)
203
    {
204
205
      if (i > 0) _str.append("->");
      calls_[i].get(_str, true, _with_counts);
206
    }
207
208
    return num;
  }
209

210
211
212
213
  int depth() const
  {
    return depth_;
  }
214

215
  bool get_indent(std::string& _str, File* _dfile, const bool is_html);
216
}; // endclass CallStack
217

218
class File
219
220
{
private:
221
222
223
224
225
  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
226
    module_stats(int _lev = 0) : lev_(_lev), col_(0x808080) {}
227
228
    ~module_stats() {}
  };
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  
  // We use this data to decide the debug level of a function in a file.
  class FilterLevelSelector
  {
  public:
    void add_file_string(const std::string& _str) { file_selct_strngs_.push_back(_str); }
    void add_func_string(const std::string& _str) { func_selct_strngs_.push_back(_str); }

    bool select_file(const char* _flnm) const
    {
      std::string flnm(_flnm);
      const std::string root_dir("ReForm");
      auto pos = flnm.rfind(root_dir);
      if (pos != std::string::npos)
        flnm = flnm.substr(pos + root_dir.size());
      return search(flnm, file_selct_strngs_);
    }

    bool select_function(const char* _func) const
    {
      return search(_func, func_selct_strngs_);
    }
251

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  private:
    static bool search(const std::string& _name,
      const std::list<std::string>& _sel_strings)
    {
      for (const auto& sel : _sel_strings)
      {
        if (_name.find(sel) != std::string::npos)
          return true;
      }
      return false;
    }

  private:
    std::list<std::string> file_selct_strngs_; // list of strings to be found inside the file name.
    std::list<std::string> func_selct_strngs_; // list of strings to be found inside the function name.
  };
268

269
  Stream::StreamType type_;
270
271
  int lev_;
  int num_flush_;
272
  int priority_; // Last permission granted
273
  int indent_size_;
274

275
276
  std::map<int, FilterLevelSelector> level_selc_map_; // A map filter_level ==> filter_selector

277
278
  bool at_line_start_;

279
280
281
282
  std::string current_;
  std::string output_;
  std::string file_name_;
  std::fstream file_stream_;
283
284

  std::string double_format_;
285
  //std::string indent_string_;
286
  Stream* deb_stream_;
287
288
289
290
  CallStack call_stack_;

public:

291
292
293
294
  CallStack& call_stack()
  {
    return call_stack_;
  }
295

296
297
  bool is_kept_open() const
  {
298
    return 0 != (type_ & Stream::StreamType::KEEP_OPEN);
299
300
301
  }
  bool is_html() const
  {
302
    return 0 != (type_ & Stream::StreamType::HTML);
303
304
305
  }
  bool is_retained() const
  {
306
    return 0 != (type_ & Stream::StreamType::RETAIN);
307
308
309
  }
  bool is_appended() const
  {
310
    return 0 != (type_ & Stream::StreamType::APPEND);
311
  }
312
  // Only applies to HTML DEB_out
313
314
315
316
317
318
319
320
  bool is_white_on_black() const
  {
    return true;
  }
  int indent_size()
  {
    return indent_size_;
  }
321

322
323
324
325
326
327
328
329
  bool file_is_open() const
  {
    return file_stream_.is_open();
  }
  int priority() const
  {
    return priority_;
  }
330

331
332
333
334
335
336
337
338
  bool fork_to_cout()
  {
    return false;
  }
  bool fork_to_cerr()
  {
    return true;
  }
339
340


341
  const char* file_name() const
342
  {
343
    if (this && (!file_name_.empty())) return file_name_.c_str();
344
345
    return nullptr;
  }
346

347
348
349
350
351
352
  void clear()
  {
    current_.clear();
    output_.clear();
    file_name_.clear();
  }
353

354
355
356
357
358
359
360
  char prev_char() const
  {
    if (!current_.empty()) return current_.back();
    if (!output_.empty()) return output_.back();
    return '\0';
  }

361

362
  void indent(bool _full_text)
363
  {
364
    std::string str;
365
    if (call_stack().get_indent(str, this,  _full_text && is_html()))
366
      current_.append(str);
367
368
  }

369
  void line_break(bool _with_indent = false)
370
  {
371
    if (is_html()) current_.append("<br>");   // Don't bother with matching </br>
372
373
    current_.append("\n", 1);

374
375
    if (_with_indent) indent(false);
    else at_line_start_ = true;
376
377
  }

378
379
380
381
382
383
  void set_level(int _lev)
  {
    lev_ = _lev;
  }

  void print_direct(const std::string& _s)
384
385
386
387
  {
    current_.append(_s);
  }

388
389
  void print(const char _c)
  {
390
    if (_c == '\n')
391
392
393
394
395
    {
      line_break();
      return;
    }

396
397
    if (at_line_start_)
    {
398
      indent(true);
399
400
401
402
      at_line_start_ = false;
    }


403
404
405
    if (is_html())
    {
      // translate the esoteric characters used in IGM DEB_out
406

407
      if (_c == '')  // -62
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
        return;

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

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

425
426
427
428
429
  void print_to_ostream(const char* const _s, std::ostream& os)
  {
    os << _s;
  }

430
  void print(const char* const _s, bool _fork = true)
431
432
433
  {
    if (_s != nullptr)
    {
434
      for (int i = 0; ; ++i)
435
436
      {
        const char c = _s[i];
437
        if (c == '\0') break;
438
439
440
441
        print(c);
      }
      if (_fork)
      {
442
        if (fork_to_cout())
443
          print_to_ostream(_s, std::cout);
444
        if (fork_to_cerr())
445
          print_to_ostream(_s, std::cerr);
446
      }
447
    }
448
449
450
451
452
453
454
455
  }

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

457

458
  const char* double_format() const
459
460
461
462
463
464
  {
    if (double_format_.empty())
      return "%.17g";
    return double_format_.c_str();
  }

465
  void set_double_format(const char* const str)
466
  {
467
    if (str == nullptr)
468
469
470
471
472
473
      double_format_.clear();
    else
      double_format_ = str;
  }


474
475
476
  void print(double _d)
  {
    char buffer[64];
477
    sprintf_s(buffer, sizeof(buffer), double_format(), _d);
478
479
480
    print(buffer);
  }

481
  void print(const Command& _co)
482
  {
483
484
    switch (_co.com())
    {
485
    case Command::END :
486
      if (is_html()) print_direct("</FONT>");
487
      break;
488
    case Command::END_ERR :
489
490
491
    // Powerdown DEB_error font
    // if (is_html()) print_direct("</BLINK>");
    // fall through
492
    case Command::END_LF :
493
      if (is_html()) print_direct("</FONT>");
494
495
496
497
498
499
500
501
502
      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";

503
504
505
506
      break;
    }
  }

507
  Stream& stream()
508
509
510
  {
    return *deb_stream_;
  }
511

512
513
  // Append current asctime to given string
  bool add_time(std::string& str)
514
515
  {
    time_t rawtime;
516
    time(&rawtime);
517
    struct tm timeinfo;
518
    errno_t err = localtime_s(&timeinfo, &rawtime);
519
520
521
522
523
524
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
525
        str.append(buffer);
526
527
528
529
530
531
        return true;
      }
    }
    return false;
  }

532

533
#if 1
534
  bool hover(std::string& _str, const std::string& _hover, const bool _open)
535
536
537
538
  {
    if (is_html())
    {
      char buffer[1024];
539
540
      if (_open)  sprintf_s(buffer, sizeof(buffer),
                              "<span title=\"%s\">", _hover.c_str());
541
542
543
544
545
546
547
548
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

549
  bool anchor(std::string& _str, const int _id, const char* _tag, const bool _open)
550
551
552
553
554
555
556
557
558
559
560
561
  {
    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;
  }

562
  bool link_to(std::string& _str, const int _id, const char* _tag, const std::string& _hover, const bool _open)
563
564
565
566
567
568
569
570
571
572
  {
    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),
573
574
575
                                        "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer),
                         "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
576
577
578
579
580
581
582
583
584
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


585
586
587
  void header(std::string& str)
  {
    if (is_html())
588
589
590
591
592
593
594
595
596
    {
      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>");
597
      if (is_white_on_black())
598
      {
599
        str.append("\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
600
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\"  >");
601
602
603
604
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
605
        //str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" >");
606
607
608
609
610
611
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
612
      if (!file_name_.empty())
613
614
615
616
      {
        str.append(file_name_);
        str.append(" opened ");
      }
617
      add_time(str);
618
      str.append("[ Build: " __TIME__  " " __DATE__  "] ");
619
620
      if (is_html()) str.append("<BR>");
      str.append("\n");
621
    }
622
   }
623
624
625
626
627
628

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
629
      std::string str("\n");
630
      if (!file_name_.empty()) str.append(file_name_);
631
      str.append(" Closed: ");
632
633
      add_time(str);
      stream() << str << "\n";
634
635
636
    }

    if (is_html())
637
      stream().print("\n</BODY></HTML>", false);
638
  }
639

640
641
642
643
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
644

645
646
647
  int flush()
  {
    int res = 0;
648
    if (!current_.empty())
649
    {
650
651
      const char* fname = file_name();
      if ((fname != nullptr) || file_is_open())
652
      {
653
        if (!file_is_open())
654
        {
655
656
657
          file_stream_.open(fname,
                            std::fstream::out | ((is_appended() && !is_first_flush()) ?
                                                 std::fstream::app  :  std::fstream::trunc));
658
659
660
661
662
        }

        if (file_stream_.is_open())
        {
          std::string hdr;
663
664
665
          if (!is_appended())
          {
            // Reoutput entire file
666
667
668
669
670
671
            header(hdr);
            output_.append(hdr);
            file_stream_ << output_;
          }
          else
          {