1 /*
2  Copyright (c) 2018, MIPI Alliance, Inc.
3  All rights reserved.
4 
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions
7  are met:
8 
9  * Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11 
12  * Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in
14    the documentation and/or other materials provided with the
15    distribution.
16 
17  * Neither the name of the copyright holder nor the names of its
18    contributors may be used to endorse or promote products derived
19    from this software without specific prior written permission.
20 
21  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 /*
35  * Contributors:
36  * Norbert Schulz (Intel Corporation) - Initial API and implementation
37  */
38 
39 #include<sstream>
40 #include <iomanip>
41 #include "mipi_syst_message.h"
42 #include "mipi_syst_collateral.h"
43 #include "mipi_syst_printf.h"
44 
45 MIPI_SYST_NAMESPACE_BEGIN
46 
47 // Code for storing and printing out decoded message content
48 //
49 
setLocAddr32(uint32_t addr)50 void message::setLocAddr32(uint32_t addr)
51 {
52 	m_loc.tag |= location_type::ADDRESS32;
53 	m_loc.tag &= ~location_type::ADDRESS64;
54 	m_loc.address = addr;
55 }
56 
setLocAddr64(uint64_t addr)57 void message::setLocAddr64(uint64_t addr)
58 {
59 	m_loc.tag |= location_type::ADDRESS64;
60 	m_loc.tag &= ~location_type::ADDRESS32;
61 	m_loc.address = addr;
62 }
63 
setLocFileLine(uint32_t file,uint32_t line)64 void message::setLocFileLine(uint32_t file, uint32_t line)
65 {
66 	m_loc.tag |= location_type::IDANDLINE;
67 	m_loc.file = file;
68 	m_loc.line = line;
69 }
70 
71 // Check type for short message
72 //
isShort(message::header hdr)73 bool message::isShort(message::header hdr) {
74 	switch (hdr.field.type) {
75 	case message::type::SHORT32:
76 	case message::type::SHORT64:
77 		return true;
78 
79 	case  message::type::BUILD:
80 		switch (hdr.field.subtype) {
81 		case message::subtype_build::BUILD_COMPACT32:
82 		case message::subtype_build::BUILD_COMPACT64:
83 			return true;
84 		}
85 	}
86 	return false;
87 }
88 
89 
90 // Pretty print decode status value
91 //
status2string(const message & msg)92 static const char * status2string(const message& msg)
93 {
94 	switch (msg.getState()) {
95 	case message::decode_state::OK:
96 		return "OK";
97 	case message::decode_state::UNKNOWN_TYPE:
98 		return "UNKNOWN_TYPE";
99 	case message::decode_state::TOO_SHORT:
100 		return "TOO_SHORT";
101 	case message::decode_state::CHECKSUM_ERROR:
102 		return "CHECKSUM_ERROR";
103 	case message::decode_state::MISSING_COLLATERAL:
104 		return "MISSING_COLLATERAL";
105 	default:
106 		return "UNKNOWN_STATE";
107 	}
108 }
109 
110 // Pretty print sevrity values
111 //
112 static const char * severity2string[] = {
113 	"MAX",
114 	"FATAL",
115 	"ERROR",
116 	"WARNING",
117 	"INFO",
118 	"USER1",
119 	"USER2",
120 	"DEBUG"
121 };
122 
123 // Pretty print type:subtype values
124 //
type2string(message::header hdr,const collateral * coll)125 static const std::string type2string(
126 	message::header hdr,
127 	const collateral * coll)
128 {
129 	static const char * typenames[] = {
130 		"BUILD",
131 		"SHORT32",
132 		"STRING",
133 		"CATALOG",
134 		"UNKNOWN(4)",
135 		"UNKNOWN(5)",
136 		"RAW",
137 		"SHORT64",
138 		"CLOCK",
139 		"UNKNOWN(9)",
140 		"UNKNOWN(10)",
141 		"UNKNOWN(11)",
142 		"UNKNOWN(12)",
143 		"UNKNOWN(13)",
144 		"UNKNOWN(14)",
145 		"UNKNOWN(15)",
146 	};
147 
148 	uint32_t type(hdr.field.type);
149 	std::stringstream sstr;
150 
151 	sstr << typenames[type];
152 
153 	// compute subtype string
154 	//
155 	std::string subtype("??");
156 
157 	switch (type) {
158 	case message::type::SHORT32:
159 	case message::type::SHORT64:
160 		subtype.clear(); // no subtype
161 		break;
162 
163 	case message::type::BUILD:
164 		switch (hdr.field.subtype) {
165 		case  message::subtype_build::BUILD_LONG:
166 			subtype = "LONG";
167 			break;
168 		case  message::subtype_build::BUILD_COMPACT32:
169 			subtype = "COMPACT32";
170 			break;
171 		case  message::subtype_build::BUILD_COMPACT64:
172 			subtype = "COMPACT64";
173 			break;
174 		}
175 		break;
176 	case  message::type::STRING:
177 		switch (hdr.field.subtype) {
178 		case  message::subtype_string::STRING_GENERIC:
179 			subtype = "GENERIC";
180 			break;
181 		case  message::subtype_string::STRING_FUNCTIONENTER:
182 			subtype = "ENTER";
183 			break;
184 		case  message::subtype_string::STRING_FUNCTIONEXIT:
185 			subtype = "EXIT";
186 			break;
187 		case  message::subtype_string::STRING_INVALIDPARAM:
188 			subtype = "INVPARAM";
189 			break;
190 		case  message::subtype_string::STRING_ASSERT:
191 			subtype = "ASSERT";
192 			break;
193 		case  message::subtype_string::STRING_PRINTF_32:
194 			subtype = "PRINTF32";
195 			break;
196 		case  message::subtype_string::STRING_PRINTF_64:
197 			subtype = "PRINTF64";
198 			break;
199 		}
200 		break;
201 	case  message::type::CATALOG:
202 		switch (hdr.field.subtype) {
203 		case  message::subtype_catalog::CATALOG_ID32_P32:
204 			subtype = "ID32P32";
205 			break;
206 		case  message::subtype_catalog::CATALOG_ID64_P32:
207 			subtype = "ID64P32";
208 			break;
209 		case  message::subtype_catalog::CATALOG_ID32_P64:
210 			subtype = "ID32P64";
211 			break;
212 		case  message::subtype_catalog::CATALOG_ID64_P64:
213 			subtype = "ID64P64";
214 			break;
215 		}
216 		break;
217 	case  message::type::RAW:
218 	{
219 		const std::string * name(nullptr);
220 		if (coll && (name = coll->getWriteType(hdr.field.subtype)) != nullptr)
221 		{
222 			std::stringstream dest;
223 			hostPrintf(dest, *name, hdr.field.subtype, std::vector<int>());
224 			subtype = dest.str();
225 		} else {
226 			subtype = std::to_string(hdr.field.subtype);
227 		}
228 	} break;
229 
230 	case  message::type::CLOCK:
231 		switch (hdr.field.subtype) {
232 		case  message::subtype_clock::CLOCK_SYNC:
233 			subtype = "SYNC";
234 			break;
235 		}
236 		break;
237 	}
238 
239 	if (!subtype.empty()) {
240 		sstr << ':' << subtype;
241 	}
242 
243 	return sstr.str();
244 }
245 
246 // CSV double-quote escaping, replace " with "" and eliminate newlines
247 //
csvQuoteString(std::ostream & os,const std::string & s)248 void csvQuoteString(std::ostream& os, const std::string& s)
249 {
250 	for (auto c : s) {
251 		if (c == '\n') {
252 			os << ' ';   // newline not supported in CSV
253 		} else if (c == '"') {
254 			os << c << c;
255 		} else {
256 			os << c;
257 		}
258 	}
259 }
260 
261 // location information printing
262 //
location2string(const message::location & loc,const collateral * collateral)263 std::string location2string(
264 	const message::location& loc,
265 	const collateral * collateral)
266 {
267 	bool hasAddress(0!=(loc.tag & (message::ADDRESS32 | message::ADDRESS32)));
268 	bool hasFileLine(0 != (loc.tag & message::IDANDLINE));
269 
270 	std::stringstream sstr;
271 
272 	if (hasFileLine) {
273 		const std::string * fileName(
274 			collateral != nullptr ?
275 				collateral->getSourceFile(loc.file) : nullptr);
276 
277 		if (fileName != nullptr) {
278 			sstr << (*fileName) << ':' << loc.line;
279 		} else {
280 			sstr << loc.file << ':' << loc.line;
281 		}
282 		if (hasAddress) sstr << ' ';
283 	}
284 
285 	if (hasAddress) {
286 		if (loc.tag & message::ADDRESS32) {
287 			sstr << toHexValue((uint32_t)loc.address);
288 		} else {
289 			sstr << toHexValue(loc.address);
290 		}
291 	}
292 
293 	return sstr.str();
294 }
295 const char message::csvHeaderString[] = "Decode Status,Payload,Type,Severity,Origin,Unit,Message TimeStamp,Context TimeStamp,Location,Raw Length,Checksum,Collateral";
296 
297 // CSV line formatting of a message
298 //
operator <<(std::ostream & os,const message & msg)299 std::ostream& operator << (std::ostream& os, const message& msg)
300 {
301 	char delimiter(',');
302 	const collateral * coll(msg.getCollateral());
303 
304 	// turn volatile fields like timestamp/crc off when running unit testing
305 	//
306 	static bool unit_testing(getenv("SYST_UNITTESTING") != NULL);
307 
308 	// status
309 	os << status2string(msg) << delimiter;
310 
311 	// payload
312 	os << '"';
313 	csvQuoteString(os, msg.getPayload());
314 	os << '"' << delimiter;
315 
316 	if (msg.getState() != message::OK && msg.getState() != message::MISSING_COLLATERAL)
317 	{
318 		// decode failed, report other fields as empty. The data can't be trusted.
319 		//
320 		os << ",,,,," << std::endl;
321 		return os;
322 	}
323 
324 	// Type/Subtype
325 	//
326 	os << type2string(msg.getHeader(), msg.getCollateral()) << delimiter;
327 
328 	// Severity
329 	//
330 	os << severity2string[msg.getHeader().field.severity] << delimiter;
331 
332 	// client name and unit
333 	//
334 	os << msg.getClientName() << delimiter;
335 	os << msg.getUnit() << delimiter;
336 
337 	// time stamps
338 	if (msg.getHeader().field.timestamp) {
339 		os << (unit_testing ?
340 			"<--UNITTEST-HIDE_TS-->" : toHexValue(msg.getMessageTS()));
341 	}
342 	os << delimiter;
343 
344 	os << (unit_testing ?
345 		"<--UNITTEST-HIDE_TS-->" : toHexValue(msg.getContextTS())) << delimiter;
346 
347 	// location
348 	//
349 	csvQuoteString(os, location2string(msg.getLocation(), coll));
350   	os << delimiter;
351 
352 	//  raw message length
353 	//
354 	os << std::to_string(msg.getLength()) << delimiter;
355 
356 	// crc
357 	//
358 	if (msg.getHeader().field.chksum) {
359 		os << (unit_testing ? "<--UNITTEST-HIDE_CRC-->" : toHexValue(msg.getCrc()));
360 	}
361 	os << delimiter;
362 
363 	if (coll != nullptr) {
364 		csvQuoteString(os, coll->getFileName());
365 	}
366 	os << std::endl;
367 
368 	return os;
369 }
370 
371 MIPI_SYST_NAMESPACE_END