Files
carbon-lang/explorer/base/trace_stream.h
T
2024-03-04 16:27:31 +00:00

227 lines
8.0 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CARBON_EXPLORER_BASE_TRACE_STREAM_H_
#define CARBON_EXPLORER_BASE_TRACE_STREAM_H_
#include <bitset>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "common/check.h"
#include "common/ostream.h"
#include "explorer/base/nonnull.h"
#include "explorer/base/source_location.h"
#include "llvm/ADT/ArrayRef.h"
namespace Carbon {
class TraceStream;
// Enumerates the phases of the program used for tracing and controlling which
// program phases are included for tracing.
enum class ProgramPhase {
SourceProgram, // Phase for the source program.
NameResolution, // Phase for name resolution.
ControlFlowResolution, // Phase for control flow resolution.
TypeChecking, // Phase for type checking.
UnformedVariableResolution, // Phase for unformed variables resolution.
Declarations, // Phase for printing declarations.
Execution, // Phase for program execution.
Timing, // Phase for timing logs.
Unknown, // Represents an unknown program phase.
All, // Represents all program phases.
Last = All // Last program phase indicator.
};
// Encapsulates the trace stream so that we can cleanly disable tracing while
// the prelude is being processed. The prelude is expected to take a
// disproportionate amount of time to log, so we try to avoid it.
class TraceStream {
public:
explicit TraceStream() = default;
// Returns true if tracing is currently enabled.
auto is_enabled() const -> bool {
return stream_.has_value() && !in_prelude_ &&
allowed_phases_[static_cast<int>(current_phase_)] &&
(!source_loc_ ||
allowed_file_kinds_[static_cast<int>(source_loc_->file_kind())]);
}
// Sets whether the prelude is being skipped.
auto set_in_prelude(bool in_prelude) -> void { in_prelude_ = in_prelude; }
// Sets the trace stream. This should only be called from the main.
auto set_stream(Nonnull<llvm::raw_ostream*> stream) -> void {
stream_ = stream;
}
auto set_current_phase(ProgramPhase current_phase) -> void {
current_phase_ = current_phase;
}
auto set_allowed_phases(llvm::ArrayRef<ProgramPhase> allowed_phases_list)
-> void {
if (allowed_phases_list.empty()) {
allowed_phases_.set(static_cast<int>(ProgramPhase::Execution));
} else {
for (auto phase : allowed_phases_list) {
if (phase == ProgramPhase::All) {
allowed_phases_.set();
} else {
allowed_phases_.set(static_cast<int>(phase));
}
}
}
}
auto set_allowed_file_kinds(llvm::ArrayRef<FileKind> kind_list) -> void {
for (auto kind : kind_list) {
allowed_file_kinds_.set(static_cast<int>(kind));
}
}
auto set_source_loc(std::optional<SourceLocation> source_loc) -> void {
source_loc_ = source_loc;
}
auto source_loc() const -> std::optional<SourceLocation> {
return source_loc_;
}
// Returns the internal stream. Requires is_enabled.
auto stream() const -> llvm::raw_ostream& {
CARBON_CHECK(is_enabled() && stream_.has_value());
return **stream_;
}
auto current_phase() const -> ProgramPhase { return current_phase_; }
auto add_blank_lines(int num_blank_lines) const -> void {
CARBON_CHECK(is_enabled() && stream_);
if (!is_trace_empty_) {
for (int i = 0; i < num_blank_lines; ++i) {
**stream_ << "\n";
}
}
}
// Outputs a trace message. Requires is_enabled.
template <typename T>
auto operator<<(T&& message) const -> llvm::raw_ostream& {
CARBON_CHECK(is_enabled() && stream_);
if (is_trace_empty_) {
is_trace_empty_ = false;
}
**stream_ << message;
return **stream_;
}
// These functions can be used for adding line prefixes in the trace output.
auto Indent() const -> llvm::raw_ostream& { return *this << " "; }
auto Start() const -> llvm::raw_ostream& { return *this << "->> "; }
auto End() const -> llvm::raw_ostream& { return *this << "<<- "; }
auto Call() const -> llvm::raw_ostream& { return *this << "-() "; }
auto Match() const -> llvm::raw_ostream& { return *this << "=== "; }
auto Result() const -> llvm::raw_ostream& { return *this << "==> "; }
auto Add() const -> llvm::raw_ostream& { return *this << " + "; }
auto Remove() const -> llvm::raw_ostream& { return *this << " - "; }
auto Read() const -> llvm::raw_ostream& { return *this << "<-- "; }
auto Write() const -> llvm::raw_ostream& { return *this << "--> "; }
auto Allocate() const -> llvm::raw_ostream& { return *this << "++# "; }
auto Deallocate() const -> llvm::raw_ostream& { return *this << "--# "; }
auto Substitute() const -> llvm::raw_ostream& { return *this << "->+ "; }
auto Push() const -> llvm::raw_ostream& { return *this << ">[] "; }
auto Pop() const -> llvm::raw_ostream& { return *this << "<[] "; }
auto Not() const -> llvm::raw_ostream& { return *this << "-!- "; }
auto Skip() const -> llvm::raw_ostream& { return *this << ">>> "; }
auto Source() const -> llvm::raw_ostream& {
// add a blank line before prefix.
add_blank_lines(1);
return *this << "*** ";
}
// Format utility methods
void Heading(std::string_view heading) const {
add_blank_lines(2);
const std::string_view stars = "* * * * * * * * * *";
const std::string dashed_line(stars.size() * 2 + heading.size() + 4, '-');
*this << stars << " " << heading << " " << stars << "\n"
<< dashed_line << "\n";
}
void SubHeading(std::string_view sub_heading) const {
add_blank_lines(1);
const std::string_view dashes = "- - - - -";
const std::string dashed_line(dashes.size() * 2 + sub_heading.size() + 4,
'-');
*this << dashes << " " << sub_heading << " " << dashes << "\n"
<< dashed_line << "\n";
}
private:
bool in_prelude_ = false;
mutable bool is_trace_empty_ = true;
ProgramPhase current_phase_ = ProgramPhase::Unknown;
std::optional<SourceLocation> source_loc_ = std::nullopt;
std::optional<Nonnull<llvm::raw_ostream*>> stream_;
std::bitset<static_cast<int>(ProgramPhase::Last) + 1> allowed_phases_;
std::bitset<static_cast<int>(FileKind::Last) + 1> allowed_file_kinds_;
};
// This is a RAII class to set the current program phase, destructor invocation
// restores the previous phase.
class SetProgramPhase {
public:
explicit SetProgramPhase(TraceStream& trace_stream,
ProgramPhase program_phase)
: trace_stream_(trace_stream),
initial_phase_(trace_stream.current_phase()) {
trace_stream.set_current_phase(program_phase);
}
~SetProgramPhase() { trace_stream_.set_current_phase(initial_phase_); }
// This can be used for cases when current phase is set multiple times within
// the same scope.
auto update_phase(ProgramPhase program_phase) -> void {
trace_stream_.set_current_phase(program_phase);
}
private:
TraceStream& trace_stream_;
ProgramPhase initial_phase_;
};
// This is a RAII class to set the source location in trace stream, destructor
// invocation restores the initial source location.
class SetFileContext {
public:
explicit SetFileContext(TraceStream& trace_stream,
std::optional<SourceLocation> source_loc)
: trace_stream_(trace_stream),
initial_source_loc_(trace_stream.source_loc()) {
trace_stream_.set_source_loc(source_loc);
}
~SetFileContext() { trace_stream_.set_source_loc(initial_source_loc_); }
// This can be used for cases when source location needs to be updated
// multiple times within the same scope.
auto update_source_loc(std::optional<SourceLocation> source_loc) {
trace_stream_.set_source_loc(source_loc);
}
private:
TraceStream& trace_stream_;
std::optional<SourceLocation> initial_source_loc_;
};
} // namespace Carbon
#endif // CARBON_EXPLORER_BASE_TRACE_STREAM_H_