mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
134 lines
3.7 KiB
C++
134 lines
3.7 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#ifdef SENTINEL_STACKTRACES
|
|
extern "C" {
|
|
#include <execinfo.h>
|
|
}
|
|
#endif
|
|
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/Debug.h"
|
|
|
|
#ifdef SENTINEL_STACKTRACES
|
|
class StackTrace {
|
|
public:
|
|
void take() { Count = backtrace(Buffer, BufferCapacity); }
|
|
|
|
void print() const {
|
|
char **Strings = backtrace_symbols(Buffer, Count);
|
|
revng_assert(Strings != nullptr);
|
|
|
|
std::string Result;
|
|
for (int J = 0; J < Count; J++)
|
|
Result += std::string(Strings[J]) + "\n";
|
|
|
|
free(Strings);
|
|
|
|
// It would be nice to be able to use the nice stack traces that LLVM
|
|
// provides, but the PrintStackTrace method is private.
|
|
//
|
|
// llvm::raw_string_ostream Stream(Result);
|
|
// llvm::sys::printSymbolizedStackTrace(Buffer, Count, Stream);
|
|
|
|
dbg << Result << "\n";
|
|
}
|
|
|
|
private:
|
|
static const size_t BufferCapacity = 10;
|
|
int Count;
|
|
void *Buffer[BufferCapacity];
|
|
};
|
|
#endif
|
|
|
|
/// Helper class to detect object used after being freed or moved
|
|
///
|
|
/// To use the ClassSentinel Simply put an instance of the ClassSentinel in
|
|
/// your class, it will be destroyed and moved with your parent object. Then
|
|
/// add some class to check where you think something wrong might be going on.
|
|
/// The check methods checks if the current object has been destroyed or moved.
|
|
///
|
|
/// The ClassSentinel can also collect backtraces on destruction/move, that will
|
|
/// be printed in case a call to check fails. This makes it easy to detect the
|
|
/// source of the problem, but creates a sensible overhead.
|
|
/// To enable this feature, define SENTINEL_STACKTRACES.
|
|
///
|
|
/// \note Using a moved object is not always bad. For instance, asking the size
|
|
/// of a moved vector might be perfectly fine. However, in many cases,
|
|
/// using a moved object is unwanted.
|
|
///
|
|
/// \note While detecting if the current object has been moved is easy and
|
|
/// correct, the same is not true for destroyed objects. In fact, this
|
|
/// class, in its destructor, writes a field. However this field might be
|
|
/// clobbered by another object using the same memory area where the
|
|
/// sentil was allocated, possibly creating false negatives.
|
|
class ClassSentinel {
|
|
public:
|
|
ClassSentinel() : Moved(false), Destroyed(false) {}
|
|
ClassSentinel(const ClassSentinel &) = default;
|
|
ClassSentinel &operator=(const ClassSentinel &) = default;
|
|
ClassSentinel(ClassSentinel &&Other) : Moved(false), Destroyed(false) {
|
|
Other.check();
|
|
Other.Moved = true;
|
|
#ifdef SENTINEL_STACKTRACES
|
|
Other.MoveBacktrace.take();
|
|
#endif
|
|
}
|
|
|
|
ClassSentinel &operator=(ClassSentinel &&Other) {
|
|
checkDestroy();
|
|
Other.check();
|
|
Other.Moved = true;
|
|
#ifdef SENTINEL_STACKTRACES
|
|
Other.MoveBacktrace.take();
|
|
#endif
|
|
Moved = false;
|
|
Destroyed = false;
|
|
return *this;
|
|
}
|
|
|
|
~ClassSentinel() {
|
|
Destroyed = true;
|
|
#ifdef SENTINEL_STACKTRACES
|
|
DestroyBacktrace.take();
|
|
#endif
|
|
}
|
|
|
|
void checkDestroy() const {
|
|
if (Destroyed) {
|
|
dbg << "Trying to use a destroyed object.\n";
|
|
#ifdef SENTINEL_STACKTRACES
|
|
dbg << "It was moved with the following backtrace:\n\n";
|
|
DestroyBacktrace.print();
|
|
#endif
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
void check() const {
|
|
checkDestroy();
|
|
if (Moved) {
|
|
dbg << "Trying to use a moved object.\n";
|
|
#ifdef SENTINEL_STACKTRACES
|
|
dbg << "It was moved with the following backtrace:\n\n";
|
|
MoveBacktrace.print();
|
|
#endif
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
bool isMoved() const { return Moved; }
|
|
bool isDestroyed() const { return Destroyed; }
|
|
|
|
private:
|
|
bool Moved;
|
|
bool Destroyed;
|
|
#ifdef SENTINEL_STACKTRACES
|
|
StackTrace DestroyBacktrace;
|
|
StackTrace MoveBacktrace;
|
|
#endif
|
|
};
|