libcsdbg  1.28
C++ exception (and generic) stack trace debug library
parser.cpp
Go to the documentation of this file.
1 #include "../include/parser.hpp"
2 #include "../include/util.hpp"
3 
10 namespace csdbg {
11 
12 /* Static member variable definition */
13 
14 parser *parser::m_default = NULL;
15 
16 style *parser::m_fallback = NULL;
17 
18 
27 {
28  try {
29  /* Create the default parser */
30  m_default = new parser;
31 
32  /*
33  * Equip the default parser with dictionaries for C++ keywords, intrinsic
34  * types and file extensions
35  */
36  string path("%s/etc/keywords.dict", util::prefix());
37  m_default->add_dictionary("keywords", path.cstr(), false);
38 
39  path.set("%s/etc/types.dict", util::prefix());
40  m_default->add_dictionary("types", path.cstr(), false);
41 
42  path.set("%s/etc/extensions.dict", util::prefix());
43  m_default->add_dictionary("extensions", path.cstr(), true);
44 
45  /*
46  * Create the default, fallback style. When a highlighter can't determine or
47  * create/obtain the correct style for a token, it uses the fallback
48  */
49  m_fallback = new style("fallback");
50 
51  /* Add styles for all kinds of trace tokens to the default parser */
52  style *s = m_fallback->clone();
53  s->set_name("delimiter");
54  m_default->add_style(s);
55 
56  s = m_fallback->clone();
57  s->set_name("number");
59  s->set_attr_enabled(style::BOLD, true);
60  m_default->add_style(s);
61 
62  s = m_fallback->clone();
63  s->set_name("keyword");
65  m_default->add_style(s);
66 
67  s = m_fallback->clone();
68  s->set_name("type");
70  s->set_attr_enabled(style::BOLD, true);
71  m_default->add_style(s);
72 
73  s = m_fallback->clone();
74  s->set_name("file");
76  m_default->add_style(s);
77 
78  s = m_fallback->clone();
79  s->set_name("scope");
81  m_default->add_style(s);
82 
83  s = m_fallback->clone();
84  s->set_name("function");
86  s->set_attr_enabled(style::BOLD, true);
87  m_default->add_style(s);
88 
89  util::dbg_info("default C++ stack trace parser/highlighter initialized");
90  return;
91  }
92 
93  catch (exception &x) {
94  std::cerr << x;
95  }
96 
97  catch (std::exception &x) {
98  std::cerr << x;
99  }
100 
101  exit(EXIT_FAILURE);
102 }
103 
104 
109 {
110  delete m_default;
111  delete m_fallback;
112  m_default = NULL;
113  m_fallback = NULL;
114 
115  util::dbg_info("default C++ stack trace parser/highlighter finalized");
116 }
117 
118 
128 std::ostream& operator<<(std::ostream &lval, const parser &rval)
129 {
130  util::lock();
131 
132  /* If an exception occurs, output its details instead of rval */
133  try {
134  string *buf = rval.highlight();
135  lval << *buf;
136  delete buf;
137  }
138 
139  catch (exception &x) {
140  lval << x;
141  }
142 
143  catch (std::exception &x) {
144  lval << x;
145  }
146 
147  util::unlock();
148  return lval;
149 }
150 
151 
158 try:
159 string(),
160 m_dictionaries(NULL),
161 m_styles(NULL)
162 {
163  try {
165  m_styles = new chain<style>;
166  }
167 
168  catch (...) {
169  delete m_dictionaries;
170  throw;
171  }
172 }
173 
174 catch (...) {
175  delete[] m_data;
176  m_data = NULL;
177  m_dictionaries = NULL;
178  m_styles = NULL;
179 }
180 
181 
190 try:
191 string(src),
192 m_dictionaries(NULL),
193 m_styles(NULL)
194 {
195  try {
196  m_dictionaries = src.m_dictionaries->clone();
197  m_styles = src.m_styles->clone();
198  }
199 
200  catch (...) {
201  delete m_dictionaries;
202  throw;
203  }
204 }
205 
206 catch (...) {
207  delete[] m_data;
208  m_data = NULL;
209  m_dictionaries = NULL;
210  m_styles = NULL;
211 }
212 
213 
218 {
219  delete m_dictionaries;
220  delete m_styles;
221  m_dictionaries = NULL;
222  m_styles = NULL;
223 }
224 
225 
233 inline parser* parser::clone() const
234 {
235  return new parser(*this);
236 }
237 
238 
245 {
246  return m_default;
247 }
248 
249 
256 {
257  return m_fallback;
258 }
259 
260 
271 {
272  if ( unlikely(this == &rval) )
273  return *this;
274 
275  /* Copy the buffer */
276  string::operator=(rval);
277 
279  *m_styles = *rval.m_styles;
280  return *this;
281 }
282 
283 
298 dictionary* parser::add_dictionary(const i8 *nm, const i8 *path, bool mode)
299 {
300  dictionary *retval = NULL;
301  try {
302  retval = new dictionary(nm, path, mode);
303  m_dictionaries->add(retval);
304  return retval;
305  }
306 
307  catch (...) {
308  delete retval;
309  throw;
310  }
311 }
312 
313 
325 {
326  m_dictionaries->add(dict);
327  return *this;
328 }
329 
330 
339 {
340  __D_ASSERT(nm != NULL);
341  if ( unlikely(nm == NULL) )
342  return *this;
343 
344  for (u32 i = 0, sz = m_dictionaries->size(); likely(i < sz); i++) {
345  dictionary *dict = m_dictionaries->at(i);
346  if ( unlikely(strcmp(dict->name(), nm) == 0) ) {
347  m_dictionaries->remove(i);
348  break;
349  }
350  }
351 
352  return *this;
353 }
354 
355 
362 {
363  m_dictionaries->clear();
364  return *this;
365 }
366 
367 
376 {
377  __D_ASSERT(nm != NULL);
378  if ( unlikely(nm == NULL) )
379  return NULL;
380 
381  for (u32 i = 0, sz = m_dictionaries->size(); likely(i < sz); i++) {
382  dictionary *dict = m_dictionaries->at(i);
383  if ( unlikely(strcmp(dict->name(), nm) == 0) )
384  return dict;
385  }
386 
387  return NULL;
388 }
389 
390 
399 {
400  chain<string> *retval = new chain<string>;
401  string *nm = NULL;
402 
403  try {
404  for (u32 i = 0, sz = m_dictionaries->size(); likely(i < sz); i++) {
405  nm = new string(m_dictionaries->at(i)->name());
406  retval->add(nm);
407  nm = NULL;
408  }
409 
410  return retval;
411  }
412 
413  catch (...) {
414  delete retval;
415  delete nm;
416  throw;
417  }
418 }
419 
420 
438 {
439  style *retval = NULL;
440  try {
441  retval = new style(nm, fg, bg, set);
442  m_styles->add(retval);
443  return retval;
444  }
445 
446  catch (...) {
447  delete retval;
448  throw;
449  }
450 }
451 
452 
464 {
465  m_styles->add(stl);
466  return *this;
467 }
468 
469 
478 {
479  __D_ASSERT(nm != NULL);
480  if ( unlikely(nm == NULL) )
481  return *this;
482 
483  for (u32 i = 0, sz = m_styles->size(); likely(i < sz); i++) {
484  style *stl = m_styles->at(i);
485  if ( unlikely(strcmp(stl->name(), nm) == 0) ) {
486  m_styles->remove(i);
487  break;
488  }
489  }
490 
491  return *this;
492 }
493 
494 
501 {
502  m_styles->clear();
503  return *this;
504 }
505 
506 
514 style* parser::get_style(const i8 *nm) const
515 {
516  __D_ASSERT(nm != NULL);
517  if ( unlikely(nm == NULL) )
518  return m_fallback;
519 
520  for (u32 i = 0, sz = m_styles->size(); likely(i < sz); i++) {
521  style *stl = m_styles->at(i);
522  if ( unlikely(strcmp(stl->name(), nm) == 0) )
523  return stl;
524  }
525 
526  return m_fallback;
527 }
528 
529 
538 {
539  chain<string> *retval = new chain<string>;
540  string *nm = NULL;
541 
542  try {
543  for (u32 i = 0, sz = m_styles->size(); likely(i < sz); i++) {
544  nm = new string(m_styles->at(i)->name());
545  retval->add(nm);
546  nm = NULL;
547  }
548 
549  return retval;
550  }
551 
552  catch (...) {
553  delete retval;
554  delete nm;
555  throw;
556  }
557 }
558 
559 
572 inline chain<string>* parser::parse(const i8 *syntax, bool icase) const
573 {
574  if ( likely(syntax == NULL) )
575  syntax = g_trace_syntax;
576 
577  return split(syntax, false, icase);
578 }
579 
580 
593 string* parser::highlight(const i8 *syntax, bool icase) const
594 {
595  const i8 *num = "^0x[0-9a-f]+$|^[0-9]+$";
596 
597  string *retval = new string;
598  chain<string> *tokens = NULL;
599 
600  /* If an exception occurs, release resources and rethrow it */
601  try {
602  /* Parse the buffer */
603  tokens = parse(syntax, icase);
604 
605  /* Highlight and append each token in the result */
606  for (u32 i = 0, sz = tokens->size(); likely(i < sz); i++) {
607  string *token = tokens->at(i);
608 
609  /* Select the style for the current token */
610  style *cur = NULL;
611  if ( likely(i % 2 == 1) )
612  cur = get_style("delimiter");
613 
614  else if ( unlikely(token->match(num, true)) )
615  cur = get_style("number");
616 
617  else if ( unlikely(lookup(*token, "keywords")) )
618  cur = get_style("keyword");
619 
620  else if ( unlikely(lookup(*token, "types")) )
621  cur = get_style("type");
622 
623  /* Ignore case for extension (regexp) lookups */
624  else if ( unlikely(lookup(*token, "extensions", true)) )
625  cur = get_style("file");
626 
627  /* Select the style based on the next delimiter */
628  else if ( likely(i < sz - 1) ) {
629  string *delim = tokens->at(i + 1);
630  i8 ch = delim->at(0);
631 
632  if ( unlikely(delim->cmp("::") == 0) )
633  cur = get_style("scope");
634 
635  else if ( unlikely(ch == '(' || ch == '<' || ch == '\r') )
636  cur = get_style("function");
637  }
638 
639  /* If the token was not identified (plain text) */
640  if ( unlikely(cur == NULL) )
641  cur = m_fallback;
642 
643  /* Apply the style to the token and append it to the result buffer */
644  cur->apply(*token);
645  retval->append(*token);
646  }
647 
648  delete tokens;
649  return retval;
650  }
651 
652  catch (...) {
653  delete tokens;
654  delete retval;
655  throw;
656  }
657 }
658 
659 
673 inline bool parser::lookup(const string &exp, const i8 *nm, bool icase) const
674 {
675  dictionary *dict = get_dictionary(nm);
676  if ( unlikely(dict == NULL) )
677  return false;
678 
679  return dict->lookup(exp, icase) != NULL;
680 }
681 
682 
694 const i8* parser::lookup(const string &exp, bool icase) const
695 {
696  for (u32 i = 0, sz = m_dictionaries->size(); likely(i < sz); i++) {
697  dictionary *dict = m_dictionaries->at(i);
698  if ( unlikely(dict->lookup(exp, icase) != NULL) )
699  return dict->name();
700  }
701 
702  return NULL;
703 }
704 
705 }
706 
static style * get_fallback_style()
Get the shared fallback style.
Definition: parser.cpp:255
#define HLT_NUMBER_FG
Highlighter color for numbers (any base)
Definition: config.hpp:309
#define HLT_KEYWORD_FG
Highlighter color for C++ keywords.
Definition: config.hpp:314
virtual style & set_attr_enabled(attrset_t, bool)
Enable/disable a set of text formatting attributes.
Definition: style.cpp:237
virtual string & append(const string &)
Append a string.
Definition: string.cpp:404
virtual chain< string > * get_style_names() const
Get all the registered style names.
Definition: parser.cpp:537
virtual parser & remove_dictionary(const i8 *)
Remove a dictionary, indexed by name.
Definition: parser.cpp:338
chain< dictionary > * m_dictionaries
Dictionary collection.
Definition: parser.hpp:42
virtual style * clone() const
Object virtual copy constructor.
Definition: style.cpp:74
virtual string & set(const i8 *,...)
Fill with a printf-style format C-string expanded with the values of a variable argument list...
Definition: string.cpp:271
virtual T * at(u32) const
Get the node data pointer at a chain offset.
Definition: chain.hpp:440
static const i8 g_trace_syntax[]
C++ stack trace syntax.
Definition: config.hpp:222
virtual style & set_fgcolor(color_t)
Set the foreground color.
Definition: style.cpp:158
#define HLT_FILE_FG
Highlighter color for C++ files.
Definition: config.hpp:324
virtual u32 size() const
Get the chain size (node count)
Definition: chain.hpp:276
Lightweight, templated, doubly-linked list (using XOR linking)
Definition: chain.hpp:33
A named collection of words (for syntax highlighters)
Definition: dictionary.hpp:32
char i8
8-bit signed integer
Definition: config.hpp:72
std::ostream & operator<<(std::ostream &, const std::exception &)
Stream insertion operator for std::exception objects.
Definition: exception.cpp:23
static parser * get_default()
Get the default (stack trace) parser.
Definition: parser.cpp:244
virtual chain< string > * get_dictionary_names() const
Get all the registered dictionary names.
Definition: parser.cpp:398
virtual parser * clone() const
Object virtual copy constructor.
Definition: parser.cpp:233
virtual string * highlight(const i8 *=NULL, bool=false) const
Highlight (escape) the current buffer using a custom syntax.
Definition: parser.cpp:593
virtual chain< string > * parse(const i8 *=NULL, bool=false) const
Parse the current buffer using a custom syntax.
Definition: parser.cpp:572
static parser * m_default
Default parser.
Definition: parser.hpp:35
Stack trace parser and syntax highlighter for VT100 terminals.
Definition: parser.hpp:29
virtual parser & remove_style(const i8 *)
Remove a style, indexed by name.
Definition: parser.cpp:477
virtual string & operator=(const string &)
Assignment operator.
Definition: string.cpp:317
virtual style & apply(string &) const
Apply the style to some text.
Definition: style.cpp:304
A set of formatting attributes for VT100 (and compatible) terminals.
Definition: style.hpp:20
#define HLT_TYPE_FG
Highlighter color for C++ intrinsic types.
Definition: config.hpp:319
virtual const i8 * name() const
Get the style name.
Definition: style.cpp:85
string(u32=0)
Object constructor.
Definition: string.cpp:127
u8 color_t
VT100 terminal color.
Definition: config.hpp:162
#define likely(expr)
Offer a hint (positive) to the pipeline branch predictor.
Definition: config.hpp:344
virtual style & set_name(const i8 *)
Set the style name.
Definition: style.cpp:134
virtual ~parser()
Object destructor.
Definition: parser.cpp:217
virtual style * add_style(const i8 *, color_t, color_t, attrset_t)
Add a style to the parser/highlighter.
Definition: parser.cpp:437
static const i8 * prefix()
Get the library installation prefix.
Definition: util.cpp:73
#define HLT_SCOPE_FG
Highlighter color for C++ ABI scopes.
Definition: config.hpp:329
virtual const i8 * name() const
Get the dictionary name.
Definition: dictionary.cpp:96
static void lock()
Lock the global access mutex.
Definition: util.cpp:397
static void unlock()
Unlock the global access mutex.
Definition: util.cpp:406
virtual parser & operator=(const parser &)
Assignment operator.
Definition: parser.cpp:270
#define HLT_FUNCTION_FG
Highlighter color for C++ function names.
Definition: config.hpp:334
virtual i32 cmp(const string &, bool=false) const
Compare to another string.
Definition: string.cpp:470
virtual i8 & at(u32)
Get/set the character at an offset.
Definition: string.cpp:248
virtual const i8 * cstr() const
Get the C-string equivalent.
Definition: string.cpp:211
virtual chain & add(T *)
Add a node to the chain.
Definition: chain.hpp:363
static void on_lib_unload() __attribute((destructor))
Library destructor.
Definition: parser.cpp:108
static void on_lib_load() __attribute((constructor))
Library constructor.
Definition: parser.cpp:26
Lightweight string buffer class (for ISO-8859-1 text)
Definition: string.hpp:36
virtual bool match(const string &, bool=false) const
Match against a POSIX extended regular expression.
Definition: string.cpp:490
virtual style * get_style(const i8 *) const
Get a style, indexed by name.
Definition: parser.cpp:514
virtual dictionary * add_dictionary(const i8 *, const i8 *, bool)
Add a dictionary to the parser.
Definition: parser.cpp:298
virtual dictionary * get_dictionary(const i8 *) const
Get a dictionary, indexed by name.
Definition: parser.cpp:375
unsigned int u32
32-bit unsigned integer
Definition: config.hpp:102
parser()
Object default constructor.
Definition: parser.cpp:157
virtual parser & remove_all_styles()
Remove all styles.
Definition: parser.cpp:500
virtual bool lookup(const string &, const i8 *, bool=false) const
Lookup an expression in one of the parser dictionaries.
Definition: parser.cpp:673
u16 attrset_t
VT100 attribute bitmask.
Definition: config.hpp:170
virtual parser & remove_all_dictionaries()
Remove all dictionaries.
Definition: parser.cpp:361
This class is a throwable with a textual description of an error.
Definition: exception.hpp:26
virtual chain< string > * split(const string &, bool=true, bool=false) const
Tokenize using a POSIX extended regular expression.
Definition: string.cpp:642
#define unlikely(expr)
Offer a hint (negative) to the pipeline branch predictor.
Definition: config.hpp:349
static void dbg_info(const i8 *,...)
Print an informational debug message on the standard error stream.
Definition: util.cpp:680
virtual const string * lookup(const string &, bool=false) const
Dictionary lookup.
Definition: dictionary.cpp:324
static style * m_fallback
Shared fallback style.
Definition: parser.hpp:37
#define __D_ASSERT(x)
Assertion macro.
Definition: config.hpp:268
chain< style > * m_styles
VT100 style collection.
Definition: parser.hpp:44