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