CIRCT 23.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
30// We only support v0.
31constexpr uint32_t ESIVersion = 0;
32
33namespace {
34class TraceChannelPort;
35class TraceEngine;
36} // namespace
37
39
41 friend class TraceAccelerator;
42 Impl(Mode mode, std::filesystem::path manifestJson,
43 std::filesystem::path traceFile)
45 if (!std::filesystem::exists(manifestJson))
46 throw std::runtime_error("manifest file '" + manifestJson.string() +
47 "' does not exist");
48
49 if (mode == Write) {
50 // Open the trace file for writing.
51 traceWrite = new std::ofstream(traceFile);
52 if (!traceWrite->is_open())
53 throw std::runtime_error("failed to open trace file '" +
54 traceFile.string() + "'");
55 } else if (mode == Discard) {
56 traceWrite = nullptr;
57 } else {
58 assert(false && "not implemented");
59 }
60 }
61
63 if (traceWrite) {
64 traceWrite->close();
65 delete traceWrite;
66 }
67 }
68
70 AppIDPath idPath, const ServiceImplDetails &details,
71 const HWClientDetails &clients);
72
73 void adoptChannelPort(ChannelPort *port) { channels.emplace_back(port); }
74
75 void write(const AppIDPath &id, const std::string &portName, const void *data,
76 size_t size, const std::string &prefix = "");
77 std::ostream &write(std::string service) {
78 assert(traceWrite && "traceWrite is null");
79 *traceWrite << "[" << service << "] ";
80 return *traceWrite;
81 }
82 bool isWriteable() { return traceWrite; }
83
84private:
85 std::ofstream *traceWrite;
86 std::filesystem::path manifestJson;
87 std::filesystem::path traceFile;
88 std::vector<std::unique_ptr<ChannelPort>> channels;
89};
90
91void TraceAccelerator::Impl::write(const AppIDPath &id,
92 const std::string &portName,
93 const void *data, size_t size,
94 const std::string &prefix) {
95 if (!isWriteable())
96 return;
97 std::string b64data;
98 utils::encodeBase64(data, size, b64data);
99
100 *traceWrite << prefix << (prefix.empty() ? "w" : "W") << "rite " << id << '.'
101 << portName << ": " << b64data << std::endl;
102}
103
104std::unique_ptr<AcceleratorConnection>
105TraceAccelerator::connect(Context &ctxt, std::string connectionString) {
106 std::string manifestPath;
107 std::string traceFile = "trace.log";
108
109 // Parse the connection std::string.
110 // Format: <mode>SEP<manifest path>[SEP<traceFile>]
111 // where SEP is ':' on Unix and ';' on Windows.
112 // Using different separators avoids conflicts with Windows drive letters
113 // (e.g. "C:/...").
114#ifdef _WIN32
115 constexpr char SEP = ';';
116#else
117 constexpr char SEP = ':';
118#endif
119
120 if (connectionString.size() < 2 || connectionString[1] != SEP)
121 throw std::runtime_error(
122 std::string("connection string must be of the form ") + "'<mode>" +
123 SEP + "<manifest path>[" + SEP + "<traceFile>]'");
124
125 char modeChar = connectionString[0];
126 std::string rest = connectionString.substr(2);
127
128 // Find the optional trace file, separated by the last SEP.
129 size_t lastSep = rest.rfind(SEP);
130 if (lastSep != std::string::npos) {
131 manifestPath = rest.substr(0, lastSep);
132 traceFile = rest.substr(lastSep + 1);
133
134 // Validate that the trace file name is non-empty when present.
135 if (traceFile.empty())
136 throw std::runtime_error(
137 std::string("connection string must be of the form ") + "'<mode>" +
138 SEP + "<manifest path>[" + SEP +
139 "<traceFile>]': "
140 "trace file name cannot be empty");
141 } else {
142 manifestPath = rest;
143 }
144
145 // Validate that manifest path is non-empty.
146 if (manifestPath.empty())
147 throw std::runtime_error(
148 std::string("connection string must be of the form ") + "'<mode>" +
149 SEP + "<manifest path>[" + SEP +
150 "<traceFile>]': "
151 "manifest path cannot be empty");
152
153 // Parse the mode.
154 Mode mode;
155 if (modeChar == 'w')
156 mode = Write;
157 else if (modeChar == '-')
158 mode = Discard;
159 else
160 throw std::runtime_error("unknown mode '" + std::string(1, modeChar) + "'");
161
162 return std::make_unique<TraceAccelerator>(ctxt, mode,
163 std::filesystem::path(manifestPath),
164 std::filesystem::path(traceFile));
165}
166
168 std::filesystem::path manifestJson,
169 std::filesystem::path traceFile)
171 impl = std::make_unique<Impl>(mode, manifestJson, traceFile);
172}
174
175namespace {
176class TraceSysInfo : public SysInfo {
177public:
178 TraceSysInfo(AcceleratorConnection &conn, std::filesystem::path manifestJson)
179 : SysInfo(conn), manifestJson(manifestJson) {}
180
181 uint32_t getEsiVersion() const override { return ESIVersion; }
182
183 std::string getJsonManifest() const override {
184 // Read in the whole json file and return it.
185 std::ifstream manifest(manifestJson);
186 if (!manifest.is_open())
187 throw std::runtime_error("failed to open manifest file '" +
188 manifestJson.string() + "'");
189 std::stringstream buffer;
190 buffer << manifest.rdbuf();
191 manifest.close();
192 return buffer.str();
193 }
194
195 std::vector<uint8_t> getCompressedManifest() const override {
196 throw std::runtime_error(
197 "compressed manifest not supported by trace backend");
198 }
199
200private:
201 std::filesystem::path manifestJson;
202};
203} // namespace
204
205namespace {
206class WriteTraceChannelPort : public WriteChannelPort {
207public:
208 WriteTraceChannelPort(TraceAccelerator::Impl &impl, const Type *type,
209 const AppIDPath &id, const std::string &portName)
210 : WriteChannelPort(type), impl(impl), id(id), portName(portName) {}
211
212protected:
213 void writeImpl(const MessageData &data) override {
214 impl.write(id, portName, data.getBytes(), data.getSize());
215 }
216
217 bool tryWriteImpl(const MessageData &data) override {
218 impl.write(id, portName, data.getBytes(), data.getSize(), "try");
219 return true;
220 }
221
223 AppIDPath id;
224 std::string portName;
225};
226} // namespace
227
228namespace {
229class ReadTraceChannelPort : public ReadChannelPort {
230public:
231 ReadTraceChannelPort(TraceAccelerator::Impl &impl, const Type *type)
232 : ReadChannelPort(type) {}
233 ~ReadTraceChannelPort() { disconnect(); }
234
235private:
236 MessageData genMessage() {
237 std::ptrdiff_t numBits = getType()->getBitWidth();
238 if (numBits < 0)
239 // TODO: support other types.
240 throw std::runtime_error("unsupported type for read: " +
241 getType()->getID());
242
243 std::ptrdiff_t size = (numBits + 7) / 8;
244 std::vector<uint8_t> bytes(size);
245 for (std::ptrdiff_t i = 0; i < size; ++i)
246 bytes[i] = rand() % 256;
247 return MessageData(bytes);
248 }
249
250 bool pollImpl() override { return callback(genMessage()); }
251};
252} // namespace
253
254namespace {
255class TraceEngine : public Engine {
256public:
257 TraceEngine(AcceleratorConnection &conn, TraceAccelerator::Impl &impl)
258 : Engine(conn), impl(impl) {}
259
260 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
261 const std::string &channelName,
263 const Type *type) override {
264 std::unique_ptr<ChannelPort> port;
265 if (BundlePort::isWrite(dir))
266 port = std::make_unique<WriteTraceChannelPort>(impl, type, idPath,
267 channelName);
268 else
269 port = std::make_unique<ReadTraceChannelPort>(impl, type);
270 return port;
271 }
272
273private:
275};
276} // namespace
277
278void TraceAccelerator::createEngine(const std::string &dmaEngineName,
279 AppIDPath idPath,
280 const ServiceImplDetails &details,
281 const HWClientDetails &clients) {
282 registerEngine(idPath, std::make_unique<TraceEngine>(*this, getImpl()),
283 clients);
284}
285
286class TraceMMIO : public MMIO {
287public:
289 const HWClientDetails &clients)
290 : MMIO(conn, idPath, clients), impl(conn.getImpl()) {}
291
292 virtual uint64_t read(uint32_t addr) const override {
293 uint64_t data = rand();
294 if (impl.isWriteable())
295 impl.write("MMIO") << "[" << std::hex << addr << "] -> " << data
296 << std::endl;
297 return data;
298 }
299 virtual void write(uint32_t addr, uint64_t data) override {
300 if (!impl.isWriteable())
301 return;
302 impl.write("MMIO") << "[" << std::hex << addr << "] <- " << data
303 << std::endl;
304 }
305
306private:
308};
309
310class TraceHostMem : public HostMem {
311public:
312 TraceHostMem(TraceAccelerator &conn) : HostMem(conn), impl(conn.getImpl()) {}
313
316 : impl(impl) {
317 ptr = malloc(size);
318 this->size = size;
319 }
321 if (impl.isWriteable())
322 impl.write("HostMem") << "free " << ptr << std::endl;
323 free(ptr);
324 }
325 virtual void *getPtr() const override { return ptr; }
326 virtual std::size_t getSize() const override { return size; }
327
328 private:
329 void *ptr;
330 std::size_t size;
332 };
333
334 virtual std::unique_ptr<HostMemRegion>
335 allocate(std::size_t size, HostMem::Options opts) const override {
336 auto ret =
337 std::unique_ptr<HostMemRegion>(new TraceHostMemRegion(size, impl));
338 if (impl.isWriteable())
339 impl.write("HostMem 0x")
340 << ret->getPtr() << " allocate " << size
341 << " bytes. Writeable: " << opts.writeable
342 << ", useLargePages: " << opts.useLargePages << std::endl;
343 return ret;
344 }
345 virtual bool mapMemory(void *ptr, std::size_t size,
346 HostMem::Options opts) const override {
347
348 if (impl.isWriteable())
349 impl.write("HostMem")
350 << "map 0x" << ptr << " size " << size
351 << " bytes. Writeable: " << opts.writeable
352 << ", useLargePages: " << opts.useLargePages << std::endl;
353 return true;
354 }
355 virtual void unmapMemory(void *ptr) const override {
356 if (impl.isWriteable())
357 impl.write("HostMem") << "unmap 0x" << ptr << std::endl;
358 }
359
360private:
362};
363
365 AppIDPath idPath, std::string implName,
366 const ServiceImplDetails &details,
367 const HWClientDetails &clients) {
368 if (svcType == typeid(SysInfo))
369 return new TraceSysInfo(*this, getImpl().manifestJson);
370 if (svcType == typeid(MMIO))
371 return new TraceMMIO(*this, idPath, clients);
372 if (svcType == typeid(HostMem))
373 return new TraceHostMem(*this);
374 return nullptr;
375}
376
#define REGISTER_ACCELERATOR(Name, TAccelerator)
assert(baseType &&"element must be base type")
constexpr uint32_t ESIVersion
Definition Trace.cpp:31
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:335
TraceHostMem(TraceAccelerator &conn)
Definition Trace.cpp:312
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:345
virtual void unmapMemory(void *ptr) const override
Unmap memory which was previously mapped with 'mapMemory'.
Definition Trace.cpp:355
TraceAccelerator::Impl & impl
Definition Trace.cpp:361
TraceMMIO(TraceAccelerator &conn, const AppIDPath &idPath, const HWClientDetails &clients)
Definition Trace.cpp:288
virtual uint64_t read(uint32_t addr) const override
Read a 64-bit value from the global MMIO space.
Definition Trace.cpp:292
virtual void write(uint32_t addr, uint64_t data) override
Write a 64-bit value to the global MMIO space.
Definition Trace.cpp:299
TraceAccelerator::Impl & impl
Definition Trace.cpp:307
Abstract class representing a connection to an accelerator.
Definition Accelerator.h:89
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:36
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
A logical chunk of data representing serialized data.
Definition Common.h:113
A ChannelPort which reads data from the accelerator.
Definition Ports.h:318
Root class of the ESI type system.
Definition Types.h:36
A ChannelPort which sends data to the accelerator.
Definition Ports.h:206
Connect to an ESI simulation.
Definition Trace.h:35
std::unique_ptr< Impl > impl
Definition Trace.h:81
TraceAccelerator(Context &, Mode mode, std::filesystem::path manifestJson, std::filesystem::path traceFile)
Create a trace-based accelerator backend.
Definition Trace.cpp:167
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:278
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:364
static std::unique_ptr< AcceleratorConnection > connect(Context &, std::string connectionString)
Parse the connection string and instantiate the accelerator.
Definition Trace.cpp:105
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:326
TraceHostMemRegion(std::size_t size, TraceAccelerator::Impl &impl)
Definition Trace.cpp:315
virtual void * getPtr() const override
Get a pointer to the host memory.
Definition Trace.cpp:325
TraceAccelerator::Impl & impl
Definition Trace.cpp:331
void write(const AppIDPath &id, const std::string &portName, const void *data, size_t size, const std::string &prefix="")
Definition Trace.cpp:91
std::vector< std::unique_ptr< ChannelPort > > channels
Definition Trace.cpp:88
std::ostream & write(std::string service)
Definition Trace.cpp:77
void adoptChannelPort(ChannelPort *port)
Definition Trace.cpp:73
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:42
RAII memory region for host memory.
Definition Services.h:237
Options for allocating host memory.
Definition Services.h:255