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