rusefi-1/firmware/util/loggingcentral.cpp

177 lines
4.6 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file loggingcentral.cpp
*
2019-05-04 07:18:49 -07:00
*
* As of May 2019 we have given up on text-based 'push' terminal mode. At the moment binary protocol
* is the consumen of this logging buffer.
*
2015-07-10 06:01:56 -07:00
* @date Mar 8, 2015
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*/
2018-09-16 19:26:57 -07:00
#include "global.h"
2015-07-10 06:01:56 -07:00
#include "efilib.h"
2019-04-12 19:10:57 -07:00
#if ! EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
typedef char log_buf_t[DL_OUTPUT_BUFFER];
/**
* we need to leave a byte for zero terminator, also two bytes for the \r\n in
* printWithLength, also couple of bytes just in case
*/
#define MAX_DL_CAPACITY (DL_OUTPUT_BUFFER - 5)
/**
* This is the buffer into which all the data providers write
*/
static char *accumulationBuffer;
2017-05-17 17:36:07 -07:00
static log_buf_t pendingBuffers0;
2015-07-10 06:01:56 -07:00
static log_buf_t pendingBuffers1;
/**
* We copy all the pending data into this buffer once we are ready to push it out
*/
static char * outputBuffer;
class LoggingCentral {
public:
/**
* Class constructors are a great way to have simple initialization sequence
*/
LoggingCentral() {
pendingBuffers0[0] = 0;
pendingBuffers1[0] = 0;
accumulationBuffer = pendingBuffers0;
outputBuffer = pendingBuffers1;
accumulatedSize = 0;
}
/**
* amount of data accumulated so far
*/
uint32_t accumulatedSize;
};
static LoggingCentral loggingCentral;
2015-07-10 06:01:56 -07:00
/**
2019-05-04 07:18:49 -07:00
* This method appends the content of specified thread-local logger into the global buffer
* of logging content.
2015-07-10 06:01:56 -07:00
*/
void scheduleLogging(Logging *logging) {
2019-04-12 19:10:57 -07:00
#if EFI_TEXT_LOGGING
#ifdef EFI_PRINT_MESSAGES_TO_TERMINAL
print(logging->buffer);
print("\r\n");
#endif /* EFI_PRINT_MESSAGES_TO_TERMINAL */
2015-07-10 06:01:56 -07:00
// this could be done without locking
int newLength = efiStrlen(logging->buffer);
bool alreadyLocked = lockOutputBuffer();
if (loggingCentral.accumulatedSize + newLength >= MAX_DL_CAPACITY) {
2015-07-10 06:01:56 -07:00
/**
* if no one is consuming the data we have to drop it
* this happens in case of serial-over-USB, todo: find a better solution?
*/
if (!alreadyLocked) {
unlockOutputBuffer();
}
resetLogging(logging);
return;
}
// memcpy is faster then strcpy because it is not looking for line terminator
memcpy(accumulationBuffer + loggingCentral.accumulatedSize, logging->buffer, newLength + 1);
loggingCentral.accumulatedSize += newLength;
2015-07-10 06:01:56 -07:00
if (!alreadyLocked) {
unlockOutputBuffer();
}
resetLogging(logging);
#endif /* EFI_TEXT_LOGGING */
2015-07-10 06:01:56 -07:00
}
/**
2019-05-04 07:18:49 -07:00
* Actual communication layer invokes this method when it's ready to send some data out
*
2015-07-10 06:01:56 -07:00
* this method should always be invoked from the same thread!
2019-05-04 07:18:49 -07:00
* @return pointer to the buffer which should be print to console
2015-07-10 06:01:56 -07:00
*/
char * swapOutputBuffers(int *actualOutputBufferSize) {
2019-04-12 19:10:57 -07:00
#if EFI_ENABLE_ASSERTS
2015-07-10 06:01:56 -07:00
int expectedOutputSize;
2016-12-30 09:03:15 -08:00
#endif /* EFI_ENABLE_ASSERTS */
2015-07-10 06:01:56 -07:00
{ // start of critical section
2017-06-03 19:27:05 -07:00
bool alreadyLocked = lockOutputBuffer();
2015-07-10 06:01:56 -07:00
/**
* we cannot output under syslock, we simply rotate which buffer is which
*/
char *temp = outputBuffer;
2019-04-12 19:10:57 -07:00
#if EFI_ENABLE_ASSERTS
expectedOutputSize = loggingCentral.accumulatedSize;
2015-07-10 06:01:56 -07:00
#endif /* EFI_ENABLE_ASSERTS */
outputBuffer = accumulationBuffer;
accumulationBuffer = temp;
loggingCentral.accumulatedSize = 0;
2015-07-10 06:01:56 -07:00
accumulationBuffer[0] = 0;
2017-06-03 19:27:05 -07:00
if (!alreadyLocked) {
unlockOutputBuffer();
}
2015-07-10 06:01:56 -07:00
} // end of critical section
*actualOutputBufferSize = efiStrlen(outputBuffer);
2019-04-12 19:10:57 -07:00
#if EFI_ENABLE_ASSERTS
2016-12-30 09:03:15 -08:00
if (*actualOutputBufferSize != expectedOutputSize) {
2017-04-22 06:50:09 -07:00
int sizeToShow = minI(10, *actualOutputBufferSize);
int offsetToShow = *actualOutputBufferSize - sizeToShow;
2017-05-29 16:23:15 -07:00
firmwareError(ERROR_LOGGING_SIZE_CALC, "lsize mismatch %d/%d [%s]", *actualOutputBufferSize, expectedOutputSize,
2017-04-22 06:50:09 -07:00
&outputBuffer[offsetToShow]);
2016-12-30 09:03:15 -08:00
return NULL;
}
#endif /* EFI_ENABLE_ASSERTS */
2015-07-10 06:01:56 -07:00
return outputBuffer;
}
2018-08-31 19:30:03 -07:00
/**
2019-05-04 07:18:49 -07:00
* rusEfi business logic invokes this method in order to eventually print stuff to rusEfi console
*
2018-08-31 19:30:03 -07:00
* this whole method is executed under syslock so that we can have multiple threads use the same shared buffer
* in order to reduce memory usage
2019-05-04 07:18:49 -07:00
*
* this is really 'global lock + printf + scheduleLogging + unlock' a bit more clear
2018-08-31 19:30:03 -07:00
*/
void scheduleMsg(Logging *logging, const char *fmt, ...) {
2019-04-13 07:59:29 -07:00
for (unsigned int i = 0;i<strlen(fmt);i++) {
2019-05-04 07:18:49 -07:00
// todo: open question which layer would not handle CR/LF properly?
2019-04-13 07:59:29 -07:00
efiAssertVoid(OBD_PCM_Processor_Fault, fmt[i] != '\n', "No CRLF please");
}
2019-04-12 19:10:57 -07:00
#if EFI_TEXT_LOGGING
2018-08-31 19:30:03 -07:00
if (logging == NULL) {
warning(CUSTOM_ERR_LOGGING_NULL, "logging NULL");
return;
}
int wasLocked = lockAnyContext();
resetLogging(logging); // todo: is 'reset' really needed here?
appendMsgPrefix(logging);
va_list ap;
va_start(ap, fmt);
logging->vappendPrintf(fmt, ap);
va_end(ap);
appendMsgPostfix(logging);
scheduleLogging(logging);
if (!wasLocked) {
unlockAnyContext();
}
#endif /* EFI_TEXT_LOGGING */
2018-08-31 19:30:03 -07:00
}
2015-07-10 06:01:56 -07:00
#endif /* EFI_UNIT_TEST */