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