Panda3D
Loading...
Searching...
No Matches
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"
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
36using std::cerr;
37using std::cout;
38using std::ostream;
39using std::ostringstream;
40using std::string;
41
42Notify *Notify::_global_ptr = nullptr;
43
44/**
45 *
46 */
47Notify::
48Notify() {
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 */
60Notify::
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 */
75set_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 */
92ostream *Notify::
93get_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 */
107ios_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 */
138set_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 */
155has_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 */
163Notify::AssertHandler *Notify::
164get_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 */
183get_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 */
222get_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 */
234get_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 */
264ostream &Notify::
265out() {
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 */
273ostream &Notify::
274null() {
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 */
284write_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 */
293ptr() {
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 */
312assert_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 */
328assert_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 */
389NotifySeverity Notify::
390string_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:44
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
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.
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.