#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include "llvm/Support/Error.h" #include "llvm/Support/YAMLTraits.h" #include "revng/Support/Assert.h" #include "revng/Support/Debug.h" #include "revng/Support/Error.h" namespace detail { template inline T toInt(const llvm::StringRef StrInt) { T Result; revng_assert(!StrInt.getAsInteger(10, Result)); return Result; } } // namespace detail namespace revng::tracing { struct Argument { private: enum class ArgumentState { Invalid, Scalar, Sequence }; private: ArgumentState State = ArgumentState::Invalid; std::string Scalar; std::vector Sequence; public: bool isValid() const { return State != ArgumentState::Invalid; } bool isScalar() const { return State == ArgumentState::Scalar; } bool isSequence() const { return State == ArgumentState::Sequence; } std::string &getScalar() { setState(ArgumentState::Scalar); return Scalar; } std::vector &getSequence() { setState(ArgumentState::Sequence); return Sequence; } const std::string &getScalar() const { revng_assert(State == ArgumentState::Scalar); return Scalar; } const std::vector &getSequence() const { revng_assert(State == ArgumentState::Sequence); return Sequence; } const std::string toString() const { revng_assert(isValid()); if (isScalar()) { return Scalar; } else { return "[" + llvm::join(Sequence, ", ") + "]"; } } public: template T asInt() const { revng_assert(isScalar()); return ::detail::toInt(Scalar); } template std::vector toIntList() const { revng_assert(isSequence()); std::vector Result; for (auto &Elem : Sequence) { Result.push_back(::detail::toInt(Elem)); } return Result; } std::vector toStringList() const { revng_assert(isSequence()); return Sequence; } private: void setState(const ArgumentState NewState) { revng_assert(State == ArgumentState::Invalid || State == NewState); State = NewState; } }; struct Command { public: uint64_t ID; uint64_t StartTime; std::string Name; std::vector Arguments; std::string Result; uint64_t EndTime; public: void dump(llvm::raw_ostream &Stream) const; void dump() const debug_function { llvm::raw_os_ostream OS(dbg); dump(OS); } }; struct BufferLocation { std::string CommandName; size_t CommandNumber; size_t ArgumentNumber; }; struct RunTraceOptions { public: /// If true some assertions will result in a warning rather than aborting bool SoftAsserts = false; /// List of Command Indexes to break at when debugging std::set BreakAt; /// If set, all temporary directories will be created within this directory std::string TemporaryRoot; /// Instead of using a temporary directory, the first invocation will use /// these directory instead and subsequent ones will abort std::string ResumeDirectory; }; struct Trace { public: uint64_t Version; std::vector Commands; public: std::vector listBuffers() const; llvm::Expected> getBuffer(const BufferLocation &Location) const; llvm::Expected> getBuffer(size_t CommandNo, size_t ArgNo) const; llvm::Error run(const RunTraceOptions Options = {}) const; public: static llvm::Expected fromFile(const llvm::StringRef Path); static llvm::Expected fromBuffer(const llvm::MemoryBuffer &Buffer); }; } // namespace revng::tracing template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, revng::tracing::Trace &Trace) { TheIO.mapRequired("Version", Trace.Version); TheIO.mapRequired("Commands", Trace.Commands); } }; LLVM_YAML_IS_SEQUENCE_VECTOR(revng::tracing::Argument); LLVM_YAML_IS_SEQUENCE_VECTOR(revng::tracing::Command); template<> struct llvm::yaml::MappingTraits { static void mapping(IO &TheIO, revng::tracing::Command &TraceCommand) { TheIO.mapRequired("ID", TraceCommand.ID); TheIO.mapRequired("Name", TraceCommand.Name); TheIO.mapRequired("StartTime", TraceCommand.StartTime); TheIO.mapRequired("Arguments", TraceCommand.Arguments); TheIO.mapOptional("Result", TraceCommand.Result); TheIO.mapOptional("EndTime", TraceCommand.EndTime); } }; template<> struct llvm::yaml::PolymorphicTraits { static llvm::yaml::NodeKind getKind(const revng::tracing::Argument &Argument) { using llvm::yaml::NodeKind; return Argument.isScalar() ? NodeKind::Scalar : NodeKind::Sequence; } static std::string &getAsScalar(revng::tracing::Argument &Argument) { return Argument.getScalar(); } static std::vector & getAsSequence(revng::tracing::Argument &Argument) { return Argument.getSequence(); } static revng::tracing::Argument & getAsMap(revng::tracing::Argument &Argument) { revng_abort(); } }; namespace revng::tracing { inline llvm::Expected Trace::fromFile(const llvm::StringRef Path) { auto MaybeInputBuffer = llvm::MemoryBuffer::getFileAsStream(Path); if (std::error_code EC = MaybeInputBuffer.getError()) { return llvm::createStringError(EC, "Unable to read input trace: " + EC.message()); } return Trace::fromBuffer(**MaybeInputBuffer); } inline llvm::Expected Trace::fromBuffer(const llvm::MemoryBuffer &Buffer) { llvm::yaml::Input YAMLReader(Buffer); Trace Trace; YAMLReader >> Trace; if (Trace.Version != 1) { return revng::createError("Unexpected trace version: %u", Trace.Version); } for (size_t CommandI = 0; CommandI < Trace.Commands.size(); CommandI++) { auto &Command = Trace.Commands[CommandI]; for (size_t ArgumentI = 0; ArgumentI < Command.Arguments.size(); ArgumentI++) { auto &Argument = Command.Arguments[ArgumentI]; if (!Argument.isValid()) return revng::createError("Argument did not verify: Command #%u, " "Argument #%u", CommandI, ArgumentI); } } return Trace; } inline void Command::dump(llvm::raw_ostream &Stream) const { Stream << "\n"; llvm::yaml::Output YAMLOutput(Stream); YAMLOutput << *const_cast(this); Stream << "\n"; Stream.flush(); } } // namespace revng::tracing