forked from lix-project/lix
tracing: wip i am going to regret not splitting this right now #1
43
src/libtracing/main.cc
Normal file
43
src/libtracing/main.cc
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include "trace-writer.hh"
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace nix::tracing;
|
||||||
|
class MyInternedRetriever : public InternedRetriever
|
||||||
|
{
|
||||||
|
virtual auto getName(InternedName idx) const -> std::string override
|
||||||
|
{
|
||||||
|
return "meow";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual auto getPos(InternedPos idx, std::string funcName) const -> TracePos override
|
||||||
|
{
|
||||||
|
return TracePos{
|
||||||
|
.fileName = "file.nix",
|
||||||
|
.functionName = "fun",
|
||||||
|
.lineNumber = 12,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~MyInternedRetriever() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 1) {
|
||||||
|
std::cerr << "provide filename pls\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
nix::tracing::TraceWriter tw{std::make_unique<MyInternedRetriever>()};
|
||||||
|
|
||||||
|
tw.begin(InternedPos{1}, InternedName{3}, Timestamp{250});
|
||||||
|
tw.end(Timestamp{1235});
|
||||||
|
|
||||||
|
std::ofstream ofs(argv[1]);
|
||||||
|
auto buf = tw.splat();
|
||||||
|
ofs.write(reinterpret_cast<const char *>(buf.data()), buf.size());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
// Intentionally empty (crbug.com/998165)
|
123429
src/libtracing/meow/protos/perfetto/trace/perfetto_trace.pbzero.h
Normal file
123429
src/libtracing/meow/protos/perfetto/trace/perfetto_trace.pbzero.h
Normal file
File diff suppressed because it is too large
Load diff
47
src/libtracing/meson.build
Normal file
47
src/libtracing/meson.build
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
libtracing_sources = files(
|
||||||
|
'trace-writer.cc',
|
||||||
|
|
||||||
|
'protozero_src/field.cc',
|
||||||
|
'protozero_src/gen_field_helpers.cc',
|
||||||
|
'protozero_src/message_arena.cc',
|
||||||
|
'protozero_src/message.cc',
|
||||||
|
'protozero_src/packed_repeated_fields.cc',
|
||||||
|
'protozero_src/proto_decoder.cc',
|
||||||
|
'protozero_src/scattered_heap_buffer.cc',
|
||||||
|
'protozero_src/scattered_stream_null_delegate.cc',
|
||||||
|
'protozero_src/scattered_stream_writer.cc',
|
||||||
|
'protozero_src/static_buffer.cc',
|
||||||
|
'protozero_src/virtual_destructors.cc',
|
||||||
|
)
|
||||||
|
|
||||||
|
subdir('perfetto/protozero')
|
||||||
|
|
||||||
|
libtracing_headers = [
|
||||||
|
protozero_headers,
|
||||||
|
files(
|
||||||
|
'trace-writer.hh',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
libtracing = library(
|
||||||
|
'lixtracing',
|
||||||
|
libtracing_sources,
|
||||||
|
dependencies : [ ],
|
||||||
|
install : true,
|
||||||
|
# FIXME(Qyriad): is this right?
|
||||||
|
install_rpath : libdir,
|
||||||
|
cpp_args : [ '-DPERFETTO_DISABLE_LOG' ],
|
||||||
|
include_directories : include_directories('./perfetto/base/build_configs/bazel'),
|
||||||
|
)
|
||||||
|
|
||||||
|
liblixtracing = declare_dependency(
|
||||||
|
include_directories : include_directories('.'),
|
||||||
|
link_with : libtracing,
|
||||||
|
)
|
||||||
|
|
||||||
|
tracing_test = executable(
|
||||||
|
'lixtracingtest',
|
||||||
|
files('main.cc'),
|
||||||
|
dependencies : [ liblixtracing ],
|
||||||
|
install : false,
|
||||||
|
)
|
190
src/libtracing/trace-writer.cc
Normal file
190
src/libtracing/trace-writer.cc
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
#include "meow/protos/perfetto/trace/perfetto_trace.pbzero.h"
|
||||||
|
#include "perfetto/protozero/message.h"
|
||||||
|
#include "perfetto/protozero/message_handle.h"
|
||||||
|
#include "perfetto/protozero/scattered_heap_buffer.h"
|
||||||
|
#include "perfetto/protozero/scattered_stream_writer.h"
|
||||||
|
#include "trace-writer.hh"
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct std::hash<nix::tracing::InternedPos>
|
||||||
|
{
|
||||||
|
size_t operator()(const nix::tracing::InternedPos p) const
|
||||||
|
{
|
||||||
|
return std::hash<uint32_t>()(p.key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct std::hash<nix::tracing::InternedName>
|
||||||
|
{
|
||||||
|
size_t operator()(const nix::tracing::InternedName p) const
|
||||||
|
{
|
||||||
|
return std::hash<uint32_t>()(p.key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace nix::tracing {
|
||||||
|
|
||||||
|
using namespace perfetto::protos::pbzero;
|
||||||
|
|
||||||
|
struct ProtoInternedPos
|
||||||
|
{
|
||||||
|
uint32_t key;
|
||||||
|
|
||||||
|
explicit ProtoInternedPos(uint32_t key) : key(key) {}
|
||||||
|
ProtoInternedPos() = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProtoInternedName
|
||||||
|
{
|
||||||
|
uint32_t key;
|
||||||
|
|
||||||
|
explicit ProtoInternedName(uint32_t key) : key(key) {}
|
||||||
|
ProtoInternedName() = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TraceWriter::Impl
|
||||||
|
{
|
||||||
|
std::unique_ptr<InternedRetriever> retriever_;
|
||||||
|
protozero::ScatteredHeapBuffer buf_;
|
||||||
|
protozero::ScatteredStreamWriter writer_;
|
||||||
|
protozero::MessageArena arena_;
|
||||||
|
|
||||||
|
Trace trace_;
|
||||||
|
protozero::MessageHandle<Trace> traceHandle_;
|
||||||
|
|
||||||
|
std::unordered_map<InternedPos, ProtoInternedPos> posMap_;
|
||||||
|
uint64_t posIid_ = 1;
|
||||||
|
|
||||||
|
std::unordered_map<InternedName, ProtoInternedName> nameMap_;
|
||||||
|
uint64_t nameIid_ = 1;
|
||||||
|
|
||||||
|
auto getName(InternedName name, std::optional<std::string> &outName) -> ProtoInternedName;
|
||||||
|
|
||||||
|
auto getPos(InternedPos pos, InternedName funcName, std::optional<TracePos> &outPos) -> ProtoInternedPos;
|
||||||
|
|
||||||
|
Impl(std::unique_ptr<InternedRetriever> retriever)
|
||||||
|
: retriever_(std::move(retriever))
|
||||||
|
, writer_(&this->buf_)
|
||||||
|
, traceHandle_(&this->trace_)
|
||||||
|
{
|
||||||
|
buf_.set_writer(&this->writer_);
|
||||||
|
trace_.Reset(&this->writer_, &this->arena_);
|
||||||
|
|
||||||
|
auto p = protozero::MessageHandle<TracePacket>(trace_.add_packet());
|
||||||
|
auto td = protozero::MessageHandle<TrackDescriptor>(p->set_track_descriptor());
|
||||||
|
td->set_name("track");
|
||||||
|
td->set_uuid(1);
|
||||||
|
|
||||||
|
p->set_trusted_packet_sequence_id(1337);
|
||||||
|
p->set_sequence_flags(perfetto_pbzero_enum_TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
|
||||||
|
p->set_previous_packet_dropped(true);
|
||||||
|
p->set_first_packet_on_sequence(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto TraceWriter::Impl::getName(InternedName name, std::optional<std::string> &outName) -> ProtoInternedName
|
||||||
|
{
|
||||||
|
if (auto it = nameMap_.find(name); it != nameMap_.end()) {
|
||||||
|
// one already there, return it
|
||||||
|
return it->second;
|
||||||
|
} else {
|
||||||
|
auto nameStr = retriever_->getName(name);
|
||||||
|
outName = nameStr;
|
||||||
|
|
||||||
|
auto newIid = ++nameIid_;
|
||||||
|
nameMap_.insert(std::make_pair(name, newIid));
|
||||||
|
return ProtoInternedName(newIid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto TraceWriter::Impl::getPos(InternedPos pos, InternedName funcName, std::optional<TracePos> &outPos) -> ProtoInternedPos
|
||||||
|
{
|
||||||
|
if (auto it = posMap_.find(pos); it != posMap_.end()) {
|
||||||
|
// one already there, return it
|
||||||
|
return it->second;
|
||||||
|
} else {
|
||||||
|
auto nameRetrieved = retriever_->getName(funcName);
|
||||||
|
auto posResolved = retriever_->getPos(pos, nameRetrieved);
|
||||||
|
outPos = posResolved;
|
||||||
|
|
||||||
|
auto newIid = ++posIid_;
|
||||||
|
posMap_.insert(std::make_pair(pos, newIid));
|
||||||
|
return ProtoInternedPos(newIid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceWriter::TraceWriter(std::unique_ptr<InternedRetriever> retriever)
|
||||||
|
: impl_(std::make_unique<Impl>(std::move(retriever)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceWriter::~TraceWriter() = default;
|
||||||
|
|
||||||
|
void TraceWriter::begin(InternedPos pos, InternedName name, Timestamp ts)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::optional<std::string> nameToIntern;
|
||||||
|
std::optional<TracePos> posToIntern;
|
||||||
|
|
||||||
|
auto nameIid = impl_->getName(name, nameToIntern);
|
||||||
|
auto posIid = impl_->getPos(pos, name, posToIntern);
|
||||||
|
|
||||||
|
auto packet = protozero::MessageHandle<TracePacket>(impl_->trace_.add_packet());
|
||||||
|
|
||||||
|
packet->set_sequence_flags(perfetto_pbzero_enum_TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
|
||||||
|
|
||||||
|
if (nameToIntern.has_value() || posToIntern.has_value()) {
|
||||||
|
auto internedData = protozero::MessageHandle<InternedData>(packet->set_interned_data());
|
||||||
|
|
||||||
|
if (nameToIntern.has_value()) {
|
||||||
|
auto evName = protozero::MessageHandle<EventName>(internedData->add_event_names());
|
||||||
|
evName->set_name(*nameToIntern);
|
||||||
|
evName->set_iid(nameIid.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (posToIntern.has_value()) {
|
||||||
|
auto sourceLoc = protozero::MessageHandle<SourceLocation>(internedData->add_source_locations());
|
||||||
|
sourceLoc->set_iid(posIid.key);
|
||||||
|
sourceLoc->set_file_name(posToIntern->fileName);
|
||||||
|
sourceLoc->set_function_name(posToIntern->functionName);
|
||||||
|
sourceLoc->set_line_number(posToIntern->lineNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packet->set_timestamp(ts.monotonic_nanos);
|
||||||
|
packet->set_trusted_packet_sequence_id(1337);
|
||||||
|
auto te = protozero::MessageHandle<TrackEvent>(packet->set_track_event());
|
||||||
|
te->set_type(perfetto_pbzero_enum_TrackEvent::TYPE_SLICE_BEGIN);
|
||||||
|
te->set_name_iid(nameIid.key);
|
||||||
|
te->set_source_location_iid(posIid.key);
|
||||||
|
|
||||||
|
// TODO: make a real value
|
||||||
|
te->set_track_uuid(1);
|
||||||
|
|
||||||
|
// te->set_name_iid(nameIid.key);
|
||||||
|
// te->set_name("meow");
|
||||||
|
// TODO: te->set_source_location_iid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TraceWriter::end(Timestamp ts)
|
||||||
|
{
|
||||||
|
protozero::MessageHandle<TracePacket> tp(impl_->trace_.add_packet());
|
||||||
|
tp->set_timestamp(ts.monotonic_nanos);
|
||||||
|
tp->set_trusted_packet_sequence_id(1337);
|
||||||
|
|
||||||
|
protozero::MessageHandle<TrackEvent> te(tp->set_track_event());
|
||||||
|
te->set_type(perfetto_pbzero_enum_TrackEvent::TYPE_SLICE_END);
|
||||||
|
te->set_track_uuid(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto TraceWriter::splat() const -> std::vector<uint8_t>
|
||||||
|
{
|
||||||
|
impl_->traceHandle_->Finalize();
|
||||||
|
return impl_->buf_.StitchSlices();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
67
src/libtracing/trace-writer.hh
Normal file
67
src/libtracing/trace-writer.hh
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace nix::tracing {
|
||||||
|
|
||||||
|
struct InternedPos
|
||||||
|
{
|
||||||
|
uint32_t key;
|
||||||
|
|
||||||
|
auto operator<=>(InternedPos const & other) const = default;
|
||||||
|
explicit InternedPos(uint32_t key) : key(key) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InternedName
|
||||||
|
{
|
||||||
|
uint32_t key;
|
||||||
|
|
||||||
|
auto operator<=>(InternedName const & other) const = default;
|
||||||
|
explicit InternedName(uint32_t key) : key(key) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Timestamp
|
||||||
|
{
|
||||||
|
uint64_t monotonic_nanos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TracePos
|
||||||
|
{
|
||||||
|
std::string fileName;
|
||||||
|
std::string functionName;
|
||||||
|
uint32_t lineNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Abstract class that retrieves information about things if they are not
|
||||||
|
* memoized already */
|
||||||
|
struct InternedRetriever
|
||||||
|
{
|
||||||
|
virtual auto getName(InternedName idx) const -> std::string = 0;
|
||||||
|
virtual auto getPos(InternedPos idx, std::string funcName) const -> TracePos = 0;
|
||||||
|
|
||||||
|
virtual ~InternedRetriever() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TraceWriter
|
||||||
|
{
|
||||||
|
struct Impl;
|
||||||
|
std::unique_ptr<Impl> impl_;
|
||||||
|
public:
|
||||||
|
TraceWriter(std::unique_ptr<InternedRetriever> retriever);
|
||||||
|
|
||||||
|
void begin(InternedPos pos, InternedName name, Timestamp ts);
|
||||||
|
void end(Timestamp ts);
|
||||||
|
|
||||||
|
/** Splats the trace out into a buffer */
|
||||||
|
auto splat() const -> std::vector<uint8_t>;
|
||||||
|
|
||||||
|
~TraceWriter();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,8 @@ subdir('libutil')
|
||||||
subdir('libstore')
|
subdir('libstore')
|
||||||
# libfetchers depends on libstore
|
# libfetchers depends on libstore
|
||||||
subdir('libfetchers')
|
subdir('libfetchers')
|
||||||
|
# libtracing is self-contained
|
||||||
|
subdir('libtracing')
|
||||||
# libexpr depends on all of the above
|
# libexpr depends on all of the above
|
||||||
subdir('libexpr')
|
subdir('libexpr')
|
||||||
# libmain depends on libutil and libstore
|
# libmain depends on libutil and libstore
|
||||||
|
|
Loading…
Reference in a new issue