blob: 41e981f30d1e84037fa420cea75ac5646087494b [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 <algorithm>
#include <iostream>
namespace zxdb {
DwarfStackEntry::DwarfStackEntry(uint128_t generic_value) : unsigned_value_(generic_value) {}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, int128_t value)
: type_(std::move(type)), signed_value_(value) {
FX_DCHECK(TreatAsSigned());
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, uint128_t value)
: type_(std::move(type)), unsigned_value_(value) {
FX_DCHECK(TreatAsUnsigned());
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, float value)
: type_(std::move(type)), float_value_(value) {
FX_DCHECK(TreatAsFloat());
}
DwarfStackEntry::DwarfStackEntry(fxl::RefPtr<BaseType> type, double value)
: type_(std::move(type)), double_value_(value) {
FX_DCHECK(TreatAsDouble());
}
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 unsigned_value_ == other.unsigned_value_;
if (TreatAsSigned())
return signed_value_ == other.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(float_value_) || isnan(other.float_value_))
return false;
return fabsf(float_value_ - other.float_value_) < kEpsilon;
}
if (TreatAsDouble()) {
if (isnan(double_value_) || isnan(other.double_value_))
return false;
return fabs(double_value_ - other.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 signed_value_ == 0;
if (TreatAsUnsigned())
return unsigned_value_ == 0;
if (TreatAsFloat())
return !isnan(float_value_) && float_value_ > -FLT_EPSILON && float_value_ < FLT_EPSILON;
if (TreatAsDouble())
return !isnan(double_value_) && double_value_ > -DBL_EPSILON && 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