1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
#pragma once
/**
* Collection of a stack trace, and tools for showing it to the user using the information
* available in the Arena for symbolic lookup, alongside any debug information for the C++ code.
* This will be extended to allow for inspection later on, so that the one who generated code
* can inspect variables and similar things.
*/
/**
* Fixed-size data entry for stack traces.
*/
struct StackFrame {
StackFrame() : fnBase(null), offset(0), id(0), returnLocation(0) {}
// Base and offset for the function.
void *fnBase;
int offset;
// Get the function as a full pointer.
inline void *fn() const {
return (byte *)fnBase + offset;
}
// Which backend produced the data above.
int id;
// Location of the return adress on the stack for this frame. I.e. where is the address that
// *returns to* this frame stored in memory? Might be null.
void *returnLocation;
// Update the return location if it is non-null. On some architectures (e.g. ARM64) this
// function might need to sign the pointer as required by the ABI spec.
void updateReturnLocation(const void *target) const;
};
/**
* Representation of the stack trace itself. It will collect a copy of the stack with minimal processing
* so that we can later inspect the data when and if we want to. However, this means that any data stored
* here should not be considered live objects, since they are nothing more than a bitwise copy of the
* original value.
* This is since we probably want to include a Trace in exceptions, and we want to do as little as possible
* when an exception is thrown in order to minimize code that should not fail. Also, we do not want to spend
* time generating things that will never be shown anyway.
*/
class StackTrace : public Printable {
public:
// Create an empty stack trace.
StackTrace();
// Create a trace with 'n' uninitialized frames in it.
StackTrace(nat n);
// Dtor.
~StackTrace();
// Copy.
StackTrace(const StackTrace &o);
StackTrace &operator =(const StackTrace &o);
// Element access.
inline StackFrame &operator [] (nat i) { return frames[i]; }
inline const StackFrame &operator [] (nat i) const { return frames[i]; }
// Size.
inline nat count() const { return size; }
// Add an element (grows).
void push(const StackFrame &frame);
protected:
// Output. Not formatted in any way.
virtual void output(wostream &to) const;
private:
// Storage of the data.
StackFrame *frames;
// Number of frames.
nat size;
// Capacity.
nat capacity;
};
// Generate a stack trace from the calling point in the code.
StackTrace stackTrace(nat skip = 0);
// Fromat an entire trace.
String format(const StackTrace &trace);
// Generate a stack trace using a custom container.
class TraceGen {
public:
// Initialize the data structure with 'count' elements.
virtual void init(size_t count) = 0;
// Add a frame.
virtual void put(const StackFrame &frame) = 0;
};
// State is a pointer to a platform-specific collected stack state.
void createStackTrace(TraceGen &gen, nat skip = 0, void *state = null);
|