Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | /** | |||
2 | * @file datalogging.cpp | |||
3 | * @brief Buffered console output stream code | |||
4 | * | |||
5 | * Here we have a memory buffer and methods related to | |||
6 | * printing messages into this buffer. The purpose of the | |||
7 | * buffer is to allow fast, non-blocking, thread-safe logging. | |||
8 | * | |||
9 | * The idea is that each interrupt handler would have it's own logging buffer. You can add | |||
10 | * stuff into this buffer without any locking since it's you own buffer, and once you get | |||
11 | * the whole message you invoke the scheduleLogging() method which appends your local content | |||
12 | * into the global logging buffer, from which it is later dispatched to the console by our | |||
13 | * main console thread. | |||
14 | * | |||
15 | * @date Feb 25, 2013 | |||
16 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
17 | * | |||
18 | * This file is part of rusEfi - see http://rusefi.com | |||
19 | * | |||
20 | * rusEfi is free software; you can redistribute it and/or modify it under the terms of | |||
21 | * the GNU General Public License as published by the Free Software Foundation; either | |||
22 | * version 3 of the License, or (at your option) any later version. | |||
23 | * | |||
24 | * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without | |||
25 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
26 | * GNU General Public License for more details. | |||
27 | * | |||
28 | * You should have received a copy of the GNU General Public License along with this program. | |||
29 | * If not, see <http://www.gnu.org/licenses/>. | |||
30 | * | |||
31 | */ | |||
32 | ||||
33 | #include "pch.h" | |||
34 | ||||
35 | #if ! EFI_UNIT_TEST | |||
36 | ||||
37 | #include "chmtx.h" | |||
38 | #include "memstreams.h" | |||
39 | #include "console_io.h" | |||
40 | #endif // EFI_UNIT_TEST | |||
41 | ||||
42 | /** | |||
43 | * @returns true if data does not fit into this buffer | |||
44 | */ | |||
45 | ✗ | bool Logging::validateBuffer(uint32_t extraLen) { | ||
46 | ✗ | if (remainingSize() < extraLen + 1) { | ||
47 | #if EFI_PROD_CODE | |||
48 | warning(ObdCode::CUSTOM_LOGGING_BUFFER_OVERFLOW, "output overflow %s %d", name, extraLen); | |||
49 | #endif /* EFI_PROD_CODE */ | |||
50 | ✗ | return true; | ||
51 | } | |||
52 | ||||
53 | ✗ | return false; | ||
54 | } | |||
55 | ||||
56 | ✗ | void Logging::append(const char *text) { | ||
57 | ✗ | efiAssertVoid(ObdCode::CUSTOM_APPEND_NULL, text != NULL, "append NULL"); | ||
58 | ✗ | size_t extraLen = std::strlen(text); | ||
59 | ✗ | bool isCapacityProblem = validateBuffer(extraLen); | ||
60 | ✗ | if (isCapacityProblem) { | ||
61 | ✗ | return; | ||
62 | } | |||
63 | ✗ | strcpy(linePointer, text); | ||
64 | /** | |||
65 | * And now we are pointing at the zero char at the end of the buffer again | |||
66 | */ | |||
67 | ✗ | linePointer += extraLen; | ||
68 | } | |||
69 | ||||
70 | /** | |||
71 | * @note This method if fast because it does not validate much, be sure what you are doing | |||
72 | */ | |||
73 | 79743 | void Logging::appendFast(const char *text) { | ||
74 | 79743 | char *s = linePointer; | ||
75 |
2/2✓ Branch 0 taken 228761 times.
✓ Branch 1 taken 79743 times.
|
2/2✓ Decision 'true' taken 228761 times.
✓ Decision 'false' taken 79743 times.
|
308504 | while ((*s++ = *text++) != 0) |
76 | ; | |||
77 | 79743 | linePointer = s - 1; | ||
78 | 79743 | } | ||
79 | ||||
80 | 583 | void Logging::appendPrintf(const char *fmt, ...) { | ||
81 | efiAssertVoid(ObdCode::CUSTOM_APPEND_STACK, hasLotsOfRemainingStack(), "lowstck#4"); | |||
82 | ||||
83 | 583 | size_t available = remainingSize(); | ||
84 | ||||
85 | 583 | va_list ap; | ||
86 | 583 | va_start(ap, fmt); | ||
87 | 583 | size_t written = chvsnprintf(linePointer, available, fmt, ap); | ||
88 | 583 | va_end(ap); | ||
89 | ||||
90 | // chvnsprintf returns how many bytes WOULD HAVE been written if it fit, | |||
91 | // so clip it to the available space if necessary | |||
92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 583 times.
|
583 | linePointer += (written > available) ? available : written; | |
93 | // ensure buffer is always null terminated | |||
94 | 583 | buffer[bufferSize - 1] = '\0'; | ||
95 | } | |||
96 | ||||
97 | ✗ | void Logging::appendFloat(float value, int precision) { | ||
98 | /** | |||
99 | * todo: #1 this implementation is less than perfect | |||
100 | * todo: #2 The only way to avoid double promotion would probably be using *float instead of float | |||
101 | * See also http://stackoverflow.com/questions/5522051/printing-a-float-in-c-while-avoiding-variadic-parameter-promotion-to-double | |||
102 | */ | |||
103 | ✗ | switch (precision) { | ||
104 | ✗ | case 1: | ||
105 | ✗ | appendPrintf("%.1f", value); | ||
106 | ✗ | break; | ||
107 | ✗ | case 2: | ||
108 | ✗ | appendPrintf("%.2f", value); | ||
109 | ✗ | break; | ||
110 | ✗ | case 3: | ||
111 | ✗ | appendPrintf("%.3f", value); | ||
112 | ✗ | break; | ||
113 | ✗ | case 4: | ||
114 | ✗ | appendPrintf("%.4f", value); | ||
115 | ✗ | break; | ||
116 | ✗ | case 5: | ||
117 | ✗ | appendPrintf("%.5f", value); | ||
118 | ✗ | break; | ||
119 | ✗ | case 6: | ||
120 | ✗ | appendPrintf("%.6f", value); | ||
121 | ✗ | break; | ||
122 | ||||
123 | ✗ | default: | ||
124 | ✗ | appendPrintf("%.2f", value); | ||
125 | } | |||
126 | ✗ | } | ||
127 | ||||
128 | 584 | void Logging::reset() { | ||
129 | 584 | linePointer = buffer; | ||
130 | 584 | *linePointer = 0; | ||
131 | 584 | } | ||
132 | ||||
133 | 1 | Logging::Logging(char const *p_name, char *p_buffer, int p_bufferSize) | ||
134 | 1 | : name(p_name) | ||
135 | 1 | , buffer(p_buffer) | ||
136 | 1 | , bufferSize(p_bufferSize) | ||
137 | { | |||
138 | 1 | reset(); | ||
139 | 1 | } | ||
140 | ||||
141 | ✗ | LoggingWithStorage::LoggingWithStorage(const char *p_name) : Logging(p_name, DEFAULT_BUFFER, sizeof(DEFAULT_BUFFER)) { | ||
142 | ✗ | } | ||
143 |