llerror.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /**
  2. * @file llerror.h
  3. * @brief Error message system basic functions declaration
  4. *
  5. * $LicenseInfo:firstyear=2006&license=viewergpl$
  6. *
  7. * Copyright (c) 2006-2009, Linden Research, Inc.
  8. * Copyright (c) 2009-2023, Henri Beauchamp.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #ifndef LL_LLERROR_H
  34. #define LL_LLERROR_H
  35. #include <string>
  36. #include <sstream>
  37. #include <typeinfo>
  38. #include "stdtypes.h"
  39. #include "llpreprocessor.h"
  40. #include "hbfastmap.h"
  41. constexpr int LL_ERR_NOERR = 0;
  42. // Notes by Henri Beauchamp: in the Cool VL Viewer, the error logging scheme
  43. // was simplified, highly optimized, and made friendly to both programmers and
  44. // users.
  45. //
  46. // First, I made the following constatations in LL's original code:
  47. // - There was no point at all for tagged info, warning and error messages
  48. // (LL_INFOS("Tag"), LL_WARNS("Tag"), LL_ERRS("Tag")) since all info and
  49. // warning messages are normally always logged, unless the user fiddles
  50. // with the app_settings/logcontrol.xml file to explicitely disable them
  51. // (but what the Hell for ?). LL_ERRS("Tag") by itself was totally useless,
  52. // since encountering any such message always involved a voluntary crash.
  53. // - LL_DEBUGS("Tag") is indeed useful, but LL's code did not even allow to
  54. // enable those messages via the UI and you had to recourse to editing
  55. // manually the app_settings/logcontrol.xml file before viewer launch.
  56. // - "Narrow tags" were only actually used in a couple of places, and did not
  57. // really bring anything when compared to using two different "broad tags",
  58. // while slowing down the logging routine with one more test to perform.
  59. // - Fast repeating log messages could trash the filesystem badly, slowing
  60. // down everything whenever they happened.
  61. // - The *_once and *_ONCE macros were not logging just once, but rather
  62. // sparsingly (the first time, then the 10th, the 100th and finally every
  63. // 1000 occurrences), which forced to recourse to static booleans and tests
  64. // in place where you wanted to really only log a message once and for all
  65. // during the whole session.
  66. // - There was no way to get precise time stamps to measure the logged events
  67. // occurrence with a better precision than 1 second.
  68. // - The code was poorly optimized.
  69. //
  70. // I therefore:
  71. // - Kept only llinfos, llwarns, llerrs and LL_DEBUGS("Tag").
  72. // - Added a way to enable/disable LL_DEBUGS from a floater ("Advanced"
  73. // -> "Consoles" -> "Debug tags"; see also the newview/hbfloaterdebugtags.*
  74. // files); this also sets the sDebugMessages boolean accordingly (see the
  75. // optimizations point below).
  76. // - Got rid of broad/narrow tags (dual tags).
  77. // - Added llinfos_once and llwarns_once which replace LL_INFOS_ONCE("Tag")
  78. // and LL_WARNS_ONCE("Tag"), respectively, but kept LL_DEBUGS_ONCE("Tag").
  79. // - Made all *_once and LL_DEBUGS_ONCE macros indeed only logging once and
  80. // for all, but added *_sparse and LL_DEBUGS_SPARSE macros for when
  81. // occasionnal logging of often repeating cases is desired (i.e. *_sparse
  82. // and LL_DEBUGS_SPARSE act like LL's *_ONCE macros did).
  83. // - Added a repeating messages filter: messages repeated in succession are
  84. // now counted and only printed twice: the first time they occur, then a
  85. // second time, with the repeats count appended, whenever another,
  86. // different message is finally logged.
  87. // - Added an option to log timestamps with milliseconds.
  88. // - Brought many optimizations with, for example, faster tags lookups,
  89. // replacement of the glogal mUniqueLogMessages map (which was using
  90. // strings as keys, i.e. super-slow to search, and this caused _ONCE sites
  91. // to be skipped should they share the exact same message with another,
  92. // impairing the detection of bugs by hiding its location in the code) with
  93. // a per-site message hash / counter map, the use of the sDebugMessages
  94. // boolean to avoid entering a LL_DEGUGS* call site code when no debug tag
  95. // is activated at all, etc...
  96. ///////////////////////////////////////////////////////////////////////////////
  97. // Error Logging Facility
  98. //
  99. // Information for most users:
  100. //
  101. // Code can log messages with constructions like this:
  102. //
  103. // llinfos << "Request to fizzbip agent " << agent_id
  104. // << " denied due to timeout" << llendl;
  105. //
  106. // Messages can be logged to one of four increasing levels of concern, using
  107. // one of four "streams":
  108. //
  109. // LL_DEBUGS("Tag") - debug messages that are not shown unless "Tag"
  110. // is active.
  111. // llinfos - informational messages.
  112. // llwarns - warning messages that signal an unexpected
  113. // occurrence (that could be or not the sign of an
  114. // actual problem).
  115. // llerrs - error messages that are major, unrecoverable
  116. // failures.
  117. // The later (llerrs) automatically crashes the process after the message is
  118. // logged.
  119. //
  120. // Note that these "streams" are actually #define magic. Rules for use:
  121. // * they cannot be used as normal streams, only to start a message;
  122. // * messages written to them MUST be terminated with llendl or LL_ENDL;
  123. // * between the opening and closing, the << operator is indeed
  124. // writing onto a std::ostream, so all conversions and stream
  125. // formating are available.
  126. //
  127. // These messages are automatically logged with function name, and (if
  128. // enabled) file and line of the message (note: existing messages that already
  129. // include the function name do not get name printed twice).
  130. //
  131. // If you have a class, adding LOG_CLASS line to the declaration will cause
  132. // all messages emitted from member functions (normal and static) to be tagged
  133. // with the proper class name as well as the function name:
  134. //
  135. // class LLFoo
  136. // {
  137. // protected:
  138. // LOG_CLASS(LLFoo);
  139. //
  140. // public:
  141. // ...
  142. // };
  143. //
  144. // void LLFoo::doSomething(int i)
  145. // {
  146. // if (i > 100)
  147. // {
  148. // llwarns << "called with a big value for i: " << i << llendl;
  149. // }
  150. // ...
  151. // }
  152. //
  153. // will result in messages like:
  154. //
  155. // WARNING: LLFoo::doSomething: called with a big value for i: 283
  156. //
  157. //
  158. // You may also use this construct if you need to do computation in the middle
  159. // of a message (most useful with debug messages):
  160. // LL_DEBUGS ("AgentGesture") << "the agent " << agend_id;
  161. // switch (f)
  162. // {
  163. // case FOP_SHRUGS: LL_CONT << "shrugs"; break;
  164. // case FOP_TAPS: LL_CONT << "points at " << who; break;
  165. // case FOP_SAYS: LL_CONT << "says " << message; break;
  166. // }
  167. // LL_CONT << " for " << t << " seconds" << LL_ENDL;
  168. //
  169. // Such computation is done only when the message is actually logged.
  170. //
  171. //
  172. // Which messages are logged and which are suppressed can be controled at run
  173. // time from the live file logcontrol.xml based on function, class and/or
  174. // source file.
  175. //
  176. // Lastly, logging is now very efficient in both compiled code and execution
  177. // when skipped. There is no need to wrap messages, even debugging ones.
  178. ///////////////////////////////////////////////////////////////////////////////
  179. namespace LLError
  180. {
  181. enum ELevel
  182. {
  183. LEVEL_DEBUG = 0,
  184. LEVEL_INFO = 1,
  185. LEVEL_WARN = 2,
  186. LEVEL_ERROR = 3,
  187. // Not really a level: used to indicate that no messages should be
  188. // logged.
  189. LEVEL_NONE = 4
  190. };
  191. // Macro support
  192. // The classes CallSite and Log are used by the logging macros below.
  193. // They are not intended for general use.
  194. class CallSite;
  195. // Purely static class
  196. class Log
  197. {
  198. public:
  199. Log() = delete;
  200. ~Log() = delete;
  201. static bool shouldLog(CallSite& site);
  202. static std::ostringstream* out();
  203. static void flush(const std::ostringstream& out, const CallSite& site);
  204. public:
  205. // When false, skip all LL_DEBUGS checks, for speed. HB
  206. static bool sDebugMessages;
  207. // When true, print milliseconds in timestamp for log messages. HB
  208. static bool sPreciseTimeStamp;
  209. // When true, abort() on llerrs instead of crashing. HB
  210. static bool sIsBeingDebugged;
  211. };
  212. class CallSite
  213. {
  214. friend class Log;
  215. public:
  216. // Represents a specific place in the code where a message is logged
  217. // This is public because it is used by the macros below. It is not
  218. // intended for public use. The constructor is never inlined since it
  219. // is called only once per static call site in the code and inlining it
  220. // would just consume needlessly CPU instruction cache lines... HB
  221. LL_NO_INLINE CallSite(ELevel level, const char* file, S32 line,
  222. const std::type_info& class_info,
  223. const char* function, const char* tag);
  224. virtual ~CallSite() = default;
  225. // This member function needs to be in-line for efficiency
  226. LL_INLINE bool shouldLog()
  227. {
  228. return mCached ? mShouldLog : Log::shouldLog(*this);
  229. }
  230. LL_INLINE void invalidate() { mCached = false; }
  231. // This method is for adding any prefix to the log message. For this
  232. // base class, nothing is added. When it returns true, the message
  233. // is logged by the caller, else the log line is discarded. HB
  234. LL_INLINE virtual bool getPrefix(std::ostringstream& out,
  235. const std::string& message) const
  236. {
  237. return true;
  238. }
  239. private:
  240. // These describe the call site and never change
  241. const ELevel mLevel;
  242. const S32 mLine;
  243. char* mFile;
  244. const std::type_info& mClassInfo;
  245. const char* const mFunction;
  246. const char* const mTag;
  247. // These implement a cache of the call to shouldLog()
  248. bool mCached;
  249. bool mShouldLog;
  250. };
  251. // We use a derived method to avoid storing a hash map and a superfluous
  252. // boolean for call sites which are not of the ONCE or SPARSE types. HB
  253. class CallSiteOnce final : public CallSite
  254. {
  255. friend class Log;
  256. public:
  257. // Represents a specific place in the code where a message is logged
  258. // This is public because it is used by the macros below. It is not
  259. // intended for public use. The constructor is never inlined since it
  260. // is called only once per static call site in the code and inlining it
  261. // would just consume needlessly CPU instruction cache lines... HB
  262. LL_NO_INLINE CallSiteOnce(ELevel level, const char* file, S32 line,
  263. const std::type_info& class_info,
  264. const char* function, const char* tag,
  265. bool sparse);
  266. // This method allows to decide whether to log or not based on the past
  267. // occurrences of 'message', and sends the corresponding prefix to 'out'
  268. // when logging should happen, returning true. It returns false if the
  269. // 'message' has already be seen and the log line must be discarded. HB
  270. LL_NO_INLINE bool getPrefix(std::ostringstream& out,
  271. const std::string& message) const override;
  272. private:
  273. // This stores the hashes of the messages already printed for this call
  274. // site. HB
  275. typedef flat_hmap<U64, U32> msg_hash_map_t;
  276. // 'mutable', because we need the getPrefix() method to be const while
  277. // modifying this map. HB
  278. mutable msg_hash_map_t mOccurrences;
  279. // true = sparse messages, false = print them only once.
  280. const bool mSparse;
  281. };
  282. // Used to indicate the end of a message
  283. class End {};
  284. LL_INLINE std::ostream& operator<<(std::ostream& s, const End&)
  285. {
  286. return s;
  287. }
  288. // Used to indicate no class info known for logging
  289. class NoClassInfo {};
  290. std::string className(const std::type_info& type);
  291. bool isAvailable();
  292. } // namespace LLError
  293. #if defined(LL_DEBUG)
  294. # define llassert(func) llassert_always(func)
  295. # define LL_HAS_ASSERT 1
  296. #elif defined(LL_LOOP_ON_ASSERT)
  297. // Useful for debugging optimized code when it takes unsuspected paths and gdb
  298. // fails to trace it back. HB
  299. # define llassert(func) if (LL_UNLIKELY(!(func))) { while (true) ; }
  300. # define LL_HAS_ASSERT 1
  301. #else
  302. # define llassert(func)
  303. #endif
  304. #define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs << "ASSERT (" << #func << ")" << llendl;
  305. //
  306. // Class type information for logging
  307. //
  308. // Declares class to tag logged messages with. See top of file for example of
  309. // how to use this
  310. #define LOG_CLASS(s) typedef s _LL_CLASS_TO_LOG
  311. // Outside a class declaration, or in class without LOG_CLASS(), this typedef
  312. // causes the messages to not be associated with any class.
  313. typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
  314. //
  315. // Error Logging Macros. See top of file for common usage.
  316. //
  317. #define lllog(level) \
  318. { \
  319. static LLError::CallSite _site(level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, NULL); \
  320. if (LL_LIKELY(_site.shouldLog())) \
  321. { \
  322. std::ostringstream _out; \
  323. (_out)
  324. #define lllog2(level, sparse) \
  325. { \
  326. static LLError::CallSiteOnce _site(level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, NULL, sparse); \
  327. if (LL_LIKELY(_site.shouldLog())) \
  328. { \
  329. std::ostringstream _out; \
  330. (_out)
  331. #define llendl \
  332. LLError::End(); \
  333. LLError::Log::flush(_out, _site); \
  334. } \
  335. }
  336. #define llinfos lllog(LLError::LEVEL_INFO)
  337. #define llinfos_once lllog2(LLError::LEVEL_INFO, false)
  338. #define llinfos_sparse lllog2(LLError::LEVEL_INFO, true)
  339. #define llwarns lllog(LLError::LEVEL_WARN)
  340. #define llwarns_once lllog2(LLError::LEVEL_WARN, false)
  341. #define llwarns_sparse lllog2(LLError::LEVEL_WARN, true)
  342. #define llerrs lllog(LLError::LEVEL_ERROR)
  343. #define llcont (_out)
  344. // Macros for debugging with the passing of a string tag. Note that we test for
  345. // a special static variable (sDebugMessages) before calling shouldLog(), which
  346. // allows to switch off all debug messages logging at once if/when needed, and
  347. // speeds up tremendously the code when no debug tag is activated by avoiding a
  348. // pointless call to shouldLog(). HB
  349. #define lllog_debug(Tag) \
  350. { \
  351. static LLError::CallSite _site(LLError::LEVEL_DEBUG, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, Tag); \
  352. if (LL_UNLIKELY(LLError::Log::sDebugMessages && _site.shouldLog())) \
  353. { \
  354. std::ostringstream _out; \
  355. (_out)
  356. #define lllog_debug2(Tag, sparse) \
  357. { \
  358. static LLError::CallSiteOnce _site(LLError::LEVEL_DEBUG, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, Tag, sparse); \
  359. if (LL_UNLIKELY(LLError::Log::sDebugMessages && _site.shouldLog())) \
  360. { \
  361. std::ostringstream _out; \
  362. (_out)
  363. #define LL_DEBUGS(Tag) lllog_debug(Tag)
  364. #define LL_DEBUGS_ONCE(Tag) lllog_debug2(Tag, false)
  365. #define LL_DEBUGS_SPARSE(Tag) lllog_debug2(Tag, true)
  366. #define LL_ENDL llendl
  367. #define LL_CONT (_out)
  368. #endif // LL_LLERROR_H