src/debug/debug.cc
Namespaces
Source code
// Copyright 2026 The Tenzr Project Authors
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "tenzr/debug/debug.h"
#include <cmath>
#include <iomanip>
#include <sstream>
#include "tenzr/common/error.h"
#include "tenzr/txpr/emitter.h"
namespace tenzr::debug {
void Print(std::string_view label, const Array& x, bool print_data, std::ostream& os) {
if (x.IsSymbolic()) {
os << "[tenzr::debug::print] " << label << ": VarId=" << x.SymbolicVarId().id << ", shape=" << x.shape().ToString() << ", dtype=" << ToString(x.Dtype())
<< " (Symbolic Tracer)\n";
} else {
os << "[tenzr::debug::print] " << label << ": shape=" << x.shape().ToString() << ", dtype=" << ToString(x.Dtype()) << "\n";
if (print_data) {
os << x << "\n";
}
}
}
void CheckFinite(const Array& x, std::string_view op_context) {
if (x.IsSymbolic() || !x.IsDefined()) {
return;
}
if (x.Dtype() != DType::kFloat32 && x.Dtype() != DType::kFloat64) {
return;
}
size_t nan_count = 0;
size_t inf_count = 0;
const size_t total = static_cast<size_t>(x.shape().NumElements());
if (x.Dtype() == DType::kFloat32) {
auto vec = x.ToVector<float>();
for (float v : vec) {
if (std::isnan(v)) {
nan_count++;
} else if (std::isinf(v)) {
inf_count++;
}
}
} else {
auto vec = x.ToVector<double>();
for (double v : vec) {
if (std::isnan(v)) {
nan_count++;
} else if (std::isinf(v)) {
inf_count++;
}
}
}
if (nan_count > 0 || inf_count > 0) {
std::ostringstream oss;
oss << "[tenzr::debug::check_finite] Detected non-finite values in " << (op_context.empty() ? "Array" : std::string(op_context)) << ": " << nan_count
<< " NaNs, " << inf_count << " Infs out of " << total << " elements (shape: " << x.shape().ToString() << ", dtype: " << ToString(x.Dtype()) << ")";
TENZR_THROW(oss.str());
}
}
std::string Inspect(const Array& x) {
std::ostringstream oss;
oss << "=== Tenzr Array Forensic Inspection ===\n";
oss << " Defined: " << (x.IsDefined() ? "true" : "false") << "\n";
if (!x.IsDefined()) {
return oss.str();
}
oss << " Symbolic: " << (x.IsSymbolic() ? "true (Tracer active)" : "false (Materialized)") << "\n";
oss << " Shape: " << x.shape().ToString() << " (Rank: " << x.shape().Rank() << ")\n";
oss << " DType: " << ToString(x.Dtype()) << "\n";
oss << " Num Elements: " << x.shape().NumElements() << "\n";
oss << " Total Bytes: " << (x.shape().NumElements() * static_cast<int64_t>(SizeOf(x.Dtype()))) << " bytes\n";
oss << " Residency: Host=" << (x.HasData() ? "Yes" : "No") << ", Device=" << (x.HasDeviceData() ? "Yes" : "No") << "\n";
if (!x.IsSymbolic() && (x.Dtype() == DType::kFloat32 || x.Dtype() == DType::kFloat64)) {
size_t nans = 0;
size_t infs = 0;
double sum = 0.0;
double min_val = std::numeric_limits<double>::infinity();
double max_val = -std::numeric_limits<double>::infinity();
if (x.Dtype() == DType::kFloat32) {
auto vec = x.ToVector<float>();
for (float v : vec) {
if (std::isnan(v)) {
nans++;
} else if (std::isinf(v)) {
infs++;
} else {
double d = static_cast<double>(v);
sum += d;
min_val = std::min(min_val, d);
max_val = std::max(max_val, d);
}
}
} else {
auto vec = x.ToVector<double>();
for (double v : vec) {
if (std::isnan(v)) {
nans++;
} else if (std::isinf(v)) {
infs++;
} else {
sum += v;
min_val = std::min(min_val, v);
max_val = std::max(max_val, v);
}
}
}
size_t finite_count = static_cast<size_t>(x.shape().NumElements()) - nans - infs;
oss << " Finite State: " << ((nans == 0 && infs == 0) ? "Valid" : "DEGRADED") << " (" << nans << " NaNs, " << infs << " Infs)\n";
if (finite_count > 0) {
oss << " Statistics: Min=" << min_val << ", Max=" << max_val << ", Mean=" << (sum / static_cast<double>(finite_count)) << "\n";
}
}
return oss.str();
}
std::string FormatTxpr(const txpr::Txpr& txpr) {
std::ostringstream oss;
const auto& vars = txpr.Variables();
const auto& in_binders = txpr.InBinders();
const auto& out_binders = txpr.OutBinders();
const auto& eqns = txpr.Equations();
oss << "txpr {\n";
oss << " inputs: (";
for (size_t i = 0; i < in_binders.size(); ++i) {
uint32_t id = in_binders[i].id;
const auto& v = vars[id];
oss << "v" << id << ": " << ToString(v.dtype) << v.shape.ToString();
if (i + 1 < in_binders.size()) {
oss << ", ";
}
}
oss << ")\n";
for (const auto& eqn : eqns) {
oss << " ";
for (size_t i = 0; i < eqn.out_vars.size(); ++i) {
uint32_t oid = eqn.out_vars[i].id;
const auto& ov = vars[oid];
oss << "v" << oid << ": " << ToString(ov.dtype) << ov.shape.ToString();
if (i + 1 < eqn.out_vars.size()) {
oss << ", ";
}
}
oss << " = " << txpr::OpCodeToString(eqn.op);
if (eqn.op == txpr::OpCode::kCustomOp && !eqn.attrs.custom_op_name.empty()) {
oss << "(" << eqn.attrs.custom_op_name << ")";
}
oss << " [ ";
for (size_t i = 0; i < eqn.in_vars.size(); ++i) {
oss << "v" << eqn.in_vars[i].id;
if (i + 1 < eqn.in_vars.size()) {
oss << ", ";
}
}
oss << " ]\n";
}
oss << " outputs: (";
for (size_t i = 0; i < out_binders.size(); ++i) {
oss << "v" << out_binders[i].id;
if (i + 1 < out_binders.size()) {
oss << ", ";
}
}
oss << ")\n";
oss << "}\n";
return oss.str();
}
void DumpTxpr(const txpr::Txpr& txpr, std::ostream& os) { os << FormatTxpr(txpr); }
std::string FormatStablehlo(const txpr::Txpr& txpr) {
txpr::TxprEmitter emitter;
return emitter.Emit(txpr).mlir_text;
}
void DumpStablehlo(const txpr::Txpr& txpr, std::ostream& os) { os << FormatStablehlo(txpr) << "\n"; }
} // namespace tenzr::debug