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 }
This is a convenience class to specialize ConfigVariable as a boolean type.
This is a convenience class to specialize ConfigVariable as a Filename type.
get_value
Returns the variable's value.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
A particular category of error messages.
An object that handles general error reporting to the user.
Definition: pnotify.h:33
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
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
void clear_assert_handler()
Removes the installed assert handler and restores default behavior of nassertr() and nassertv().
Definition: notify.cxx:147
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
NotifyCategory * get_top_category()
Returns the topmost Category in the hierarchy.
Definition: notify.cxx:173
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
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
std::ostream * get_ostream_ptr() const
Returns the system-wide ostream for all Notify messages.
Definition: notify.cxx:93
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
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
static std::ostream & null()
A convenient way to get an ostream that doesn't do anything.
Definition: notify.cxx:274
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
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:293
bool has_assert_handler() const
Returns true if a user assert handler has been installed, false otherwise.
Definition: notify.cxx:155
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
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.