Panda3D
notify.cxx
1 // Filename: notify.cxx
2 // Created by: drose (28Feb00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pnotify.h"
16 #include "notifyCategory.h"
17 #include "configPageManager.h"
18 #include "configVariableFilename.h"
19 #include "configVariableBool.h"
20 #include "filename.h"
21 #include "config_prc.h"
22 
23 #include <ctype.h>
24 
25 #ifdef BUILD_IPHONE
26 #include <fcntl.h>
27 #endif
28 
29 #ifdef ANDROID
30 #include <android/log.h>
31 #endif
32 
33 Notify *Notify::_global_ptr = (Notify *)NULL;
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: Notify::Constructor
37 // Access: Public
38 // Description:
39 ////////////////////////////////////////////////////////////////////
40 Notify::
41 Notify() {
42  _ostream_ptr = &cerr;
43  _owns_ostream_ptr = false;
44  _null_ostream_ptr = new fstream;
45 
46  _assert_handler = (AssertHandler *)NULL;
47  _assert_failed = false;
48 }
49 
50 ////////////////////////////////////////////////////////////////////
51 // Function: Notify::Destructor
52 // Access: Public
53 // Description:
54 ////////////////////////////////////////////////////////////////////
55 Notify::
56 ~Notify() {
57  if (_owns_ostream_ptr) {
58  delete _ostream_ptr;
59  }
60  delete _null_ostream_ptr;
61 }
62 
63 ////////////////////////////////////////////////////////////////////
64 // Function: Notify::set_ostream_ptr
65 // Access: Public
66 // Description: Changes the ostream that all subsequent Notify
67 // messages will be written to. If the previous ostream
68 // was set with delete_later = true, this will delete
69 // the previous ostream. If ostream_ptr is NULL, this
70 // resets the default to cerr.
71 ////////////////////////////////////////////////////////////////////
72 void Notify::
73 set_ostream_ptr(ostream *ostream_ptr, bool delete_later) {
74  if (_owns_ostream_ptr && ostream_ptr != _ostream_ptr) {
75  delete _ostream_ptr;
76  }
77 
78  if (ostream_ptr == (ostream *)NULL) {
79  _ostream_ptr = &cerr;
80  _owns_ostream_ptr = false;
81  } else {
82  _ostream_ptr = ostream_ptr;
83  _owns_ostream_ptr = delete_later;
84  }
85 }
86 
87 ////////////////////////////////////////////////////////////////////
88 // Function: Notify::get_ostream_ptr
89 // Access: Public
90 // Description: Returns the system-wide ostream for all Notify
91 // messages.
92 ////////////////////////////////////////////////////////////////////
93 ostream *Notify::
94 get_ostream_ptr() const {
95  return _ostream_ptr;
96 }
97 
98 ////////////////////////////////////////////////////////////////////
99 // Function: Notify::get_literal_flag
100 // Access: Public
101 // Description: Returns a flag that may be set on the Notify stream
102 // via setf() that, when set, enables "literal" mode,
103 // which means the Notify stream will not attempt to do
104 // any fancy formatting (like word-wrapping).
105 //
106 // Notify does not itself respect this flag; this is
107 // left up to the ostream that Notify writes to. Note
108 // that Notify just maps to cerr by default, in which
109 // case this does nothing. But the flag is available in
110 // case any extended types want to make use of it.
111 ////////////////////////////////////////////////////////////////////
112 ios_fmtflags Notify::
114  static bool got_flag = false;
115  static ios_fmtflags flag;
116 
117  if (!got_flag) {
118 #ifndef PHAVE_IOSTREAM
119  flag = ios::bitalloc();
120 #else
121  // We lost bitalloc in the new iostream? Ok, this feature will
122  // just be disabled for now. No big deal.
123  flag = (ios_fmtflags)0;
124 #endif
125  got_flag = true;
126  }
127 
128  return flag;
129 }
130 
131 ////////////////////////////////////////////////////////////////////
132 // Function: Notify::set_assert_handler
133 // Access: Public
134 // Description: Sets a pointer to a C function that will be called
135 // when an assertion test fails. This function may
136 // decide what to do when that happens: it may choose to
137 // abort or return. If it returns, it should return
138 // true to indicate that the assertion should be
139 // respected (and the calling function should return out
140 // of its block of code), or false to indicate that the
141 // assertion should be completely ignored.
142 //
143 // If an assert handler is installed, it completely
144 // replaces the default behavior of nassertr() and
145 // nassertv().
146 ////////////////////////////////////////////////////////////////////
147 void Notify::
148 set_assert_handler(Notify::AssertHandler *assert_handler) {
149  _assert_handler = assert_handler;
150 }
151 
152 ////////////////////////////////////////////////////////////////////
153 // Function: Notify::clear_assert_handler
154 // Access: Public
155 // Description: Removes the installed assert handler and restores
156 // default behavior of nassertr() and nassertv().
157 ////////////////////////////////////////////////////////////////////
158 void Notify::
160  _assert_handler = (AssertHandler *)NULL;
161 }
162 
163 ////////////////////////////////////////////////////////////////////
164 // Function: Notify::has_assert_handler
165 // Access: Public
166 // Description: Returns true if a user assert handler has been
167 // installed, false otherwise.
168 ////////////////////////////////////////////////////////////////////
169 bool Notify::
171  return (_assert_handler != (AssertHandler *)NULL);
172 }
173 
174 ////////////////////////////////////////////////////////////////////
175 // Function: Notify::get_assert_handler
176 // Access: Public
177 // Description: Returns a pointer to the user-installed assert
178 // handler, if one was installed, or NULL otherwise.
179 ////////////////////////////////////////////////////////////////////
180 Notify::AssertHandler *Notify::
182  return _assert_handler;
183 }
184 
185 ////////////////////////////////////////////////////////////////////
186 // Function: Notify::get_top_category
187 // Access: Public
188 // Description: Returns the topmost Category in the hierarchy. This
189 // may be used to traverse the hierarchy of available
190 // Categories.
191 ////////////////////////////////////////////////////////////////////
194  return get_category(string());
195 }
196 
197 ////////////////////////////////////////////////////////////////////
198 // Function: Notify::get_category
199 // Access: Public
200 // Description: Finds or creates a new Category given the basename of
201 // the category and its parent in the category
202 // hierarchy. The parent pointer may be NULL to
203 // indicate this is a top-level Category.
204 ////////////////////////////////////////////////////////////////////
206 get_category(const string &basename, NotifyCategory *parent_category) {
207  // The string should not contain colons.
208  nassertr(basename.find(':') == string::npos, (NotifyCategory *)NULL);
209 
210  string fullname;
211  if (parent_category != (NotifyCategory *)NULL) {
212  fullname = parent_category->get_fullname() + ":" + basename;
213  } else {
214  // The parent_category is NULL. If basename is empty, that means
215  // we refer to the very top-level category (with an empty
216  // fullname); otherwise, it's a new category just below that top
217  // level.
218  if (!basename.empty()) {
219  parent_category = get_top_category();
220  fullname = ":" + basename;
221  }
222  }
223 
224  pair<Categories::iterator, bool> result =
225  _categories.insert(Categories::value_type(fullname, (NotifyCategory *)NULL));
226 
227  bool inserted = result.second;
228  NotifyCategory *&category = (*result.first).second;
229 
230  if (inserted) {
231  // If we just inserted a new record, then we have to create a new
232  // Category pointer. Otherwise, there was already one created
233  // from before.
234  category = new NotifyCategory(fullname, basename, parent_category);
235  }
236 
237  return category;
238 }
239 
240 ////////////////////////////////////////////////////////////////////
241 // Function: Notify::get_category
242 // Access: Public
243 // Description: Finds or creates a new Category given the basename of
244 // the category and the fullname of its parent. This is
245 // another way to create a category when you don't have
246 // a pointer to its parent handy, but you know the name
247 // of its parent. If the parent Category does not
248 // already exist, it will be created.
249 ////////////////////////////////////////////////////////////////////
251 get_category(const string &basename, const string &parent_fullname) {
252  return get_category(basename, get_category(parent_fullname));
253 }
254 
255 ////////////////////////////////////////////////////////////////////
256 // Function: Notify::get_category
257 // Access: Public
258 // Description: Finds or creates a new Category given the fullname of
259 // the Category. This name should be a sequence of
260 // colon-separated names of parent Categories, ending in
261 // the basename of this Category,
262 // e.g. display:glxdisplay. This is a shorthand way to
263 // define a Category when a pointer to its parent is not
264 // handy.
265 ////////////////////////////////////////////////////////////////////
267 get_category(const string &fullname) {
268  Categories::const_iterator ci;
269  ci = _categories.find(fullname);
270  if (ci != _categories.end()) {
271  return (*ci).second;
272  }
273 
274  // No such Category; create one. First identify the parent name,
275  // based on the rightmost colon.
276  NotifyCategory *parent_category = (NotifyCategory *)NULL;
277  string basename = fullname;
278 
279  size_t colon = fullname.rfind(':');
280  if (colon != string::npos) {
281  parent_category = get_category(fullname.substr(0, colon));
282  basename = fullname.substr(colon + 1);
283 
284  } else if (!fullname.empty()) {
285  // The fullname didn't begin with a colon. Infer one.
286  parent_category = get_top_category();
287  }
288 
289  return get_category(basename, parent_category);
290 }
291 
292 ////////////////////////////////////////////////////////////////////
293 // Function: Notify::out
294 // Access: Public, Static
295 // Description: A convenient way to get the ostream that should be
296 // written to for a Notify-type message. Also see
297 // Category::out() for a message that is specific to a
298 // particular Category.
299 ////////////////////////////////////////////////////////////////////
300 ostream &Notify::
301 out() {
302  return *(ptr()->_ostream_ptr);
303 }
304 
305 ////////////////////////////////////////////////////////////////////
306 // Function: Notify::null
307 // Access: Public, Static
308 // Description: A convenient way to get an ostream that doesn't do
309 // anything. Returned by Category::out() when a
310 // particular Category and/or Severity is disabled.
311 ////////////////////////////////////////////////////////////////////
312 ostream &Notify::
313 null() {
314  return *(ptr()->_null_ostream_ptr);
315 }
316 
317 ////////////////////////////////////////////////////////////////////
318 // Function: Notify::write_string
319 // Access: Public, Static
320 // Description: A convenient way for scripting languages, which may
321 // know nothing about ostreams, to write to Notify.
322 // This writes a single string, followed by an implicit
323 // newline, to the Notify output stream.
324 ////////////////////////////////////////////////////////////////////
325 void Notify::
326 write_string(const string &str) {
327  out() << str << "\n";
328 }
329 
330 ////////////////////////////////////////////////////////////////////
331 // Function: Notify::ptr
332 // Access: Public, Static
333 // Description: Returns the pointer to the global Notify object.
334 // There is only one of these in the world.
335 ////////////////////////////////////////////////////////////////////
337 ptr() {
338  if (_global_ptr == (Notify *)NULL) {
339  init_memory_hook();
340  _global_ptr = new Notify;
341  }
342  return _global_ptr;
343 }
344 
345 ////////////////////////////////////////////////////////////////////
346 // Function: Notify::assert_failure
347 // Access: Public
348 // Description: This function is not intended to be called directly
349 // by user code. It's called from the nassertr() and
350 // assertv() macros when an assertion test fails; it
351 // handles the job of printing the warning message and
352 // deciding what to do about it.
353 //
354 // If this function returns true, the calling function
355 // should return out of its function; if it returns
356 // false, the calling function should ignore the
357 // assertion.
358 ////////////////////////////////////////////////////////////////////
359 bool Notify::
360 assert_failure(const string &expression, int line,
361  const char *source_file) {
362  return assert_failure(expression.c_str(), line, source_file);
363 }
364 
365 ////////////////////////////////////////////////////////////////////
366 // Function: Notify::assert_failure
367 // Access: Public
368 // Description: This function is not intended to be called directly
369 // by user code. It's called from the nassertr() and
370 // assertv() macros when an assertion test fails; it
371 // handles the job of printing the warning message and
372 // deciding what to do about it.
373 //
374 // If this function returns true, the calling function
375 // should return out of its function; if it returns
376 // false, the calling function should ignore the
377 // assertion.
378 ////////////////////////////////////////////////////////////////////
379 bool Notify::
380 assert_failure(const char *expression, int line,
381  const char *source_file) {
382  ostringstream message_str;
383  message_str
384  << expression << " at line " << line << " of " << source_file;
385  string message = message_str.str();
386 
387  if (!_assert_failed) {
388  // We only save the first assertion failure message, as this is
389  // usually the most meaningful when several occur in a row.
390  _assert_failed = true;
391  _assert_error_message = message;
392  }
393 
394  if (has_assert_handler()) {
395  return (*_assert_handler)(expression, line, source_file);
396  }
397 
398 #ifdef ANDROID
399  __android_log_assert("assert", "Panda3D", "Assertion failed: %s", message.c_str());
400 #else
401  nout << "Assertion failed: " << message << "\n";
402 #endif
403 
404  // This is redefined here, shadowing the defining in config_prc.h,
405  // so we can guarantee it has already been constructed.
406  ALIGN_16BYTE ConfigVariableBool assert_abort("assert-abort", false);
407  if (assert_abort) {
408  // Make sure the error message has been flushed to the output.
409  nout.flush();
410 
411 #ifdef WIN32
412  // How to trigger an exception in VC++ that offers to take us into
413  // the debugger? abort() doesn't do it. We used to be able to
414  // assert(false), but in VC++ 7 that just throws an exception, and
415  // an uncaught exception just exits, without offering to open the
416  // debugger.
417 
418  // DebugBreak() seems to be provided for this purpose, but it
419  // doesn't seem to work properly either, since we don't seem to
420  // get a reliable stack trace.
421 
422  // The old reliable int 3 works (at least on an Intel platform) if
423  // you are already running within a debugger. But it doesn't
424  // offer to bring up a debugger otherwise.
425 
426  // So we'll force a segfault, which works every time.
427  int *ptr = (int *)NULL;
428  *ptr = 1;
429 
430 #else // WIN32
431  abort();
432 #endif // WIN32
433  }
434 
435  return true;
436 }
437 
438 ////////////////////////////////////////////////////////////////////
439 // Function: Notify::string_severity
440 // Access: Public
441 // Description: Given a string, one of "debug", "info", "warning",
442 // etc., return the corresponding Severity level, or
443 // NS_unspecified if none of the strings matches.
444 ////////////////////////////////////////////////////////////////////
445 NotifySeverity Notify::
446 string_severity(const string &str) {
447  // Convert the string to lowercase for a case-insensitive
448  // comparison.
449  string lstring;
450  for (string::const_iterator si = str.begin();
451  si != str.end();
452  ++si) {
453  lstring += tolower(*si);
454  }
455 
456  if (lstring == "spam") {
457  return NS_spam;
458 
459  } else if (lstring == "debug") {
460  return NS_debug;
461 
462  } else if (lstring == "info") {
463  return NS_info;
464 
465  } else if (lstring == "warning") {
466  return NS_warning;
467 
468  } else if (lstring == "error") {
469  return NS_error;
470 
471  } else if (lstring == "fatal") {
472  return NS_fatal;
473 
474  } else {
475  return NS_unspecified;
476  }
477 }
478 
479 ////////////////////////////////////////////////////////////////////
480 // Function: Notify::config_initialized
481 // Access: Public
482 // Description: Intended to be called only by Config, this is a
483 // callback that indicates to Notify when Config has
484 // done initializing and Notify can safely set up some
485 // internal state variables that depend on Config
486 // variables.
487 ////////////////////////////////////////////////////////////////////
488 void Notify::
490  static bool already_initialized = false;
491  if (already_initialized) {
492  nout << "Notify::config_initialized() called more than once.\n";
493  return;
494  }
495  already_initialized = true;
496 
497  if (_ostream_ptr == &cerr) {
498  ConfigVariableFilename notify_output
499  ("notify-output", "",
500  "The filename to which to write all the output of notify");
501 
502  if (!notify_output.empty()) {
503  if (notify_output == "stdout") {
504  cout.setf(ios::unitbuf);
505  set_ostream_ptr(&cout, false);
506 
507  } else if (notify_output == "stderr") {
508  set_ostream_ptr(&cerr, false);
509 
510  } else {
511  Filename filename = notify_output;
512  filename.set_text();
513 #ifdef BUILD_IPHONE
514  // On the iPhone, route everything through cerr, and then send
515  // cerr to the log file, since we can't get the cerr output
516  // otherwise.
517  string os_specific = filename.to_os_specific();
518  int logfile_fd = open(os_specific.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
519  if (logfile_fd < 0) {
520  nout << "Unable to open file " << filename << " for output.\n";
521  } else {
522  dup2(logfile_fd, STDOUT_FILENO);
523  dup2(logfile_fd, STDERR_FILENO);
524  close(logfile_fd);
525 
526  set_ostream_ptr(&cerr, false);
527  }
528 #else
529  pofstream *out = new pofstream;
530  if (!filename.open_write(*out)) {
531  nout << "Unable to open file " << filename << " for output.\n";
532  delete out;
533  } else {
534  out->setf(ios::unitbuf);
535  set_ostream_ptr(out, true);
536  }
537 #endif // BUILD_IPHONE
538  }
539  }
540  }
541 }
ostream * get_ostream_ptr() const
Returns the system-wide ostream for all Notify messages.
Definition: notify.cxx:94
void set_assert_handler(AssertHandler *assert_handler)
Sets a pointer to a C function that will be called when an assertion test fails.
Definition: notify.cxx:148
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:2045
This is a convenience class to specialize ConfigVariable as a Filename type.
This is a convenience class to specialize ConfigVariable as a boolean type.
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:507
NotifyCategory * get_top_category()
Returns the topmost Category in the hierarchy.
Definition: notify.cxx:193
static ostream & out()
A convenient way to get the ostream that should be written to for a Notify-type message.
Definition: notify.cxx:301
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:337
A particular category of error messages.
void clear_assert_handler()
Removes the installed assert handler and restores default behavior of nassertr() and nassertv()...
Definition: notify.cxx:159
static ostream & null()
A convenient way to get an ostream that doesn&#39;t do anything.
Definition: notify.cxx:313
NotifyCategory * get_category(const string &basename, NotifyCategory *parent_category)
Finds or creates a new Category given the basename of the category and its parent in the category hie...
Definition: notify.cxx:206
AssertHandler * get_assert_handler() const
Returns a pointer to the user-installed assert handler, if one was installed, or NULL otherwise...
Definition: notify.cxx:181
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
bool has_assert_handler() const
Returns true if a user assert handler has been installed, false otherwise.
Definition: notify.cxx:170
void set_ostream_ptr(ostream *ostream_ptr, bool delete_later)
Changes the ostream that all subsequent Notify messages will be written to.
Definition: notify.cxx:73
bool assert_failure(const string &expression, int line, const char *source_file)
This function is not intended to be called directly by user code.
Definition: notify.cxx:360
static ios_fmtflags get_literal_flag()
Returns a flag that may be set on the Notify stream via setf() that, when set, enables "literal" mode...
Definition: notify.cxx:113
void config_initialized()
Intended to be called only by Config, this is a callback that indicates to Notify when Config has don...
Definition: notify.cxx:489
static void write_string(const string &str)
A convenient way for scripting languages, which may know nothing about ostreams, to write to Notify...
Definition: notify.cxx:326
static NotifySeverity string_severity(const string &string)
Given a string, one of "debug", "info", "warning", etc., return the corresponding Severity level...
Definition: notify.cxx:446
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1196
An object that handles general error reporting to the user.
Definition: pnotify.h:38