blob: 262f361eb01bff88409d0376f47f87944c6c9d82 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/zxdb/symbols/dwarf_stack_entry.h"
#include <float.h>
#include <lib/syslog/cpp/macros.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <iostream>
namespace zxdb {
DwarfStackEntry::DwarfStackEntry(uint128_t generic_value) {
bzero(&data_, sizeof(data_));
data_.unsigned_value = generic_value;
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, const void* data, size_t data_size)
: type_(std::move(type)) {
bzero(&data_, sizeof(data_));
memcpy(&data_, data, std::min(data_size, sizeof(data_)));
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, int128_t value)
: type_(std::move(type)) {
FX_DCHECK(TreatAsSigned());
bzero(&data_, sizeof(data_));
data_.signed_value = value;
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, uint128_t value)
: type_(std::move(type)) {
FX_DCHECK(TreatAsUnsigned());
bzero(&data_, sizeof(data_));
data_.unsigned_value = value;
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, float value) : type_(std::move(type)) {
FX_DCHECK(TreatAsFloat());
bzero(&data_, sizeof(data_));
data_.float_value = value;
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, double value)
: type_(std::move(type)) {
FX_DCHECK(TreatAsDouble());
bzero(&data_, sizeof(data_));
data_.double_value = value;
}
bool DwarfStackEntry::operator==(const DwarfStackEntry& other) const {
if (is_generic() != other.is_generic())
return false;
if (!is_generic()) {
// Validate base type and byte size.
if (type_->base_type() != other.type_->base_type() ||
type_->byte_size() != other.type_->byte_size())
return false;
}
if (TreatAsUnsigned())
return data_.unsigned_value == other.data_.unsigned_value;
if (TreatAsSigned())
return data_.signed_value == other.data_.signed_value;
// This is used for tests that compare the results of expressions. The floating-point error can
// accumulate much larger than DBL_EPSILON so we have our own more permissive value. If necessary,
// this can get much fancier, gtest does some more rigorous comparisons in its ASSERT_DOUBLE_EQ.
constexpr double kEpsilon = 0.000000001;
if (TreatAsFloat()) {
if (isnan(other.data_.float_value) || isnan(other.data_.float_value))
return false;
return fabsf(other.data_.float_value - other.data_.float_value) < kEpsilon;
}
if (TreatAsDouble()) {
if (isnan(data_.double_value) || isnan(other.data_.double_value))
return false;
return fabs(data_.double_value - other.data_.double_value) < kEpsilon;
}
FX_NOTREACHED();
return false;
}
size_t DwarfStackEntry::GetByteSize() const {
if (type_) {
// In case the type info specifies something like a 256 bit integer, clamp the size to the
// maximum size of our data.
return std::min<size_t>(sizeof(UnsignedType), type_->byte_size());
}
return sizeof(UnsignedType);
}
// static
bool DwarfStackEntry::TreatAsSigned(const BaseType* type) {
if (!type)
return false; // Generic types are unsigned.
return type->base_type() == BaseType::kBaseTypeSigned ||
type->base_type() == BaseType::kBaseTypeSignedChar;
}
// static
bool DwarfStackEntry::TreatAsUnsigned(const BaseType* type) {
if (!type)
return true; // Generic types are unsigned.
return type->base_type() == BaseType::kBaseTypeAddress ||
type->base_type() == BaseType::kBaseTypeBoolean ||
type->base_type() == BaseType::kBaseTypeUnsigned ||
type->base_type() == BaseType::kBaseTypeUnsignedChar ||
type->base_type() == BaseType::kBaseTypeUTF;
}
// static
bool DwarfStackEntry::TreatAsFloat(const BaseType* type) {
if (!type)
return false; // Generic types are unsigned.
return type->base_type() == BaseType::kBaseTypeFloat && type->byte_size() == 4;
}
// static
bool DwarfStackEntry::TreatAsDouble(const BaseType* type) {
if (!type)
return false; // Generic types are unsigned.
return type->base_type() == BaseType::kBaseTypeFloat && type->byte_size() == 8;
}
bool DwarfStackEntry::IsZero() const {
if (TreatAsSigned())
return data_.signed_value == 0;
if (TreatAsUnsigned())
return data_.unsigned_value == 0;
if (TreatAsFloat()) {
return !isnan(data_.float_value) && data_.float_value > -FLT_EPSILON &&
data_.float_value < FLT_EPSILON;
}
if (TreatAsDouble()) {
return !isnan(data_.double_value) && data_.double_value > -DBL_EPSILON &&
data_.double_value < DBL_EPSILON;
}
FX_NOTREACHED();
return false;
}
bool DwarfStackEntry::SameTypeAs(const DwarfStackEntry& other) const {
if (is_generic() && other.is_generic())
return true;
if (is_generic() || other.is_generic())
return false; // One is generic and the other isn't they can't match.
// Both are declared types, the types and sizes must match.
return (type_->base_type() == other.type_->base_type()) &&
(type_->byte_size() == other.type_->byte_size());
}
std::string DwarfStackEntry::GetTypeDescription() const {
if (is_generic())
return "generic";
return BaseType::BaseTypeToString(type_->base_type()) +
"(size=" + std::to_string(type_->byte_size()) + ")";
}
std::ostream& operator<<(std::ostream& out, const DwarfStackEntry& entry) {
out << "DwarfStackEntry(type=" << entry.GetTypeDescription() << ", value=";
if (entry.TreatAsUnsigned()) {
out << to_string(entry.unsigned_value());
} else if (entry.TreatAsSigned()) {
out << to_string(entry.signed_value());
} else if (entry.TreatAsFloat()) {
out << std::to_string(entry.float_value());
} else if (entry.TreatAsDouble()) {
out << std::to_string(entry.double_value());
}
return out << ")";
}
} // namespace zxdb