CIRCT 24.0.0git
Loading...
Searching...
No Matches
Trace.cpp
Go to the documentation of this file.
1//===- Trace.cpp - Implementation of trace backend -----------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// DO NOT EDIT!
10// This file is distributed as part of an ESI package. The source for this file
11// should always be modified within CIRCT (lib/dialect/ESI/runtime/cpp/lib/).
12//
13//===----------------------------------------------------------------------===//
14
15#include "esi/backends/Trace.h"
16
17#include "esi/Accelerator.h"
18#include "esi/Services.h"
19#include "esi/Utils.h"
20
21#include <cassert>
22#include <fstream>
23#include <iostream>
24#include <sstream>
25
26using namespace esi;
27using namespace esi::services;
28using namespace esi::backends::trace;
29
30namespace {
31class TraceChannelPort;
32class TraceEngine;
33} // namespace
34
36
38 friend class TraceAccelerator;
39 Impl(Mode mode, std::filesystem::path manifestJson,
40 std::filesystem::path traceFile)
42 if (!std::filesystem::exists(manifestJson))
43 throw std::runtime_error("manifest file '" + manifestJson.string() +
44 "' does not exist");
45
46 if (mode == Write) {
47 // Open the trace file for writing.
48 traceWrite = new std::ofstream(traceFile);
49 if (!traceWrite->is_open())
50 throw std::runtime_error("failed to open trace file '" +
51 traceFile.string() + "'");
52 } else if (mode == Discard) {
53 traceWrite = nullptr;
54 } else {
55 assert(false && "not implemented");
56 }
57 }
58
60 if (traceWrite) {
61 traceWrite->close();
62 delete traceWrite;
63 }
64 }
65
67 AppIDPath idPath, const ServiceImplDetails &details,
68 const HWClientDetails &clients);
69
70 void adoptChannelPort(ChannelPort *port) { channels.emplace_back(port); }
71
72 void write(const AppIDPath &id, const std::string &portName, const void *data,
73 size_t size, const std::string &prefix = "");
74 std::ostream &write(std::string service) {
75 assert(traceWrite && "traceWrite is null");
76 *traceWrite << "[" << service << "] ";
77 return *traceWrite;
78 }
79 bool isWriteable() { return traceWrite; }
80
81private:
82 std::ofstream *traceWrite;
83 std::filesystem::path manifestJson;
84 std::filesystem::path traceFile;
85 std::vector<std::unique_ptr<ChannelPort>> channels;
86};
87
88void TraceAccelerator::Impl::write(const AppIDPath &id,
89 const std::string &portName,
90 const void *data, size_t size,
91 const std::string &prefix) {
92 if (!isWriteable())
93 return;
94 std::string b64data;
95 utils::encodeBase64(data, size, b64data);
96
97 *traceWrite << prefix << (prefix.empty() ? "w" : "W") << "rite " << id << '.'
98 << portName << ": " << b64data << std::endl;
99}
100
101std::unique_ptr<AcceleratorConnection>
102TraceAccelerator::connect(Context &ctxt, std::string connectionString) {
103 std::string manifestPath;
104 std::string traceFile = "trace.log";
105
106 // Parse the connection std::string.
107 // Format: <mode>SEP<manifest path>[SEP<traceFile>]
108 // where SEP is ':' on Unix and ';' on Windows.
109 // Using different separators avoids conflicts with Windows drive letters
110 // (e.g. "C:/...").
111#ifdef _WIN32
112 constexpr char SEP = ';';
113#else
114 constexpr char SEP = ':';
115#endif
116
117 if (connectionString.size() < 2 || connectionString[1] != SEP)
118 throw std::runtime_error(
119 std::string("connection string must be of the form ") + "'<mode>" +
120 SEP + "<manifest path>[" + SEP + "<traceFile>]'");
121
122 char modeChar = connectionString[0];
123 std::string rest = connectionString.substr(2);
124
125 // Find the optional trace file, separated by the last SEP.
126 size_t lastSep = rest.rfind(SEP);
127 if (lastSep != std::string::npos) {
128 manifestPath = rest.substr(0, lastSep);
129 traceFile = rest.substr(lastSep + 1);
130
131 // Validate that the trace file name is non-empty when present.
132 if (traceFile.empty())
133 throw std::runtime_error(
134 std::string("connection string must be of the form ") + "'<mode>" +
135 SEP + "<manifest path>[" + SEP +
136 "<traceFile>]': "
137 "trace file name cannot be empty");
138 } else {
139 manifestPath = rest;
140 }
141
142 // Validate that manifest path is non-empty.
143 if (manifestPath.empty())
144 throw std::runtime_error(
145 std::string("connection string must be of the form ") + "'<mode>" +
146 SEP + "<manifest path>[" + SEP +
147 "<traceFile>]': "
148 "manifest path cannot be empty");
149
150 // Parse the mode.
151 Mode mode;
152 if (modeChar == 'w')
153 mode = Write;
154 else if (modeChar == '-')
155 mode = Discard;
156 else
157 throw std::runtime_error("unknown mode '" + std::string(1, modeChar) + "'");
158
159 return std::make_unique<TraceAccelerator>(ctxt, mode,
160 std::filesystem::path(manifestPath),
161 std::filesystem::path(traceFile));
162}
163
165 std::filesystem::path manifestJson,
166 std::filesystem::path traceFile)
168 impl = std::make_unique<Impl>(mode, manifestJson, traceFile);
169}
171
172namespace {
173class TraceSysInfo : public SysInfo {
174public:
175 TraceSysInfo(AcceleratorConnection &conn, std::filesystem::path manifestJson)
176 : SysInfo(conn), manifestJson(manifestJson) {}
177
178 uint32_t getEsiVersion() const override {
179 return Manifest(conn.getCtxt(), getJsonManifest()).getApiVersion();
180 }
181
182 std::string getJsonManifest() const override {
183 // Read in the whole json file and return it.
184 std::ifstream manifest(manifestJson);
185 if (!manifest.is_open())
186 throw std::runtime_error("failed to open manifest file '" +
187 manifestJson.string() + "'");
188 std::stringstream buffer;
189 buffer << manifest.rdbuf();
190 manifest.close();
191 return buffer.str();
192 }
193
194 std::vector<uint8_t> getCompressedManifest() const override {
195 throw std::runtime_error(
196 "compressed manifest not supported by trace backend");
197 }
198
199private:
200 std::filesystem::path manifestJson;
201};
202} // namespace
203
204namespace {
205class WriteTraceChannelPort : public WriteChannelPort {
206public:
207 WriteTraceChannelPort(TraceAccelerator::Impl &impl, const Type *type,
208 const AppIDPath &id, const std::string &portName)
209 : WriteChannelPort(type), impl(impl), id(id), portName(portName) {}
210
211protected:
212 void writeImpl(const MessageData &data) override {
213 impl.write(id, portName, data.getBytes(), data.getSize());
214 }
215
216 bool tryWriteImpl(const MessageData &data) override {
217 impl.write(id, portName, data.getBytes(), data.getSize(), "try");
218 return true;
219 }
220
222 AppIDPath id;
223 std::string portName;
224};
225} // namespace
226
227namespace {
228class ReadTraceChannelPort : public ReadChannelPort {
229public:
230 ReadTraceChannelPort(TraceAccelerator::Impl &impl, const Type *type)
231 : ReadChannelPort(type) {}
232 ~ReadTraceChannelPort() { disconnect(); }
233
234private:
235 MessageData genMessage() {
236 std::ptrdiff_t numBits = getType()->getBitWidth();
237 if (numBits < 0)
238 // TODO: support other types.
239 throw std::runtime_error("unsupported type for read: " +
240 getType()->getID());
241
242 std::ptrdiff_t size = (numBits + 7) / 8;
243 // Zero-width types need a 1-byte placeholder.
244 if (size == 0)
245 return MessageData({0});
246 std::vector<uint8_t> bytes(size);
247 for (std::ptrdiff_t i = 0; i < size; ++i)
248 bytes[i] = rand() % 256;
249 return MessageData(bytes);
250 }
251
252 bool pollImpl() override {
253 if (!pendingMessage)
254 pendingMessage = std::make_unique<MessageData>(genMessage());
255 if (!invokeCallback(pendingMessage))
256 return false;
257 pendingMessage.reset();
258 return true;
259 }
260
261 std::unique_ptr<SegmentedMessageData> pendingMessage;
262};
263} // namespace
264
265namespace {
266class TraceEngine : public Engine {
267public:
268 TraceEngine(AcceleratorConnection &conn, TraceAccelerator::Impl &impl)
269 : Engine(conn), impl(impl) {}
270
271 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
272 const std::string &channelName,
274 const Type *type) override {
275 std::unique_ptr<ChannelPort> port;
276 if (BundlePort::isWrite(dir))
277 port = std::make_unique<WriteTraceChannelPort>(impl, type, idPath,
278 channelName);
279 else
280 port = std::make_unique<ReadTraceChannelPort>(impl, type);
281 return port;
282 }
283
284private:
286};
287} // namespace
288
289void TraceAccelerator::createEngine(const std::string &dmaEngineName,
290 AppIDPath idPath,
291 const ServiceImplDetails &details,
292 const HWClientDetails &clients) {
293 registerEngine(idPath, std::make_unique<TraceEngine>(*this, getImpl()),
294 clients);
295}
296
297class TraceMMIO : public MMIO {
298public:
300 const HWClientDetails &clients)
301 : MMIO(conn, idPath, clients), impl(conn.getImpl()) {}
302
303 virtual uint64_t read(uint32_t addr) const override {
304 uint64_t data = rand();
305 if (impl.isWriteable())
306 impl.write("MMIO") << "[" << std::hex << addr << "] -> " << data
307 << std::endl;
308 return data;
309 }
310 virtual void write(uint32_t addr, uint64_t data) override {
311 if (!impl.isWriteable())
312 return;
313 impl.write("MMIO") << "[" << std::hex << addr << "] <- " << data
314 << std::endl;
315 }
316
317private:
319};
320
321class TraceHostMem : public HostMem {
322public:
323 TraceHostMem(TraceAccelerator &conn) : HostMem(conn), impl(conn.getImpl()) {}
324
327 : impl(impl) {
328 ptr = malloc(size);
329 this->size = size;
330 }
332 if (impl.isWriteable())
333 impl.write("HostMem") << "free " << ptr << std::endl;
334 free(ptr);
335 }
336 virtual void *getPtr() const override { return ptr; }
337 virtual std::size_t getSize() const override { return size; }
338
339 private:
340 void *ptr;
341 std::size_t size;
343 };
344
345 virtual std::unique_ptr<HostMemRegion>
346 allocate(std::size_t size, HostMem::Options opts) const override {
347 auto ret =
348 std::unique_ptr<HostMemRegion>(new TraceHostMemRegion(size, impl));
349 if (impl.isWriteable())
350 impl.write("HostMem 0x")
351 << ret->getPtr() << " allocate " << size
352 << " bytes. Writeable: " << opts.writeable
353 << ", useLargePages: " << opts.useLargePages << std::endl;
354 return ret;
355 }
356 virtual bool mapMemory(void *ptr, std::size_t size,
357 HostMem::Options opts) const override {
358
359 if (impl.isWriteable())
360 impl.write("HostMem")
361 << "map 0x" << ptr << " size " << size
362 << " bytes. Writeable: " << opts.writeable
363 << ", useLargePages: " << opts.useLargePages << std::endl;
364 return true;
365 }
366 virtual void unmapMemory(void *ptr) const override {
367 if (impl.isWriteable())
368 impl.write("HostMem") << "unmap 0x" << ptr << std::endl;
369 }
370
371private:
373};
374
376 AppIDPath idPath, std::string implName,
377 const ServiceImplDetails &details,
378 const HWClientDetails &clients) {
379 if (svcType == typeid(SysInfo))
380 return new TraceSysInfo(*this, getImpl().manifestJson);
381 if (svcType == typeid(MMIO))
382 return new TraceMMIO(*this, idPath, clients);
383 if (svcType == typeid(HostMem))
384 return new TraceHostMem(*this);
385 return nullptr;
386}
387
#define REGISTER_ACCELERATOR(Name, TAccelerator)
assert(baseType &&"element must be base type")
virtual std::unique_ptr< HostMemRegion > allocate(std::size_t size, HostMem::Options opts) const override
Allocate a region of host memory in accelerator accessible address space.
Definition Trace.cpp:346
TraceHostMem(TraceAccelerator &conn)
Definition Trace.cpp:323
virtual bool mapMemory(void *ptr, std::size_t size, HostMem::Options opts) const override
Try to make a region of host memory accessible to the accelerator.
Definition Trace.cpp:356
virtual void unmapMemory(void *ptr) const override
Unmap memory which was previously mapped with 'mapMemory'.
Definition Trace.cpp:366
TraceAccelerator::Impl & impl
Definition Trace.cpp:372
TraceMMIO(TraceAccelerator &conn, const AppIDPath &idPath, const HWClientDetails &clients)
Definition Trace.cpp:299
virtual uint64_t read(uint32_t addr) const override
Read a 64-bit value from the global MMIO space.
Definition Trace.cpp:303
virtual void write(uint32_t addr, uint64_t data) override
Write a 64-bit value to the global MMIO space.
Definition Trace.cpp:310
TraceAccelerator::Impl & impl
Definition Trace.cpp:318
Abstract class representing a connection to an accelerator.
Context & ctxt
ESI accelerator context.
void registerEngine(AppIDPath idPath, std::unique_ptr< Engine > engine, const HWClientDetails &clients)
If createEngine is overridden, this method should be called to register the engine and all of the cha...
virtual void disconnect()
Disconnect from the accelerator cleanly.
Unidirectional channels are the basic communication primitive between the host and accelerator.
Definition Ports.h:115
AcceleratorConnections, Accelerators, and Manifests must all share a context.
Definition Context.h:34
Engines implement the actual channel communication between the host and the accelerator.
Definition Engines.h:42
Class to parse a manifest.
Definition Manifest.h:39
uint32_t getApiVersion() const
A concrete flat message backed by a single vector of bytes.
Definition Common.h:155
A ChannelPort which reads data from the accelerator.
Definition Ports.h:453
Root class of the ESI type system.
Definition Types.h:36
A ChannelPort which sends data to the accelerator.
Definition Ports.h:308
Connect to an ESI simulation.
Definition Trace.h:35
std::unique_ptr< Impl > impl
Definition Trace.h:84
TraceAccelerator(Context &, Mode mode, std::filesystem::path manifestJson, std::filesystem::path traceFile)
Create a trace-based accelerator backend.
Definition Trace.cpp:164
void createEngine(const std::string &engineTypeName, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients) override
Create a new engine for channel communication with the accelerator.
Definition Trace.cpp:289
virtual Service * createService(Service::Type service, AppIDPath idPath, std::string implName, const ServiceImplDetails &details, const HWClientDetails &clients) override
Called by getServiceImpl exclusively.
Definition Trace.cpp:375
static std::unique_ptr< AcceleratorConnection > connect(Context &, std::string connectionString)
Parse the connection string and instantiate the accelerator.
Definition Trace.cpp:102
Parent class of all APIs modeled as 'services'.
Definition Services.h:59
const std::type_info & Type
Definition Services.h:61
Information about the Accelerator system.
Definition Services.h:113
void encodeBase64(const void *data, size_t size, std::string &out)
Definition Utils.cpp:23
Definition esi.py:1
std::map< std::string, std::any > ServiceImplDetails
Definition Common.h:108
std::vector< HWClientDetail > HWClientDetails
Definition Common.h:107
virtual std::size_t getSize() const override
Definition Trace.cpp:337
TraceHostMemRegion(std::size_t size, TraceAccelerator::Impl &impl)
Definition Trace.cpp:326
virtual void * getPtr() const override
Get a pointer to the host memory.
Definition Trace.cpp:336
TraceAccelerator::Impl & impl
Definition Trace.cpp:342
void write(const AppIDPath &id, const std::string &portName, const void *data, size_t size, const std::string &prefix="")
Definition Trace.cpp:88
std::vector< std::unique_ptr< ChannelPort > > channels
Definition Trace.cpp:85
std::ostream & write(std::string service)
Definition Trace.cpp:74
void adoptChannelPort(ChannelPort *port)
Definition Trace.cpp:70
Service * createService(TraceAccelerator &conn, Service::Type svcType, AppIDPath idPath, const ServiceImplDetails &details, const HWClientDetails &clients)
Impl(Mode mode, std::filesystem::path manifestJson, std::filesystem::path traceFile)
Definition Trace.cpp:39
RAII memory region for host memory.
Definition Services.h:243
Options for allocating host memory.
Definition Services.h:261