1 // SPDX-License-Identifier: Apache-2.0
2 // ----------------------------------------------------------------------------
3 // Copyright 2021-2022 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 <string>
28 
29 #include "astcenc_diagnostic_trace.h"
30 
31 /** @brief The global trace logger. */
32 static TraceLog* g_TraceLog = nullptr;
33 
34 /** @brief The JSON indentation level. */
35 static const size_t g_trace_indent = 2;
36 
TraceLog(const char * file_name)37 TraceLog::TraceLog(
38 	const char* file_name):
39 	m_file(file_name, std::ofstream::out | std::ofstream::binary)
40 {
41 	assert(!g_TraceLog);
42 	g_TraceLog = this;
43 	m_root = new TraceNode("root");
44 }
45 
46 /* See header for documentation. */
get_current_leaf()47 TraceNode* TraceLog::get_current_leaf()
48 {
49 	if (m_stack.size())
50 	{
51 		return m_stack.back();
52 	}
53 
54 	return nullptr;
55 }
56 
57 /* See header for documentation. */
get_depth()58 size_t TraceLog::get_depth()
59 {
60 	return m_stack.size();
61 }
62 
63 /* See header for documentation. */
~TraceLog()64 TraceLog::~TraceLog()
65 {
66 	assert(g_TraceLog == this);
67 	delete m_root;
68 	g_TraceLog = nullptr;
69 }
70 
71 /* See header for documentation. */
TraceNode(const char * format,...)72 TraceNode::TraceNode(
73 	const char* format,
74 	...
75 ) {
76 	// Format the name string
77 	constexpr size_t bufsz = 256;
78 	char buffer[bufsz];
79 
80 	va_list args;
81 	va_start (args, format);
82 	vsnprintf (buffer, bufsz, format, args);
83 	va_end (args);
84 
85 	// Guarantee there is a nul terminator
86 	buffer[bufsz - 1] = 0;
87 
88 	// Generate the node
89 	TraceNode* parent = g_TraceLog->get_current_leaf();
90 	size_t depth = g_TraceLog->get_depth();
91 	g_TraceLog->m_stack.push_back(this);
92 
93 	bool comma = parent && parent->m_attrib_count;
94 	auto& out = g_TraceLog->m_file;
95 
96 	if (parent)
97 	{
98 		parent->m_attrib_count++;
99 	}
100 
101 	if (comma)
102 	{
103 		out << ',';
104 	}
105 
106 	if (depth)
107 	{
108 		out << '\n';
109 	}
110 
111 	size_t out_indent = (depth * 2) * g_trace_indent;
112 	size_t in_indent = (depth * 2 + 1) * g_trace_indent;
113 
114 	std::string out_indents("");
115 	if (out_indent)
116 	{
117 		out_indents = std::string(out_indent, ' ');
118 	}
119 
120 	std::string in_indents(in_indent, ' ');
121 
122 	out << out_indents << "[ \"node\", \"" << buffer << "\",\n";
123 	out << in_indents << "[";
124 }
125 
126 /* See header for documentation. */
add_attrib(std::string type,std::string key,std::string value)127 void TraceNode::add_attrib(
128 	std::string type,
129 	std::string key,
130 	std::string value
131 ) {
132 	(void)type;
133 
134 	size_t depth = g_TraceLog->get_depth();
135 	size_t indent = (depth * 2) * g_trace_indent;
136 	auto& out = g_TraceLog->m_file;
137 	bool comma = m_attrib_count;
138 	m_attrib_count++;
139 
140 	if (comma)
141 	{
142 		out << ',';
143 	}
144 
145 	out << '\n';
146 	out << std::string(indent, ' ') << "[ "
147 	                                << "\"" << key << "\", "
148 	                                << value << " ]";
149 }
150 
151 /* See header for documentation. */
~TraceNode()152 TraceNode::~TraceNode()
153 {
154 	g_TraceLog->m_stack.pop_back();
155 
156 	auto& out = g_TraceLog->m_file;
157 	size_t depth = g_TraceLog->get_depth();
158 	size_t out_indent = (depth * 2) * g_trace_indent;
159 	size_t in_indent = (depth * 2 + 1) * g_trace_indent;
160 
161 	std::string out_indents("");
162 	if (out_indent)
163 	{
164 		out_indents = std::string(out_indent, ' ');
165 	}
166 
167 	std::string in_indents(in_indent, ' ');
168 
169 	if (m_attrib_count)
170 	{
171 		out << "\n" << in_indents;
172 	}
173 	out << "]\n";
174 
175 	out << out_indents << "]";
176 }
177 
178 /* See header for documentation. */
trace_add_data(const char * key,const char * format,...)179 void trace_add_data(
180 	const char* key,
181 	const char* format,
182 	...
183 ) {
184 	constexpr size_t bufsz = 256;
185 	char buffer[bufsz];
186 
187 	va_list args;
188 	va_start (args, format);
189 	vsnprintf (buffer, bufsz, format, args);
190 	va_end (args);
191 
192 	// Guarantee there is a nul terminator
193 	buffer[bufsz - 1] = 0;
194 
195 	std::string value = "\"" + std::string(buffer) + "\"";
196 
197 	TraceNode* node = g_TraceLog->get_current_leaf();
198 	node->add_attrib("str", key, value);
199 }
200 
201 /* See header for documentation. */
trace_add_data(const char * key,float value)202 void trace_add_data(
203 	const char* key,
204 	float value
205 ) {
206   	char buffer[256];
207 	sprintf(buffer, "%.20g", (double)value);
208 	TraceNode* node = g_TraceLog->get_current_leaf();
209 	node->add_attrib("float", key, buffer);
210 }
211 
212 /* See header for documentation. */
trace_add_data(const char * key,int value)213 void trace_add_data(
214 	const char* key,
215 	int value
216 ) {
217 	TraceNode* node = g_TraceLog->get_current_leaf();
218 	node->add_attrib("int", key, std::to_string(value));
219 }
220 
221 /* See header for documentation. */
trace_add_data(const char * key,unsigned int value)222 void trace_add_data(
223 	const char* key,
224 	unsigned int value
225 ) {
226 	TraceNode* node = g_TraceLog->get_current_leaf();
227 	node->add_attrib("int", key, std::to_string(value));
228 }
229 
230 #endif
231