The Pedigree Project  0.1
Log.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "pedigree/kernel/Log.h"
21 #include "pedigree/kernel/BootstrapInfo.h"
22 #include "pedigree/kernel/LockGuard.h"
23 #include "pedigree/kernel/machine/Machine.h"
24 #include "pedigree/kernel/machine/Timer.h"
25 #include "pedigree/kernel/panic.h"
26 #include "pedigree/kernel/processor/Processor.h"
27 #include "pedigree/kernel/process/Scheduler.h"
28 #include "pedigree/kernel/time/Time.h"
29 #include "pedigree/kernel/utilities/StaticCord.h"
30 #include "pedigree/kernel/utilities/String.h"
31 #include "pedigree/kernel/utilities/StringView.h"
32 #include "pedigree/kernel/utilities/Vector.h"
33 #include "pedigree/kernel/utilities/utility.h"
34 
35 extern BootstrapStruct_t *g_pBootstrapInfo;
36 
38 #define LOG_MAX_DEDUPE_MESSAGES 20
39 
41 #define LOG_TIMESTAMPS_IN_NANOS 0
42 
44 EXPORTED_PUBLIC BootProgressUpdateFn g_BootProgressUpdate = 0;
45 EXPORTED_PUBLIC size_t g_BootProgressTotal = 0;
46 EXPORTED_PUBLIC size_t g_BootProgressCurrent = 0;
47 
49 TinyStaticString Log::m_NoticeSeverityString("(NN) ");
50 TinyStaticString Log::m_WarningSeverityString("(WW) ");
51 TinyStaticString Log::m_ErrorSeverityString("(EE) ");
52 TinyStaticString Log::m_FatalSeverityString("(FF) ");
53 
54 #ifndef SERIAL_IS_FILE
56 #else
58 #endif
59 
60 NormalStaticString Log::m_DedupeHead("(last message+severity repeated ");
61 TinyStaticString Log::m_DedupeTail(" times)");
62 
63 // Lookup tables to not do int->str conversions every repeated log message
64 static const char *g_RepeatedStrings[] = {
65  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
66  "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
67 };
68 
69 static size_t g_RepeatedLengths[] = {
70  1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
71  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
72 };
73 
74 static const size_t g_NumRepeatedStrings = 20;
75 
77  :
78 #ifdef THREADS
79  m_Lock(),
80 #endif
81  m_StaticEntries(0), m_StaticEntryStart(0), m_StaticEntryEnd(0),
82  m_Buffer(),
83 #ifdef DONT_LOG_TO_SERIAL
84  m_EchoToSerial(false),
85 #else
86  m_EchoToSerial(true),
87 #endif
88  m_nOutputCallbacks(0),
89  m_LastEntryHash(0),
90  m_LastEntrySeverity(Fatal),
91  m_HashMatchedCount(0),
92  m_Timestamps(true),
93  m_LastTime(0)
94 {
95  for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i)
96  {
97  m_OutputCallbacks[i] = nullptr;
98  }
99 }
100 
102 {
103  LogEntry entry;
104  entry << Notice << "-- Log Terminating --";
105  addEntry(entry);
106 }
107 
109 {
110  return m_Instance;
111 }
112 
114 {
115 #ifndef ARM_COMMON
116  char *cmdline = g_pBootstrapInfo->getCommandLine();
117  if (cmdline)
118  {
119  Vector<String> cmds = String(cmdline).tokenise(' ');
120  for (auto it = cmds.begin(); it != cmds.end(); it++)
121  {
122  auto cmd = *it;
123  if (cmd == String("--disable-log-to-serial"))
124  {
125  m_EchoToSerial = false;
126  break;
127  }
128  else if (cmd == String("--enable-log-to-serial"))
129  {
130  m_EchoToSerial = true;
131  break;
132  }
133  }
134  }
135 #endif
136 }
137 
139 {
140 #ifndef DONT_LOG_TO_SERIAL
141  if (m_EchoToSerial)
142  installSerialLogger();
143 #endif
144 }
145 
146 void Log::installCallback(LogCallback *pCallback, bool bSkipBacklog)
147 {
148  {
149 #ifdef THREADS
151 #endif
152  bool ok = false;
153  for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i)
154  {
155  if (m_OutputCallbacks[i] == nullptr)
156  {
157  m_OutputCallbacks[i] = pCallback;
158  ++m_nOutputCallbacks;
159  ok = true;
160  break;
161  }
162  }
163 
164  if (!ok)
165  {
167  return;
168  }
169  }
170 
171  // Some callbacks want to skip a (potentially) massive backlog
172  if (bSkipBacklog)
173  return;
174 
175  // Call the callback for the existing, flushed, log entries
176  size_t entry = m_StaticEntryStart;
177  LogCord msg;
178  while (1)
179  {
180  msg.clear();
181 
182  if (entry == m_StaticEntryEnd)
183  {
184  break;
185  }
186  else if (m_StaticLog[entry].str.length())
187  {
188  msg.append("(backlog) ", 10);
189 
190  const TinyStaticString &severity = severityToString(m_StaticLog[entry].severity);
191  msg.append(severity, severity.length());
192  if (m_Timestamps)
193  {
194  const NormalStaticString &ts = getTimestamp();
195  msg.append(ts, ts.length());
196  }
197  msg.append(m_StaticLog[entry].str, m_StaticLog[entry].str.length());
198  msg.append(m_LineEnding, m_LineEnding.length());
199 
203  pCallback->callback(msg);
204  }
205 
206  entry = (entry + 1) % LOG_ENTRIES;
207  }
208 }
210 {
211 #ifdef THREADS
213 #endif
214  for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i)
215  {
216  if (m_OutputCallbacks[i] == pCallback)
217  {
218  m_OutputCallbacks[i] = nullptr;
219  --m_nOutputCallbacks;
220  break;
221  }
222  }
223 }
224 
226 {
227  return m_StaticEntries;
228 }
229 
231 {
232  return 0;
233 }
234 
236 {
237  return m_StaticLog[(m_StaticEntryStart + n) % LOG_ENTRIES];
238 }
241 {
242  return m_StaticLog[0];
243 }
244 
245 bool Log::echoToSerial()
246 {
247  return m_EchoToSerial;
248 }
249 
250 const Log::LogEntry &Log::getLatestEntry() const
251 {
252  return m_StaticLog[m_StaticEntries - 1];
253 }
254 
255 Log::LogEntry::LogEntry() : timestamp(), severity(), str(), numberType(Dec)
256 {
258 }
259 
261 {
262  str.append(s);
263  return *this;
264 }
265 
267 {
268  str.appendBytes(s, s.length());
269  return *this;
270 }
271 
273 {
274  str.appendBytes(s.str(), s.length());
275  return *this;
276 }
277 
279 {
280  return (*this) << (reinterpret_cast<const char *>(append_str));
281 }
282 
284 {
285  if (b)
286  return *this << "true";
287 
288  return *this << "false";
289 }
290 
291 template <class T>
293 {
294  size_t radix = 10;
295  if (numberType == Hex)
296  {
297  radix = 16;
298  str.append("0x");
299  }
300  else if (numberType == Oct)
301  {
302  radix = 8;
303  str.append("0");
304  }
305  str.append(n, radix);
306  return *this;
307 }
308 
310 {
311  numberType = type;
312  return *this;
313 }
314 
316 {
317  // Zero the buffer.
318  str.clear();
319  severity = level;
320 
321 #ifndef UTILITY_LINUX
322  Machine &machine = Machine::instance();
323  if (machine.isInitialised() == true && machine.getTimer() != 0)
324  {
325  Timer &timer = *machine.getTimer();
326  timestamp = timer.getTickCount();
327  }
328  else
329  timestamp = 0;
330 #endif
331 
332  return *this;
333 }
334 
335 // NOTE: Make sure that the templated << operator gets only instantiated for
336 // integer types.
338 template Log::LogEntry &Log::LogEntry::operator<<(unsigned char);
340 template Log::LogEntry &Log::LogEntry::operator<<(unsigned short);
342 template Log::LogEntry &Log::LogEntry::operator<<(unsigned int);
344 template Log::LogEntry &Log::LogEntry::operator<<(unsigned long);
345 // NOTE: Instantiating these for MIPS32 requires __udiv3di, but we only have
346 // __udiv3ti (??) in libgcc.a for mips.
347 #ifndef MIPS32
348 template Log::LogEntry &Log::LogEntry::operator<<(long long);
349 template Log::LogEntry &Log::LogEntry::operator<<(unsigned long long);
350 #endif
351 
353 {
354  m_Buffer = entry;
355  return *this;
356 }
357 
359 {
360  // Flush the buffer.
361  if (type == Flush)
362  {
363  flushEntry();
364  }
365 
366  return *this;
367 }
368 
369 void Log::addEntry(const LogEntry &entry, bool lock, bool flush)
370 {
371  m_Buffer = entry;
372  if (flush)
373  {
374  flushEntry(lock);
375  }
376 }
377 
378 void Log::flushEntry(bool lock)
379 {
380  static bool handlingFatal = false;
381 
382  LogCord msg;
383  msg.clear();
384 
385 #ifdef THREADS
386  if (lock)
387  m_Lock.acquire();
388 #endif
389 
391  {
392  m_StaticEntryStart = (m_StaticEntryStart + 1) % LOG_ENTRIES;
393  }
394  else
395  m_StaticEntries++;
396 
397  m_StaticLog[m_StaticEntryEnd] = m_Buffer;
398  m_StaticEntryEnd = (m_StaticEntryEnd + 1) % LOG_ENTRIES;
399 
400 #ifdef THREADS
401  // no need for lock anymore - all tracked now
402  // remaining work hits callbacks which can lock themselves
403  if (lock)
404  m_Lock.release();
405 #endif
406 
407  if (m_nOutputCallbacks)
408  {
409  bool wasRepeated = false;
410  uint64_t repeatedTimes = 0;
411 
412  // Have we seen this message before?
413  m_Buffer.str.allowHashing(true); // calculate hash now
414  uint64_t currentHash = m_Buffer.str.hash();
416  if (currentHash == m_LastEntryHash)
417  {
419  {
421 
422  if (m_HashMatchedCount < LOG_MAX_DEDUPE_MESSAGES)
423  {
424 
425 #ifdef THREADS
426  // this thread is spammy, let something else run for a bit
427  //Scheduler::instance().yield();
428 #endif
429  return;
430  }
431  }
432  }
433 
434  if (m_HashMatchedCount)
435  {
436  wasRepeated = true;
437  repeatedTimes = m_HashMatchedCount;
438  m_HashMatchedCount = 0;
439  }
440 
441  m_LastEntryHash = currentHash;
443 
444  // We have output callbacks installed. Build the string we'll pass
445  // to each callback *now* and then send it.
446  if (wasRepeated)
447  {
448  msg.append(m_DedupeHead, m_DedupeHead.length());
449  if (repeatedTimes < g_NumRepeatedStrings)
450  {
451  msg.append(g_RepeatedStrings[repeatedTimes], g_RepeatedLengths[repeatedTimes]);
452  }
453  else
454  {
455  TinyStaticString repeated;
456  repeated.append(repeatedTimes);
457  msg.append(repeated, repeated.length());
458  }
459  msg.append(m_DedupeTail, m_DedupeTail.length());
460  msg.append(m_LineEnding, m_LineEnding.length());
461  }
462 
463  const TinyStaticString &severity = severityToString(m_Buffer.severity);
464  msg.append(severity, severity.length());
465  if (m_Timestamps)
466  {
467  const NormalStaticString &ts = getTimestamp();
468  msg.append(ts, ts.length());
469  }
470  msg.append(m_Buffer.str, m_Buffer.str.length());
471  msg.append(m_LineEnding, m_LineEnding.length());
472 
473  for (size_t i = 0; i < LOG_CALLBACK_COUNT; ++i)
474  {
475  if (m_OutputCallbacks[i] != nullptr)
476  {
477  m_OutputCallbacks[i]->callback(msg);
478  }
479  }
480  }
481 
482  // Panic if that was a fatal error.
483  if ((!handlingFatal) && m_Buffer.severity == Fatal)
484  {
485  handlingFatal = true;
486 
487  const char *panicstr = static_cast<const char *>(m_Buffer.str);
488 
489 // Attempt to trap to debugger, panic if that fails.
490 #ifdef DEBUGGER
492 #endif
493  panic(panicstr);
494  }
495 }
496 
497 void Log::enableTimestamps()
498 {
499  m_Timestamps = true;
500 }
501 
502 void Log::disableTimestamps()
503 {
504  m_Timestamps = false;
505 }
506 
507 const NormalStaticString &Log::getTimestamp()
508 {
509  Time::Timestamp tn = Time::getTimeNanoseconds();
510  Time::Timestamp ts = Time::getTime();
511  Time::Timestamp t;
512 #if LOG_TIMESTAMPS_IN_NANOS
513  t = tn;
514 #else
515  t = ts;
516 #endif
517  if (t == m_LastTime)
518  {
519  return m_CachedTimestamp;
520  }
521 
522  m_LastTime = t;
523 
525  r += "[";
526  r.append(t);
527  r += ".";
528  r.append(Processor::id());
529  r += "] ";
530 
531  m_CachedTimestamp = r;
532  return m_CachedTimestamp;
533 }
534 
535 const TinyStaticString &Log::severityToString(SeverityLevel level) const
536 {
537  switch (level)
538  {
539  case Debug:
540  return m_DebugSeverityString;
541  case Notice:
542  return m_NoticeSeverityString;
543  case Warning:
544  return m_WarningSeverityString;
545  case Error:
546  return m_ErrorSeverityString;
547  default:
548  return m_FatalSeverityString;
549  }
550 }
551 
552 Log::LogCallback::~LogCallback() = default;
Bootstrap structure passed to the kernel entry point.
void release()
Definition: Spinlock.cc:273
StaticLogEntry m_Buffer
Definition: Log.h:327
size_t m_HashMatchedCount
Definition: Log.h:346
EXPORTED_PUBLIC void addEntry(const LogEntry &entry, bool lock=true, bool flush=true)
Definition: Log.cc:369
Iterator begin()
Definition: Vector.h:148
Iterator end()
Definition: Vector.h:160
Definition: cmd.h:30
void initialise2()
Definition: Log.cc:138
bool m_Timestamps
Definition: Log.h:349
StaticString< LOG_LENGTH > str
Definition: Log.h:245
Time::Timestamp m_LastTime
Definition: Log.h:352
A vector / dynamic array.
virtual Timer * getTimer()=0
static TinyStaticString m_LineEnding
Definition: Log.h:365
the kernel&#39;s log
Definition: Log.h:166
size_t getStaticEntryCount() const
Definition: Log.cc:225
bool acquire(bool recurse=false, bool safe=true)
Definition: Spinlock.cc:43
Definition: String.h:49
SeverityLevel severity
Definition: Log.h:243
const StaticLogEntry & getStaticEntry(size_t n) const
Definition: Log.cc:235
StaticLogEntry m_StaticLog[LOG_ENTRIES]
Definition: Log.h:317
void allowHashing(bool computeNow=false)
Definition: StaticString.h:563
Log()
Definition: Log.cc:76
SeverityLevel m_LastEntrySeverity
Definition: Log.h:343
const DynamicLogEntry & getDynamicEntry(size_t n) const
Definition: Log.cc:240
EXPORTED_PUBLIC Log & operator<<(const LogEntry &entry)
Definition: Log.cc:352
virtual uint64_t getTickCount()=0
bool m_EchoToSerial
Definition: Log.h:330
unsigned int timestamp
Definition: Log.h:241
size_t m_StaticEntries
Definition: Log.h:321
NumberType
Definition: Log.h:133
Definition: Log.h:136
EXPORTED_PUBLIC void removeCallback(LogCallback *pCallback)
Definition: Log.cc:209
Spinlock m_Lock
Definition: Log.h:193
size_t getDynamicEntryCount() const
Definition: Log.cc:230
#define LOG_ENTRIES
Definition: Log.h:127
SeverityLevel
Definition: Log.h:180
static EXPORTED_PUBLIC Log m_Instance
Definition: Log.h:337
LogCallback * m_OutputCallbacks[LOG_CALLBACK_COUNT]
Definition: Log.h:333
Definition: Log.h:140
static EXPORTED_PUBLIC Log & instance()
Definition: Log.cc:108
static void breakpoint()
void initialise1()
Definition: Log.cc:113
static ProcessorId id()
Definition: Processor.cc:40
LogEntry & operator<<(const char *)
Definition: Log.cc:260
LogEntry()
Definition: Log.cc:255
Modifier
Definition: Log.h:144
void disableHashing()
Definition: StaticString.h:573
Definition: Log.h:147
Definition: Log.h:138
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition: panic.cc:121
uint64_t m_LastEntryHash
Definition: Log.h:340
void flushEntry(bool lock=true)
Definition: Log.cc:378
#define LOG_CALLBACK_COUNT
Definition: Log.h:130
Definition: errors.h:24
static TinyStaticString m_DebugSeverityString
Definition: Log.h:358
EXPORTED_PUBLIC void installCallback(LogCallback *pCallback, bool bSkipBacklog=false)
Definition: Log.cc:146
static NormalStaticString m_DedupeHead
Definition: Log.h:368
~Log()
Definition: Log.cc:101
NormalStaticString m_CachedTimestamp
Definition: Log.h:355
NumberType numberType
Definition: Log.h:247