DebStream.cc 18.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// (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.

#include "DebOut.hh"
#include <string>
#include <fstream>
#include <time.h>
#include <vector>
#include <iostream>
17
#include <map>
18

19
#ifdef DEB_ON
20
21
22
23
24
25
26
27
28
29
30
31
32

#define LOCAL_PROC static

LOCAL_PROC bool is_html_filename(const char * const str)  
{
  const char * dot = strchr(str, '.');
  if (dot == nullptr) return false;
  ++dot;
  return ( !strncmp(dot, "htm", 3) ) || ( !strncmp(dot,"HTM",3) ) ;
}

namespace ReForm
{
33
  class FunctionCallSequence
34
35
36
37
38
  {
    std::string func_name_;
    std::vector<int> counts_; // These may not be sequential when multithreaded.

  public:
39
    FunctionCallSequence(const char * _func_name, const int _count) 
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
      : func_name_(_func_name)
    {
      counts_.push_back(_count);
    }
    ~FunctionCallSequence() {}

    bool add(const char * _func_name, const int _count)
    {
      if (func_name_ == _func_name)
      {
        counts_.push_back(_count);
        return true;
      }
      return false;
    }

    bool pop()
    {
      if (counts_.size() > 1 )
      {
        counts_.pop_back();
        return true;
      }
      counts_.clear();
      return false;
    }

    int number_calls() const 
    {
      if (!this) return 0;
      return (int)counts_.size();
    }

    int count(int i=0) const
    { 
      int num = number_calls();
      if (i < num) return counts_[num -1 - i];
      return -1;
    }

    const char * name() const { return func_name_.c_str();}

    // 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'))
      {
        char c = *ptr;
        if (c=='>') --cnt;
        if (cnt==0) str.append(1, c);
        if (c=='<') 
        {
          if (cnt==0) str.append(".");
          ++cnt;
        }
        ++ptr;
      }
    }

    // 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)
      {

        int num = number_calls();
        int prev = -2;
        int seq_cnt =0;
        for (int i=0; i < num; ++i)
        {
          int cnt = counts_[i];
          if (cnt != prev + 1)
          {
            char buffer[64];

            if (seq_cnt > 0)
            {
              str.append("-");
              sprintf_s(buffer, sizeof(buffer), "%i", prev);
              str.append(buffer);
            }
            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

  }; // endclass FunctionCallSequence


146
  class CallStack
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  {
    std::vector<FunctionCallSequence> calls_;
    int depth_;
  public:
    CallStack() : depth_(0) {}
    ~CallStack() {}

    void add(const char * _func_name, const int _count)
    {
      if (calls_.empty() || !calls_.back().add(_func_name, _count))
      {
        calls_.push_back( FunctionCallSequence(_func_name, _count) );
      }
      ++depth_;
    }

    void pop()
    {
      if (!calls_.back().pop())
        calls_.pop_back();
      --depth_;
    }

    const FunctionCallSequence * call( int _up =0)
    {
      int num = (int)calls_.size();
      if (_up < num) return &calls_[num - 1 - _up];
      return nullptr;
    }

    // Read a particular call stack element
    bool read(int _up, const char*& _funcname, int & _count)
    {
      const FunctionCallSequence * fcs = call(_up);
      if (fcs != nullptr)
      {
        _funcname = fcs->name();
        _count = fcs->count(0); // Return most recent deb_enter_count
        return true;
      }
      return false;
    }

    // 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)
      {
        if (i>0) _str.append("->");
        calls_[i].get(_str, true, _with_counts);
      }
      return num;
    }

    int depth() const { return depth_; }

  }; // endclass CallStack
 


209
class DebFile
210
211
{
private:
212
213
214
215
216
217
218
219
220
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
    module_stats(int _lev=0) : lev_(_lev), col_(0x808080) {}
    ~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;
  
  DebStream::StreamType type_;
226
227
  int lev_;
  int num_flush_;
228
  int priority_; // Last permission granted
229
230
231
232
233
234
235
236
237
238
239
240
  std::string current_;
  std::string output_;
  std::string file_name_;
  std::fstream file_stream_;
  std::string indent_string_;
  DebStream * deb_stream_;
  CallStack call_stack_;

public:

  CallStack& call_stack() { return call_stack_;}

241
242
243
244
  bool is_kept_open() const  { return 0 != (type_ & DebStream::StreamType::keep_open); }
  bool is_html() const  { return 0 != (type_ & DebStream::StreamType::html); }
  bool is_retained() const  { return 0 != (type_ & DebStream::StreamType::retain); }
  bool is_appended() const  { return 0 != (type_ & DebStream::StreamType::append); }
245
  // Only applies to HTML DEB_out
246
  bool is_white_on_black() const { return true; }
247
248

  bool file_is_open() const { return file_stream_.is_open(); }
249
  int priority() const { return priority_; }
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

  bool fork_to_cout() { return false; }
  bool fork_to_cerr() { return true; }


  const char * file_name() const
  {
   if ( this && (!file_name_.empty()) ) return file_name_.c_str();
    return nullptr;
  }
  
  void clear()
  {
    current_.clear();
    output_.clear();
    file_name_.clear();
  }
267
  
268
269
270
271
272
273
274
  char prev_char() const
  {
    if (!current_.empty()) return current_.back();
    if (!output_.empty()) return output_.back();
    return '\0';
  }

275
276
277
278
279
280
281
282
283
284
  void indent(int _depth)
  {
    for (int i=0; i < _depth; ++i)
    {
    if (is_html()) current_.append("-");
    //else current_.append(" ");
    }
  }

  void line_break(bool _with_indent = true)
285
286
287
288
289
  {
    _with_indent;
    if ( is_html() ) current_.append("<br>"); // Don't bother with matching </br>
    current_.append("\n", 1);

290
    if (_with_indent)
291
    {
292
      indent(call_stack().depth() );
293
    }
294

295
  }
296
      
297
298
  void set_level(int _lev) { lev_ = _lev; }

299
300
301
302
303
304
  void print_direct(const std::string & _s)
  {
    current_.append(_s);
  }


305
306
307
308
309
310
311
312
313
314
315
316
317
  void print(const char _c)
  {
    if (_c == '\n') line_break();
    else current_.append(&_c, 1);
  }

  void print(const char * const _s, bool _fork = true)
  {
    if (_s != nullptr)
    {
      for (int i=0; ;++i)
      {
        const char c = _s[i];
318
        if (c=='\0') break;
319
320
321
322
        print(c);
      }
      if (_fork)
      {
323
324
325
326
        if (fork_to_cout())
          std::cout << _s;
        if (fork_to_cerr())
          std::cerr << _s;
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
      }

    } 
  }

  void print(int _i)
  {
    char buffer[64];
    sprintf_s(buffer, sizeof(buffer), "%i", _i);
    print(buffer);
  }
  
  void print(double _d)
  {
    char buffer[64];
    sprintf_s(buffer, sizeof(buffer), "%.17g", _d);
    print(buffer);
  }

346
347
348
349
350
351
352
353
354
355
356
357
358
359
  void print(const DebCommand& _co)
  {
    switch(_co.com()) {
    case DebCommand::end : 
      if (is_html()) print("</FONT>", false);
      break;
    case DebCommand::end_lf :
      if (is_html()) print("</FONT>", false);
      line_break();
      break;
    }
  }


360
  DebStream & stream() { return *deb_stream_; }
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381

  // Append current asctime to given string 
  bool add_time(std::string & str)
  {
    time_t rawtime;
    time ( &rawtime );
    struct tm timeinfo;
    errno_t err = localtime_s( &timeinfo, &rawtime );
    if (err == 0)
    {
      char buffer[256];
      err = asctime_s(buffer, sizeof(buffer), &timeinfo);
      if (err == 0)
      {
        str.append( buffer );
        return true;
      }
    }
    return false;
  }

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434

#if 0
  bool hover(std::string & _str, const std::string & _hover, const bool _open)
  {
    if (is_html())
    {
      char buffer[1024];
      if (_open)  sprintf_s(buffer, sizeof(buffer), 
        "<span title=\"%s\">", _hover.c_str());
      else sprintf_s(buffer, sizeof(buffer), "</span>");
      _str.append(buffer);
      return true;
    }
    return false;
  }
#endif

  bool anchor(std::string & _str, const int _id, const char * _tag, const bool _open)
  {
    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;
  }

  bool link_to(std::string & _str, const int _id, const char * _tag, const std::string & _hover, const bool _open)
  {
    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),
          "<A href=\"#%08X_%s\">", _id, _tag);
        else sprintf_s(buffer, sizeof(buffer), 
          "<A href=\"#%08X_%s\" title=\"%s\">", _id, _tag, _hover.c_str());
      }
      else sprintf_s(buffer, sizeof(buffer), "</A>");
      _str.append(buffer);
      return true;
    }
    return false;
  }


435
436
437
438
439
440
441
442
443
444
445
446
  void header(std::string & str)
  { 
    if ( is_html() )
    {
      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>");
447
      if ( is_white_on_black() )
448
449
450
451
452
453
454
455
456
457
458
459
      {
        str.append( "\n<BODY BGCOLOR=\"#000000\" TEXT=\"#FFFFFF\" LINK=\"#%00FFFF\" VLINK=\"#FFFF00\" >");
      }
      else
      {
        str.append("\n<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" LINK=\"#%FF0000\" VLINK=\"#0000FF\" >");
      }
      str.append("\n");
    } // endif is_html
    bool date_header = true;
    if (date_header)
    {
460
      add_time(str);
461
462
463
      str.append( "[ Build: " __TIME__  " " __DATE__  "] ");
      if (is_html()) str.append("<BR>");
      str.append("\n");
464
465
466
467
468
469
470
471
    }
  }

  void footer()
  {
    bool date_footer = true;
    if (date_footer)
    {
472
473
474
      std::string str = " \n Closed: ";
      add_time(str);
      stream() << str << "\n";
475
476
477
478
    }

    if (is_html())
    {
479
      stream().print("\n</BODY></HTML>", false);
480
481
482
483
484
485
486
487
488
489
490
491
492
493
    }

  }
  bool is_first_flush()
  {
    return num_flush_ == 0 ;
  }
  
  int flush()
  {
    int res = 0;
    if ( !current_.empty() )
    {
      const char * fname = file_name();
494
      if ((fname != nullptr) || file_is_open() )
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
      {
        if ( !file_is_open() )
        {
          file_stream_.open( fname, 
            std::fstream::out | ( (is_appended() && !is_first_flush() ) ? 
                std::fstream::app  :  std::fstream::trunc ) );
        }

        if (file_stream_.is_open())
        {
          std::string hdr;
          if ( !is_appended() )
          { // Reoutput entire file
            header(hdr);
            output_.append(hdr);
            file_stream_ << output_;
          }
          else
          {
            if ( is_first_flush() )
            {
              header(hdr);
              if ( is_retained() )
                output_.append( hdr);
              file_stream_ << hdr;
            }
            ++num_flush_;
          }
          file_stream_ << current_;

          if ( is_retained() )
            output_.append(current_);

          current_.clear(); 
          if ( !is_kept_open() )
            file_stream_.close();
        } // endif fs.is_open
        else
        {
          res = -1;
        }
      }
    } // endif current empty
  return res;
  }  // endfunc flush

  void close()
  {
    footer();
    flush();
  }


  void set_file_name(const char * _name)
  {
    file_name_ = _name;
    if ( is_html_filename(_name) )
552
553
554
    {
      type_ = (DebStream::StreamType)(type_ | DebStream::StreamType::html);
    }
555
556

  }
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  
  void set_module_level(const char * const _module, const int _lev)
  {
    std::pair<module_map_itr, bool> ins =  module_map_.insert(
      std::pair<std::string, module_stats>(_module, module_stats(_lev)) );
    if (!ins.second) ins.first->second.lev_ = _lev;
  }

  int get_module_level(const char * const _module) const
  {
    const_module_map_itr it = module_map_.find(std::string(_module));
    if (it == module_map_.end()) return lev_;
    return it->second.lev_;
  }


  void set_module_color(const char * const _module, const int _col)
  {
    std::pair<module_map_itr, bool> ins =  module_map_.insert(
      std::pair<std::string, module_stats>(_module, module_stats(lev_)) );
    ins.first->second.col_ = _col;
  }

  int get_module_color(const char * const _module) const
  {
    const_module_map_itr it = module_map_.find(std::string(_module));
    if (it == module_map_.end()) 
      {
        if (is_white_on_black() ) return 0xFFFFFF;
        else return 0x000000;
      }
    return it->second.col_;
  }
  
  int permission(const int _lev, const int _warn, const char * const _module)
  {
    _warn;
    int lev = get_module_level(_module);
    lev -= _lev;
    if (lev <0) lev = 0;
    if (lev > 0) priority_ = lev;
    return lev;
  }


  bool at_line_start() 
  {
    char prev = prev_char();
    return (prev=='\n') || (prev=='\0');
  }


  DebFile(
    DebStream* _deb_stream,
    DebStream::StreamType _type = (DebStream::StreamType) (DebStream::append | DebStream::retain), 
    const char * _file_name = nullptr) :
  type_(_type), lev_(5), deb_stream_(_deb_stream), num_flush_(0)
  {
    set_file_name(_file_name);
    indent_string_ = ".";
    set_module_color("PARA", 0xFF0000);
    set_module_color("SOLV", 0x00FF00);
    set_module_color("NSLV", 0xFFFF00);
    set_module_color("CFLD", 0x0000FF);
    set_module_color("CURV", 0x00FFFF);
  }

  ~DebFile() {}

 
}; // endclass DebFile
628
629
630
631
632

// =====================================
//      DebEnter member funcs
// =====================================

633
DebEnter::DebEnter(const char * const _funcname, const int _count, const char * const _module) 
634
{
635
636
637
  // TODO: this might have to be atomic
  static int id_cnt=0;
  module_ = _module;
638
  deb_outs_ = 0;
639
640
  id_ = ++id_cnt;
  stream(0, false).dfile()->call_stack().add(_funcname, _count);
641
642
643
644
}

DebEnter::~DebEnter() 
{
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
  DebFile * impl = stream(0, false).dfile();
  impl->call_stack().pop();
  std::string str;
  if ((deb_outs_ > 0) && impl->anchor(str, id_, "exit", true))
  {
    std::string cstk;
    impl->call_stack().get(cstk);
    if (impl->link_to(str, id_, "enter", cstk, true))
    {
     

      str.append("&lt;");
      
      impl->link_to(str, id_, "enter", cstk,  false);
    }
    impl->anchor(str, id_, "exit", false);
    impl->print_direct(str);
  }
663
664
}

665
DebStream& DebEnter::stream(const int _warn, const bool _print)
666
667
{
  DebStream & ds = ReForm::DebStream::get_global(_warn);
668
669
670
  DebFile * impl = ds.dfile();

  if ( _print)
671
  {
672
673
674
675
676
677
678
679
680
    if (impl->is_html())
    {
      int col = impl->get_module_color(module_);
      char buffer[256];
      sprintf_s(buffer, sizeof(buffer),"<FONT COLOR=\"#%06X\" SIZE=%i>", 
        col, impl->priority()+1 );
      ds.print(buffer, false);
    }

681
682
683
    if ( deb_outs_ < 1)
    {
      // First DEB_out in this function so output callstack, and flush.
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
      std::string str;
      std::string cstk;
      bool is_html = impl->is_html();
      if (is_html)
        {
        str.append("<FONT SIZE=2>");
        impl->anchor(str, id_,"enter", true);
        }
      str.append("***");
      if (is_html)
      {
        impl->anchor(str, id_,"enter", false);
        impl->link_to(str, id_,"exit", cstk, true);
        str.append("&gt;");
        impl->link_to(str, id_,"exit", cstk, false);
      }
      impl->call_stack().get(str);
      if (is_html) str.append("</FONT>");
702
703
      str.append("\n");
      ds.print(str.c_str(), false); // Don't fork callstack to cerr etc.
704
      ds.dfile()->flush();
705
706
707
    }
    else
    {
708
      if ( impl->at_line_start() )
709
      {
710
        impl->indent(  impl->call_stack().depth() );
711
712
713
714
715
716
717
718
      }
    }
    ++deb_outs_;
  }

  return ds;
}

719
int DebEnter::permission(const int _lev, const int _warn)
720
{ 
721
722
  int res = ReForm::DebStream::get_global().dfile()->permission(_lev, _warn, module_);
  return res;
723
724
725
726
727
728
729
730
731
732
}

// =====================================
//        DebStream member funcs
// =====================================


DebStream::DebStream(const char * _file_name, StreamType _type)
{
  // NB. disable DEB_out over this new if new contains DEB_out
733
  dfile_ = new DebFile(this, _type, _file_name);
734
735
736
737
}

DebStream::~DebStream()
{
738
  if ( dfile_ != nullptr)
739
  {
740
741
    dfile()->close();
    dfile()->clear();
742
    // NB. disable DEB_out over this delete if delete contains DEB_out
743
744
    delete dfile_;
    dfile_ = nullptr;
745
746
747
748
749
  }
}

DebStream& DebStream::print(const int _i)
{
750
  dfile_->print(_i);
751
752
753
754
755
  return *this;
};

DebStream& DebStream::print(const double _d)
{
756
  dfile_->print(_d);
757
758
759
760
761
  return *this;
};

DebStream& DebStream::print(const char * _s, bool _fork)
{
762
  dfile_->print(_s, _fork);
763
764
765
766
767
  return *this;
};

DebStream& DebStream::print(const char _c)
{
768
769
770
771
772
773
774
  dfile_->print(_c);
  return *this;
};

DebStream& DebStream::print(const DebCommand & _co)
{
  dfile_->print(_co);
775
776
777
778
779
780
781
782
  return *this;
};

DebStream & DebStream::get_global(int _warn)
{
  _warn; 
  // TODO: Replace with a Singleton?? ThreadArray??
  static DebStream g_ds__("reform_deb_out.txt");
783
  //static DebStream g_ds__("reform_deb_out.htm");
784
785
786
787
788
789
790
791
792
793
794
795
796
  return g_ds__;
}

DebStream& operator<<(DebStream& ds, const int i ) { return ds.print(i);}
DebStream& operator<<(DebStream& ds, const double d ) { return ds.print(d);}
DebStream& operator<<(DebStream& ds, const char * const s ) { return ds.print(s);}
DebStream& operator<<(DebStream& ds, const char c) { return ds.print(c);}

DebStream& operator<<(DebStream& ds, const size_t i ) { return ds.print((int)i);}
DebStream& operator<<(DebStream& ds, const unsigned int i ) { return ds.print((int)i);}
DebStream& operator<<(DebStream& ds, const float f ) { return ds.print((double)f);}
DebStream& operator<<(DebStream& ds, const std::string& s ) { return ds.print(s.c_str());}

797
798
799
DebStream& operator<<(DebStream& ds, const DebCommand& co ) { return ds.print(co); }


800
801
802
}//namespace


803
#endif // DEB_ON