1// SPDX-License-Identifier: Apache-2.0
2// ----------------------------------------------------------------------------
3// Copyright 2021-2023 Arm Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License"); you may not
6// use this file except in compliance with the License. You may obtain a copy
7// of the License at:
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14// License for the specific language governing permissions and limitations
15// under the License.
16// ----------------------------------------------------------------------------
17
18/**
19 * @brief Functions for the library entrypoint.
20 */
21
22#if defined(ASTCENC_DIAGNOSTICS)
23
24#include <cassert>
25#include <cstdarg>
26#include <cstdio>
27#include <cmath>
28#include <limits>
29#include <string>
30
31#include "astcenc_diagnostic_trace.h"
32
33/** @brief The global trace logger. */
34static TraceLog* g_TraceLog = nullptr;
35
36/** @brief The JSON indentation level. */
37static const size_t g_trace_indent = 2;
38
39TraceLog::TraceLog(
40	const char* file_name):
41	m_file(file_name, std::ofstream::out | std::ofstream::binary)
42{
43	assert(!g_TraceLog);
44	g_TraceLog = this;
45	m_root = new TraceNode("root");
46}
47
48/* See header for documentation. */
49TraceNode* TraceLog::get_current_leaf()
50{
51	if (m_stack.size())
52	{
53		return m_stack.back();
54	}
55
56	return nullptr;
57}
58
59/* See header for documentation. */
60size_t TraceLog::get_depth()
61{
62	return m_stack.size();
63}
64
65/* See header for documentation. */
66TraceLog::~TraceLog()
67{
68	assert(g_TraceLog == this);
69	delete m_root;
70	g_TraceLog = nullptr;
71}
72
73/* See header for documentation. */
74TraceNode::TraceNode(
75	const char* format,
76	...
77) {
78	// Format the name string
79	constexpr size_t bufsz = 256;
80	char buffer[bufsz];
81
82	va_list args;
83	va_start (args, format);
84	vsnprintf (buffer, bufsz, format, args);
85	va_end (args);
86
87	// Guarantee there is a nul terminator
88	buffer[bufsz - 1] = 0;
89
90	// Generate the node
91	TraceNode* parent = g_TraceLog->get_current_leaf();
92	size_t depth = g_TraceLog->get_depth();
93	g_TraceLog->m_stack.push_back(this);
94
95	bool comma = parent && parent->m_attrib_count;
96	auto& out = g_TraceLog->m_file;
97
98	if (parent)
99	{
100		parent->m_attrib_count++;
101	}
102
103	if (comma)
104	{
105		out << ',';
106	}
107
108	if (depth)
109	{
110		out << '\n';
111	}
112
113	size_t out_indent = (depth * 2) * g_trace_indent;
114	size_t in_indent = (depth * 2 + 1) * g_trace_indent;
115
116	std::string out_indents("");
117	if (out_indent)
118	{
119		out_indents = std::string(out_indent, ' ');
120	}
121
122	std::string in_indents(in_indent, ' ');
123
124	out << out_indents << "[ \"node\", \"" << buffer << "\",\n";
125	out << in_indents << "[";
126}
127
128/* See header for documentation. */
129void TraceNode::add_attrib(
130	std::string type,
131	std::string key,
132	std::string value
133) {
134	(void)type;
135
136	size_t depth = g_TraceLog->get_depth();
137	size_t indent = (depth * 2) * g_trace_indent;
138	auto& out = g_TraceLog->m_file;
139	bool comma = m_attrib_count;
140	m_attrib_count++;
141
142	if (comma)
143	{
144		out << ',';
145	}
146
147	out << '\n';
148	out << std::string(indent, ' ') << "[ "
149	                                << "\"" << key << "\", "
150	                                << value << " ]";
151}
152
153/* See header for documentation. */
154TraceNode::~TraceNode()
155{
156	g_TraceLog->m_stack.pop_back();
157
158	auto& out = g_TraceLog->m_file;
159	size_t depth = g_TraceLog->get_depth();
160	size_t out_indent = (depth * 2) * g_trace_indent;
161	size_t in_indent = (depth * 2 + 1) * g_trace_indent;
162
163	std::string out_indents("");
164	if (out_indent)
165	{
166		out_indents = std::string(out_indent, ' ');
167	}
168
169	std::string in_indents(in_indent, ' ');
170
171	if (m_attrib_count)
172	{
173		out << "\n" << in_indents;
174	}
175	out << "]\n";
176
177	out << out_indents << "]";
178}
179
180/* See header for documentation. */
181void trace_add_data(
182	const char* key,
183	const char* format,
184	...
185) {
186	constexpr size_t bufsz = 256;
187	char buffer[bufsz];
188
189	va_list args;
190	va_start (args, format);
191	vsnprintf (buffer, bufsz, format, args);
192	va_end (args);
193
194	// Guarantee there is a nul terminator
195	buffer[bufsz - 1] = 0;
196
197	std::string value = "\"" + std::string(buffer) + "\"";
198
199	TraceNode* node = g_TraceLog->get_current_leaf();
200	node->add_attrib("str", key, value);
201}
202
203/* See header for documentation. */
204void trace_add_data(
205	const char* key,
206	float value
207) {
208	// Turn infinities into parseable values
209	if (std::isinf(value))
210	{
211		if (value > 0.0f)
212		{
213			value = std::numeric_limits<float>::max();
214		}
215		else
216		{
217			value = -std::numeric_limits<float>::max();
218		}
219	}
220
221	char buffer[256];
222	sprintf(buffer, "%.20g", (double)value);
223	TraceNode* node = g_TraceLog->get_current_leaf();
224	node->add_attrib("float", key, buffer);
225}
226
227/* See header for documentation. */
228void trace_add_data(
229	const char* key,
230	int value
231) {
232	TraceNode* node = g_TraceLog->get_current_leaf();
233	node->add_attrib("int", key, std::to_string(value));
234}
235
236/* See header for documentation. */
237void trace_add_data(
238	const char* key,
239	unsigned int value
240) {
241	TraceNode* node = g_TraceLog->get_current_leaf();
242	node->add_attrib("int", key, std::to_string(value));
243}
244
245#endif
246