Files
fs2open.github.com/code/tracing/TraceEventWriter.cpp
T
Goober5000andClaude Opus 4.8 bed228d264 avoid undefined behavior from UNREACHABLE on reachable code paths (#7555)
* avoid undefined behavior from UNREACHABLE on reachable code paths

UNREACHABLE expands to __assume(false) / __builtin_unreachable() in
release builds, which is undefined behavior if the code is ever actually
reached. Many UNREACHABLE sites only guard against unexpected-but-
possible runtime states (values from tables, mission/save files, network
packets, scripting, or user input), so reaching them should degrade
gracefully rather than invoke UB.

Replace UNREACHABLE with Assertion(false, ...) at all such sites. It
still pops an error in debug builds, but in release it is a harmless
no-op that simply falls through to the following code. Sites that are
genuinely unreachable by local invariants (e.g. a switch over a bounded
random number) are left as UNREACHABLE.

A few converted sites had no safe fall-through -- an uninitialized read,
null dereference, or null call would have followed -- so they also get a
minimal recovery: initialize the value, guard the dereference, return
early, or skip the offending item.

Where the converted site was a simple guard of the form
    if (x) { Assertion(false, ...); return; }
hoist the check onto the invariant instead:
    Assertion(!x, ...);
    if (x) { return; }
This states the expectation directly and shows the offending value in
the debug popup, while keeping the same graceful runtime degradation.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

* addendum

After consultation with Lafiel, use UNREACHABLE in situations that are effectively impossible even if not provably impossible.  UNREACHABLE = "this shouldn't ever happen"; Assertion = "if this ever happens, it's a coding error".  Also fix a few other sites and change a couple to Warnings.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-09 11:20:25 +09:00

126 lines
2.6 KiB
C++

#include "tracing/TraceEventWriter.h"
#include <iomanip>
namespace
{
using namespace tracing;
const char* getTypeStr(tracing::EventType type) {
switch(type) {
case tracing::EventType::Complete:
return "X";
case tracing::EventType::Begin:
return "B";
case tracing::EventType::End:
return "E";
case EventType::AsyncBegin:
return "b";
case EventType::AsyncStep:
return "n";
case EventType::AsyncEnd:
return "e";
case EventType::Counter:
return "C";
default:
UNREACHABLE("Invalid enum value %d!", static_cast<int>(type));
return "";
}
}
void writeTime(std::ofstream& out, std::uint64_t time) {
// Save stream state
auto flags = out.flags();
out << std::fixed << std::setprecision(3);
out << (time / 1000.);
// and now restore it
out.flags(flags);
}
void writeCompleteEvent(std::ofstream& out_str, const trace_event* evt) {
Assertion(evt->type == EventType::Complete, "Event must be a complete event!");
out_str << ",\"dur\":";
writeTime(out_str, evt->duration);
}
}
namespace tracing
{
TraceEventWriter::TraceEventWriter() : _out("tracing/trace.json") {
_out << "[";
}
TraceEventWriter::~TraceEventWriter() {
_out << "]\n";
_out.close();
}
void TraceEventWriter::processEvent(const trace_event* event) {
if (event->type == EventType::Complete) {
if (event->duration < 1000) {
// Discard events that are less than a millisecond long
return;
}
}
if (!_first_line) {
_out << ",";
}
_out << "\n{\"tid\": " << event->tid << ",\"ts\":";
writeTime(_out, event->timestamp);
_out << ",\"pid\":";
if (event->pid == GPU_PID) {
_out << "\"GPU\"";
}
else {
_out << event->pid;
}
if (event->scope != nullptr) {
_out << ",\"cat\":\"" << event->scope->getName() << "\"";
_out << ",\"id\":\"" << reinterpret_cast<const void*>(event->scope) << "\"";
}
_out << ",\"name\":\"" << event->category->getName() << "\",\"ph\":\"" << getTypeStr(event->type) << "\"";
switch (event->type) {
case EventType::Complete:
writeCompleteEvent(_out, event);
break;
case EventType::Begin:
case EventType::End:
// Nothing to do here
break;
case EventType::AsyncBegin:
case EventType::AsyncStep:
case EventType::AsyncEnd:
// Nothing to do here...
break;
case EventType::Counter: {
auto flags = _out.flags();
_out << std::fixed;
_out << ",\"args\": {\"value\": " << event->value << "}";
// and now restore it
_out.flags(flags);
break;
}
default:
UNREACHABLE("Unhandled enum value %d! This function should not have been called with this value!", static_cast<int>(event->type));
break;
}
_out << "}";
_first_line = false;
}
}