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 // Zero-width types need a 1-byte placeholder.
245 if (size == 0)
246 return MessageData({0});
247 std::vector<uint8_t> bytes(size);
248 for (std::ptrdiff_t i = 0; i < size; ++i)
249 bytes[i] = rand() % 256;
250 return MessageData(bytes);
251 }
252
253 bool pollImpl() override {
254 if (!pendingMessage)
255 pendingMessage = std::make_unique<MessageData>(genMessage());
256 if (!invokeCallback(pendingMessage))
257 return false;
258 pendingMessage.reset();
259 return true;
260 }
261
262 std::unique_ptr<SegmentedMessageData> pendingMessage;
263};
264} // namespace
265
266namespace {
267class TraceEngine : public Engine {
268public:
269 TraceEngine(AcceleratorConnection &conn, TraceAccelerator::Impl &impl)
270 : Engine(conn), impl(impl) {}
271
272 std::unique_ptr<ChannelPort> createPort(AppIDPath idPath,
273 const std::string &channelName,
275 const Type *type) override {
276 std::unique_ptr<ChannelPort> port;
277 if (BundlePort::isWrite(dir))
278 port = std::make_unique<WriteTraceChannelPort>(impl, type, idPath,
279 channelName);
280 else
281 port = std::make_unique<ReadTraceChannelPort>(impl, type);
282 return port;
283 }
284
285private:
287};
288} // namespace
289
290void TraceAccelerator::createEngine(const std::string &dmaEngineName,
291 AppIDPath idPath,
292 const ServiceImplDetails &details,
293 const HWClientDetails &clients) {
294 registerEngine(idPath, std::make_unique<TraceEngine>(*this, getImpl()),
295 clients);
296}
297
298class TraceMMIO : public MMIO {
299public:
301 const HWClientDetails &clients)
302 : MMIO(conn, idPath, clients), impl(conn.getImpl()) {}
303
304 virtual uint64_t read(uint32_t addr) const override {
305 uint64_t data = rand();
306 if (impl.isWriteable())
307 impl.write("MMIO") << "[" << std::hex << addr << "] -> " << data
308 << std::endl;
309 return data;
310 }
311 virtual void write(uint32_t addr, uint64_t data) override {
312 if (!impl.isWriteable())
313 return;
314 impl.write("MMIO") << "[" << std::hex << addr << "] <- " << data
315 << std::endl;
316 }
317
318private:
320};
321
322class TraceHostMem : public HostMem {
323public:
324 TraceHostMem(TraceAccelerator &conn) : HostMem(conn), impl(conn.getImpl()) {}
325
328 : impl(impl) {
329 ptr = malloc(size);
330 this->size = size;
331 }
333 if (impl.isWriteable())
334 impl.write("HostMem") << "free " << ptr << std::endl;
335 free(ptr);
336 }
337 virtual void *getPtr() const override { return ptr; }
338 virtual std::size_t getSize() const override { return size; }
339
340 private:
341 void *ptr;
342 std::size_t size;
344 };
345
346 virtual std::unique_ptr<HostMemRegion>
347 allocate(std::size_t size, HostMem::Options opts) const override {
348 auto ret =
349 std::unique_ptr<HostMemRegion>(new TraceHostMemRegion(size, impl));
350 if (impl.isWriteable())
351 impl.write("HostMem 0x")
352 << ret->getPtr() << " allocate " << size
353 << " bytes. Writeable: " << opts.writeable
354 << ", useLargePages: " << opts.useLargePages << std::endl;
355 return ret;
356 }
357 virtual bool mapMemory(void *ptr, std::size_t size,
358 HostMem::Options opts) const override {
359
360 if (impl.isWriteable())
361 impl.write("HostMem")
362 << "map 0x" << ptr << " size " << size
363 << " bytes. Writeable: " << opts.writeable
364 << ", useLargePages: " << opts.useLargePages << std::endl;
365 return true;
366 }
367 virtual void unmapMemory(void *ptr) const override {
368 if (impl.isWriteable())
369 impl.write("HostMem") << "unmap 0x" << ptr << std::endl;
370 }
371
372private:
374};
375
377 AppIDPath idPath, std::string implName,
378 const ServiceImplDetails &details,
379 const HWClientDetails &clients) {
380 if (svcType == typeid(SysInfo))
381 return new TraceSysInfo(*this, getImpl().manifestJson);
382 if (svcType == typeid(MMIO))
383 return new TraceMMIO(*this, idPath, clients);
384 if (svcType == typeid(HostMem))
385 return new TraceHostMem(*this);
386 return nullptr;
387}
388
#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:347
TraceHostMem(TraceAccelerator &conn)
Definition Trace.cpp:324
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:357
virtual void unmapMemory(void *ptr) const override
Unmap memory which was previously mapped with 'mapMemory'.
Definition Trace.cpp:367
TraceAccelerator::Impl & impl
Definition Trace.cpp:373
TraceMMIO(TraceAccelerator &conn, const AppIDPath &idPath, const HWClientDetails &clients)
Definition Trace.cpp:300
virtual uint64_t read(uint32_t addr) const override
Read a 64-bit value from the global MMIO space.
Definition Trace.cpp:304
virtual void write(uint32_t addr, uint64_t data) override
Write a 64-bit value to the global MMIO space.
Definition Trace.cpp:311
TraceAccelerator::Impl & impl
Definition Trace.cpp:319
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: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
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: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:290
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:376
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:338
TraceHostMemRegion(std::size_t size, TraceAccelerator::Impl &impl)
Definition Trace.cpp:327
virtual void * getPtr() const override
Get a pointer to the host memory.
Definition Trace.cpp:337
TraceAccelerator::Impl & impl
Definition Trace.cpp:343
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