LCOV - code coverage report
Current view: top level - base - TracingInterface.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.8 % 86 85
Test Date: 2025-10-02 16:03:03 Functions: 100.0 % 13 13

            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
        

Generated by: LCOV version 2.0-1