Skip to content
2 changes: 1 addition & 1 deletion tools/ctrace/src/decode/DwtPacketDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ std::vector<TraceEvent> DwtPacketDecoder::decode(const DwtPayloadPacket& payload

if (source == DwtPacketSource::ExceptionTrace) {
output = flush(payload.quality, payload.tcyc);
const auto exceptionNumber = payload.value & kExceptionNumberMask;
const auto exceptionNumber = static_cast<ExceptionNumber>(payload.value & kExceptionNumberMask);
const auto action = exceptionAction((payload.value >> kExceptionActionShift) & kExceptionActionMask);
if (action == ExceptionAction::Unknown) {
TraceEvent error{TraceIssueEvent{
Expand Down
5 changes: 4 additions & 1 deletion tools/ctrace/src/model/TraceEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,12 @@ inline std::optional<std::uint32_t> dwtAddressOffset(const DwtAddressTraceEvent&
return std::nullopt;
}

/** @brief Stores a decoded Cortex-M exception number (9 bits on the wire). */
using ExceptionNumber = std::uint16_t;

/** @brief Contains a decoded exception transition. */
struct ExceptionTraceEvent {
std::uint32_t number = 0;
ExceptionNumber number = 0;
ExceptionAction action = ExceptionAction::Unknown;
};

Expand Down
64 changes: 39 additions & 25 deletions tools/ctrace/src/output/ctf/CtfEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,18 @@ void CtfEncoder::writeGlobalTimestampEvent(const TraceEvent& event, const Global

void CtfEncoder::writeTraceStatusEvent(std::uint8_t reason, std::uint8_t traceBusId, bool emitEvent)
{
if (reason == CtfSchema::value(CtfSchema::TraceStatusReason::Overflow) ||
reason == CtfSchema::value(CtfSchema::TraceStatusReason::DataLoss)) {
const auto lane = m_exceptionLanes.find(traceBusId);
if (lane != m_exceptionLanes.end()) {
lane->second.resetForDiscontinuity(
[this, traceBusId](ExceptionNumber number, CtfExceptionLaneTracker::RecordAction action,
CtfExceptionLaneTracker::RecordOrigin origin) {
emitExceptionRecord(traceBusId, number, action, origin);
});
}
}

if (emitEvent) {
constexpr auto payloadSize = 1U + 4U;
const auto eventTimestamp = allocateEventTimestamp(traceBusId);
Expand All @@ -354,48 +366,49 @@ void CtfEncoder::writeTraceStatusEvent(std::uint8_t reason, std::uint8_t traceBu
record.writeU32(ctfOverflowCount(m_streamStates[traceBusId].overflowCount));
});
}

if (reason == CtfSchema::value(CtfSchema::TraceStatusReason::Overflow) ||
reason == CtfSchema::value(CtfSchema::TraceStatusReason::DataLoss)) {
const auto lane = m_exceptionLanes.find(traceBusId);
if (lane != m_exceptionLanes.end()) {
lane->second.resetForDiscontinuity(
[this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) {
emitExceptionRecord(traceBusId, number, action);
});
}
}
}

void CtfEncoder::writeExceptionEvent(std::uint8_t traceBusId, const ExceptionTraceEvent& exception)
{
exceptionLane(traceBusId)
.consume(exception, [this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) {
emitExceptionRecord(traceBusId, number, action);
.consume(exception, [this, traceBusId](ExceptionNumber number, CtfExceptionLaneTracker::RecordAction action,
CtfExceptionLaneTracker::RecordOrigin origin) {
emitExceptionRecord(traceBusId, number, action, origin);
});
}

void CtfEncoder::emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t number,
CtfExceptionLaneTracker::RecordAction action)
void CtfEncoder::emitExceptionRecord(std::uint8_t traceBusId, ExceptionNumber number,
CtfExceptionLaneTracker::RecordAction action,
CtfExceptionLaneTracker::RecordOrigin origin)
{
const auto semanticAction =
action == CtfExceptionLaneTracker::RecordAction::Enter
? ExceptionAction::Entered
: action == CtfExceptionLaneTracker::RecordAction::Exit ? ExceptionAction::Exited : ExceptionAction::Returned;
TraceEvent selectionEvent{ExceptionTraceEvent{
number,
action == CtfExceptionLaneTracker::RecordAction::Enter ? ExceptionAction::Entered : ExceptionAction::Exited,
semanticAction,
}};
selectionEvent.traceBusId = traceBusId;
if (!traceEventSelectedForOutput(selectionEvent, m_config.selection)) {
return;
}
constexpr auto payloadSize = 2U + 1U + 2U;
constexpr auto payloadSize = 2U + 1U + 2U + 1U;
Comment thread
thorstendb-ARM marked this conversation as resolved.
const auto eventTimestamp = allocateEventTimestamp(traceBusId);
const auto encodedAction =
CtfSchema::value(action == CtfExceptionLaneTracker::RecordAction::Enter ? CtfSchema::ExceptionAction::Entered
: CtfSchema::ExceptionAction::Exited);
const auto encodedAction = CtfSchema::value(
action == CtfExceptionLaneTracker::RecordAction::Enter
? CtfSchema::ExceptionAction::Entered
: action == CtfExceptionLaneTracker::RecordAction::Exit ? CtfSchema::ExceptionAction::Exited
: CtfSchema::ExceptionAction::Returned);
const auto encodedOrigin = CtfSchema::value(origin == CtfExceptionLaneTracker::RecordOrigin::Trace
? CtfSchema::ExceptionOrigin::Trace
: CtfSchema::ExceptionOrigin::Synthetic);
m_stream.writeRecord(CtfSchema::value(CtfSchema::EventId::Exception), eventTimestamp, traceBusId, payloadSize,
[&](CtfStreamWriter::Record& record) {
record.writeU16(static_cast<std::uint16_t>(number & 0xffffU));
record.writeU16(number);
record.writeU8(encodedAction);
record.writeU16(static_cast<std::uint16_t>(number & 0xffffU));
record.writeU16(number);
record.writeU8(encodedOrigin);
});
}

Expand All @@ -404,8 +417,9 @@ CtfExceptionLaneTracker& CtfEncoder::exceptionLane(std::uint8_t traceBusId)
const auto [lane, inserted] = m_exceptionLanes.try_emplace(traceBusId);
if (inserted) {
lane->second.startThreadMode(
[this, traceBusId](std::uint32_t number, CtfExceptionLaneTracker::RecordAction action) {
emitExceptionRecord(traceBusId, number, action);
[this, traceBusId](ExceptionNumber number, CtfExceptionLaneTracker::RecordAction action,
CtfExceptionLaneTracker::RecordOrigin origin) {
emitExceptionRecord(traceBusId, number, action, origin);
});
}
return lane->second;
Expand All @@ -428,7 +442,7 @@ std::pair<std::uint8_t, std::uint32_t> CtfEncoder::computeSampleQuality(const Tr

void CtfEncoder::writeMetadataFile()
{
std::set<std::uint32_t> observedExceptionNumbers;
std::set<ExceptionNumber> observedExceptionNumbers;
for (const auto& [traceBusId, lane] : m_exceptionLanes) {
(void)traceBusId;
observedExceptionNumbers.insert(lane.observedExceptionNumbers().begin(), lane.observedExceptionNumbers().end());
Expand Down
4 changes: 3 additions & 1 deletion tools/ctrace/src/output/ctf/CtfEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ class CtfEncoder final {
/** @brief Applies one exception transition to its CTF lane state. */
void writeExceptionEvent(std::uint8_t traceBusId, const ExceptionTraceEvent& exception);
/** @brief Emits one concrete exception lane record. */
void emitExceptionRecord(std::uint8_t traceBusId, std::uint32_t number, CtfExceptionLaneTracker::RecordAction action);
void emitExceptionRecord(std::uint8_t traceBusId, ExceptionNumber number,
CtfExceptionLaneTracker::RecordAction action,
CtfExceptionLaneTracker::RecordOrigin origin);
/** @brief Returns the exception tracker for one stream. */
CtfExceptionLaneTracker& exceptionLane(std::uint8_t traceBusId);
/** @brief Computes CTF sample flags and saturated overflow count. */
Expand Down
53 changes: 31 additions & 22 deletions tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,92 +15,101 @@

void CtfExceptionLaneTracker::startThreadMode(const RecordEmitter& emit)
{
setActiveContext(kThreadModeNumber, emit);
setActiveContext(kThreadModeNumber, RecordAction::Enter, RecordOrigin::Synthetic, emit);
}

void CtfExceptionLaneTracker::resetForDiscontinuity(const RecordEmitter& emit)
{
m_contextStack.clear();
closeActiveContext(emit);
closeActiveContext(RecordOrigin::Synthetic, emit);
}

void CtfExceptionLaneTracker::consume(const ExceptionTraceEvent& event, const RecordEmitter& emit)
{
const auto number = event.number & 0x1ffU;
const auto number = event.number;
switch (event.action) {
case ExceptionAction::Entered:
enterContext(number);
break;
updateActiveContext(RecordAction::Enter, RecordOrigin::Trace, emit);
return;
case ExceptionAction::Exited:
exitContext(number);
break;
if (exitContext(number)) {
closeActiveContext(RecordOrigin::Trace, emit);
}
return;
case ExceptionAction::Returned:
returnToContext(number);
break;
updateActiveContext(RecordAction::Return, RecordOrigin::Trace, emit);
return;
case ExceptionAction::Unknown:
return;
}
updateActiveContext(emit);
}

const std::vector<std::uint32_t>& CtfExceptionLaneTracker::observedExceptionNumbers() const
const std::vector<ExceptionNumber>& CtfExceptionLaneTracker::observedExceptionNumbers() const
{
return m_observedExceptionNumbers;
}

void CtfExceptionLaneTracker::setActiveContext(std::uint32_t number, const RecordEmitter& emit)
void CtfExceptionLaneTracker::setActiveContext(ExceptionNumber number, RecordAction action, RecordOrigin origin,
const RecordEmitter& emit)
{
if (m_activeContextNumber.has_value() && *m_activeContextNumber == number) {
if (action == RecordAction::Return) {
emitRecord(number, action, origin, emit);
}
return;
}
if (m_activeContextNumber.has_value()) {
emitRecord(*m_activeContextNumber, RecordAction::Exit, emit);
emitRecord(*m_activeContextNumber, RecordAction::Exit, RecordOrigin::Synthetic, emit);
}
emitRecord(number, RecordAction::Enter, emit);
emitRecord(number, action, origin, emit);
m_activeContextNumber = number;
}

void CtfExceptionLaneTracker::closeActiveContext(const RecordEmitter& emit)
void CtfExceptionLaneTracker::closeActiveContext(RecordOrigin origin, const RecordEmitter& emit)
{
if (!m_activeContextNumber.has_value()) {
return;
}
emitRecord(*m_activeContextNumber, RecordAction::Exit, emit);
emitRecord(*m_activeContextNumber, RecordAction::Exit, origin, emit);
m_activeContextNumber.reset();
}

void CtfExceptionLaneTracker::updateActiveContext(const RecordEmitter& emit)
void CtfExceptionLaneTracker::updateActiveContext(RecordAction action, RecordOrigin origin, const RecordEmitter& emit)
{
setActiveContext(m_contextStack.empty() ? kThreadModeNumber : m_contextStack.back().number, emit);
setActiveContext(m_contextStack.empty() ? kThreadModeNumber : m_contextStack.back().number, action, origin, emit);
}

void CtfExceptionLaneTracker::emitRecord(std::uint32_t number, RecordAction action, const RecordEmitter& emit)
void CtfExceptionLaneTracker::emitRecord(ExceptionNumber number, RecordAction action, RecordOrigin origin,
const RecordEmitter& emit)
{
if (std::find(m_observedExceptionNumbers.begin(), m_observedExceptionNumbers.end(), number) ==
m_observedExceptionNumbers.end()) {
m_observedExceptionNumbers.push_back(number);
}
emit(number, action);
emit(number, action, origin);
}

void CtfExceptionLaneTracker::enterContext(std::uint32_t number)
void CtfExceptionLaneTracker::enterContext(ExceptionNumber number)
{
if (!m_contextStack.empty() && m_contextStack.back().state == ContextState::Running) {
m_contextStack.back().state = ContextState::Preempted;
}
m_contextStack.push_back({number, ContextState::Running});
}

void CtfExceptionLaneTracker::exitContext(std::uint32_t number)
bool CtfExceptionLaneTracker::exitContext(ExceptionNumber number)
{
if (m_contextStack.empty() || m_contextStack.back().number != number ||
m_contextStack.back().state != ContextState::Running) {
return;
return false;
}
m_contextStack.pop_back();
return true;
}

void CtfExceptionLaneTracker::returnToContext(std::uint32_t number)
void CtfExceptionLaneTracker::returnToContext(ExceptionNumber number)
{
while (!m_contextStack.empty() && m_contextStack.back().number != number) {
m_contextStack.pop_back();
Expand Down
41 changes: 24 additions & 17 deletions tools/ctrace/src/output/ctf/CtfExceptionLaneTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@
/** @brief Tracks active Cortex-M exception contexts for Trace Compass lanes. */
class CtfExceptionLaneTracker {
public:
/** @brief Selects whether an emitted lane record enters or exits a context. */
/** @brief Selects whether an emitted lane record enters, exits, or returns to a context. */
enum class RecordAction {
Enter,
Exit,
Return,
};

/** @brief Identifies whether a record came from trace input or lane reconstruction. */
enum class RecordOrigin {
Trace,
Synthetic,
};

/** @brief Emits one exception-lane record. */
using RecordEmitter = std::function<void(std::uint32_t number, RecordAction action)>;
using RecordEmitter = std::function<void(ExceptionNumber number, RecordAction action, RecordOrigin origin)>;

/** @brief Starts the initial thread-mode context. */
void startThreadMode(const RecordEmitter& emit);
Expand All @@ -34,10 +41,10 @@ class CtfExceptionLaneTracker {
/** @brief Applies one exception transition and emits resulting lane records. */
void consume(const ExceptionTraceEvent& event, const RecordEmitter& emit);
/** @brief Returns exception numbers observed by this tracker. */
const std::vector<std::uint32_t>& observedExceptionNumbers() const;
const std::vector<ExceptionNumber>& observedExceptionNumbers() const;

private:
static constexpr std::uint32_t kThreadModeNumber = 0;
static constexpr ExceptionNumber kThreadModeNumber = 0;

/** @brief Identifies whether an exception context is active or preempted. */
enum class ContextState : std::uint8_t {
Expand All @@ -47,28 +54,28 @@ class CtfExceptionLaneTracker {

/** @brief Stores one exception context on the nesting stack. */
struct ContextFrame {
std::uint32_t number = 0;
ExceptionNumber number = 0;
ContextState state = ContextState::Running;
};

/** @brief Switches the emitted active lane to one context. */
void setActiveContext(std::uint32_t number, const RecordEmitter& emit);
/** @brief Switches the emitted active lane to one context with the requested activation action. */
void setActiveContext(ExceptionNumber number, RecordAction action, RecordOrigin origin, const RecordEmitter& emit);
/** @brief Closes the currently emitted active lane. */
void closeActiveContext(const RecordEmitter& emit);
/** @brief Reconciles emitted lane state with the context stack. */
void updateActiveContext(const RecordEmitter& emit);
void closeActiveContext(RecordOrigin origin, const RecordEmitter& emit);
/** @brief Activates the context selected by the stack after an enter or return. */
void updateActiveContext(RecordAction action, RecordOrigin origin, const RecordEmitter& emit);
/** @brief Emits and records one lane transition. */
void emitRecord(std::uint32_t number, RecordAction action, const RecordEmitter& emit);
void emitRecord(ExceptionNumber number, RecordAction action, RecordOrigin origin, const RecordEmitter& emit);
/** @brief Pushes or reactivates an entered exception context. */
void enterContext(std::uint32_t number);
/** @brief Removes an exited exception context. */
void exitContext(std::uint32_t number);
void enterContext(ExceptionNumber number);
/** @brief Removes an exited running exception context. */
bool exitContext(ExceptionNumber number);
/** @brief Returns the stack to a previously active context. */
void returnToContext(std::uint32_t number);
void returnToContext(ExceptionNumber number);

std::vector<ContextFrame> m_contextStack;
std::optional<std::uint32_t> m_activeContextNumber;
std::vector<std::uint32_t> m_observedExceptionNumbers;
std::optional<ExceptionNumber> m_activeContextNumber;
std::vector<ExceptionNumber> m_observedExceptionNumbers;
};

#endif // CTRACE_SRC_OUTPUT_CTF_CTFEXCEPTIONLANETRACKER_H
Loading
Loading