Line data Source code
1 : // Copyright 2024, UChicago Argonne, LLC
2 : // All Rights Reserved
3 : // Software Name: NEML2 -- the New Engineering material Model Library, version 2
4 : // By: Argonne National Laboratory
5 : // OPEN SOURCE LICENSE (MIT)
6 : //
7 : // Permission is hereby granted, free of charge, to any person obtaining a copy
8 : // of this software and associated documentation files (the "Software"), to deal
9 : // in the Software without restriction, including without limitation the rights
10 : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 : // copies of the Software, and to permit persons to whom the Software is
12 : // furnished to do so, subject to the following conditions:
13 : //
14 : // The above copyright notice and this permission notice shall be included in
15 : // all copies or substantial portions of the Software.
16 : //
17 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 : // THE SOFTWARE.
24 :
25 : #include <chrono>
26 : #include <filesystem>
27 : #include <memory>
28 : #include <thread>
29 :
30 : #include "neml2/base/TracingInterface.h"
31 : #include "neml2/misc/assertions.h"
32 :
33 : namespace neml2
34 : {
35 : std::unordered_map<std::string, std::unique_ptr<TraceWriter>> &
36 8 : event_trace_writers()
37 : {
38 8 : static std::unordered_map<std::string, std::unique_ptr<TraceWriter>> trace_writers;
39 8 : return trace_writers;
40 : }
41 :
42 3 : TraceWriter::TraceWriter(const std::filesystem::path & file)
43 3 : : filename(file.string()),
44 3 : _epoch(std::chrono::high_resolution_clock::now())
45 : {
46 : #ifdef NEML2_HAS_JSON
47 : {
48 3 : std::lock_guard<std::mutex> lock(_mtx);
49 3 : out.open(file);
50 3 : neml_assert(out.is_open(), "Failed to open trace file: " + file.string());
51 : // Write an opening bracket (because the chrome tracing format is represented as a JSON array)
52 : // We eagerly write this in case the program crashes before the closing bracket is written.
53 : // Note that the chrome tracing format supports this behavior (i.e. it can handle a partial JSON
54 : // array without a closing bracket).
55 3 : out << "[\n";
56 3 : }
57 :
58 24 : trace_duration_begin("trace writer", "TraceWriter", {{"file", file.string()}});
59 : #endif
60 15 : }
61 :
62 6 : TraceWriter::~TraceWriter()
63 : {
64 : #ifdef NEML2_HAS_JSON
65 3 : json event;
66 24 : write_event_common(event, "trace writer", "TraceWriter", {{"file", filename}}, "E", 0);
67 3 : dump_event(event, true);
68 3 : std::lock_guard<std::mutex> lock(_mtx);
69 3 : out << "]\n";
70 3 : out.close();
71 : #endif
72 15 : }
73 :
74 : #ifdef NEML2_HAS_JSON
75 : void
76 18 : TraceWriter::write_event_common(json & event,
77 : const std::string & name,
78 : const std::string & category,
79 : const json & args,
80 : const std::string & phase,
81 : unsigned int pid)
82 : {
83 18 : event["name"] = name;
84 18 : event["cat"] = category;
85 18 : event["ph"] = phase;
86 36 : event["ts"] = std::chrono::duration_cast<std::chrono::microseconds>(
87 18 : std::chrono::high_resolution_clock::now() - _epoch)
88 36 : .count();
89 18 : event["pid"] = pid;
90 18 : event["tid"] =
91 36 : static_cast<unsigned short>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
92 18 : event["args"] = args;
93 18 : }
94 :
95 : void
96 18 : TraceWriter::dump_event(const json & event, bool last)
97 : {
98 18 : std::lock_guard<std::mutex> lock(_mtx);
99 18 : out << event.dump();
100 18 : if (!last)
101 15 : out << ',';
102 18 : out << '\n';
103 18 : }
104 :
105 : void
106 7 : TraceWriter::trace_duration_begin(const std::string & name,
107 : const std::string & category,
108 : const json & args,
109 : unsigned int pid)
110 : {
111 7 : json event;
112 7 : write_event_common(event, name, category, args, "B", pid);
113 7 : dump_event(event);
114 7 : }
115 :
116 : void
117 4 : TraceWriter::trace_duration_end(const std::string & name,
118 : const std::string & category,
119 : const json & args,
120 : unsigned int pid)
121 : {
122 4 : json event;
123 4 : write_event_common(event, name, category, args, "E", pid);
124 4 : dump_event(event);
125 4 : }
126 :
127 : void
128 4 : TraceWriter::trace_instant(const std::string & name,
129 : const std::string & category,
130 : const json & args,
131 : const std::string & scope,
132 : unsigned int pid)
133 : {
134 4 : json event;
135 4 : write_event_common(event, name, category, args, "i", pid);
136 4 : event["s"] = scope;
137 4 : dump_event(event);
138 4 : }
139 : #endif
140 :
141 : OptionSet
142 17 : TracingInterface::expected_options()
143 : {
144 17 : OptionSet options;
145 34 : options.set<std::string>("trace_file") = "";
146 34 : options.set("trace_file").doc() =
147 17 : "The file to write the trace to. If not set, tracing will be disabled.";
148 17 : return options;
149 0 : }
150 :
151 15 : TracingInterface::TracingInterface(std::string trace_file)
152 30 : : _enabled(!trace_file.empty()),
153 15 : _writer(_enabled ? &init_writer(std::move(trace_file)) : nullptr)
154 : {
155 15 : }
156 :
157 11 : TracingInterface::TracingInterface(const OptionSet & options)
158 11 : : TracingInterface(options.get<std::string>("trace_file"))
159 : {
160 11 : }
161 :
162 : TraceWriter &
163 4 : TracingInterface::init_writer(std::string filename)
164 : {
165 : #ifndef NEML2_HAS_JSON
166 : throw NEMLException("TracingInterface: JSON support is required for event tracing.");
167 : #endif
168 :
169 : static std::mutex trace_writer_mutex;
170 4 : std::lock_guard<std::mutex> lock(trace_writer_mutex);
171 :
172 4 : auto file = std::filesystem::absolute(std::move(filename));
173 4 : auto & trace_writers = event_trace_writers();
174 4 : auto it = trace_writers.find(file);
175 4 : if (it != trace_writers.end())
176 1 : return *it->second;
177 :
178 : // Create a new TraceWriter
179 3 : auto writer = std::make_unique<TraceWriter>(file);
180 3 : auto [it2, success] = trace_writers.emplace(std::string(file), std::move(writer));
181 3 : neml_assert(success, "Internal error: Trace writer already exists: ", file.string());
182 3 : return *it2->second;
183 4 : }
184 :
185 : TraceWriter &
186 4 : TracingInterface::event_trace_writer() const
187 : {
188 4 : neml_assert(_enabled, "Event tracing is not enabled");
189 4 : neml_assert(_writer != nullptr, "Event trace writer is not initialized");
190 4 : return *_writer;
191 : }
192 : } // namespace neml2
|