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