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 */
74 void Notify::
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::
108 get_literal_flag() {
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 */
137 void Notify::
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 */
146 void Notify::
147 clear_assert_handler() {
148 _assert_handler = nullptr;
149 }
150
151 /**
152 * Returns true if a user assert handler has been installed, false otherwise.
153 */
154 bool Notify::
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 */
172 NotifyCategory *Notify::
173 get_top_category() {
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 */
182 NotifyCategory *Notify::
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 */
221 NotifyCategory *Notify::
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 */
233 NotifyCategory *Notify::
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 */
283 void Notify::
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 */
292 Notify *Notify::
293 ptr() {
294 if (_global_ptr == nullptr) {
295 init_memory_hook();
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 */
311 bool Notify::
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 */
327 bool Notify::
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 */
427 void Notify::
428 config_initialized() {
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 }
ConfigVariableBool
This is a convenience class to specialize ConfigVariable as a boolean type.
Definition: configVariableBool.h:23
ConfigVariableFilename
This is a convenience class to specialize ConfigVariable as a Filename type.
Definition: configVariableFilename.h:27
ConfigVariableFilename::get_value
get_value
Returns the variable's value.
Definition: configVariableFilename.h:57
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
Filename::set_text
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
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
NotifyCategory
A particular category of error messages.
Definition: notifyCategory.h:32
Notify
An object that handles general error reporting to the user.
Definition: pnotify.h:33
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::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
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
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::get_top_category
NotifyCategory * get_top_category()
Returns the topmost Category in the hierarchy.
Definition: notify.cxx:173
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
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_ostream_ptr
std::ostream * get_ostream_ptr() const
Returns the system-wide ostream for all Notify messages.
Definition: notify.cxx:93
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::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::null
static std::ostream & null()
A convenient way to get an ostream that doesn't do anything.
Definition: notify.cxx:274
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
Notify::ptr
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:293
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
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
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
configPageManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
configVariableBool.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
configVariableFilename.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_prc.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
notifyCategory.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnotify.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.