Enable Execution trace

Generate the trace file with CLI

```
kelvin_sim --trace <elf file>
```

PiperOrigin-RevId: 560821841
diff --git a/sim/kelvin_top.cc b/sim/kelvin_top.cc
index 1deb21c..9955c3f 100644
--- a/sim/kelvin_top.cc
+++ b/sim/kelvin_top.cc
@@ -1,7 +1,13 @@
 #include "sim/kelvin_top.h"
 
+#include <sys/stat.h>
+
+#include <cerrno>
 #include <cstdint>
 #include <cstring>
+#include <fstream>
+#include <iomanip>
+#include <ios>
 #include <iostream>
 #include <string>
 #include <thread>  // NOLINT(build/c++11): built with c++17
@@ -31,13 +37,17 @@
 #include "mpact/sim/util/memory/flat_demand_memory.h"
 
 ABSL_FLAG(bool, use_semihost, false, "Use semihost in the simulation");
+ABSL_FLAG(bool, trace, false, "Dump executed instruction trace");
+ABSL_FLAG(std::string, trace_path, "/tmp/kelvin_trace.txt",
+          "Path to save trace");
 
 namespace kelvin::sim {
 
 constexpr char kKelvinName[] = "Kelvin";
 
 // Local helper function used to execute instructions.
-static inline bool ExecuteInstruction(mpact::sim::util::Instruction *inst) {
+static inline bool ExecuteInstruction(mpact::sim::util::Instruction *inst,
+                                      std::fstream *trace = nullptr) {
   for (auto *resource : inst->ResourceHold()) {
     if (!resource->IsFree()) {
       return false;
@@ -46,9 +56,12 @@
   for (auto *resource : inst->ResourceAcquire()) {
     resource->Acquire();
   }
-  // Comment out instruction logging during execution.
-  // LOG(INFO) << "[" << std::hex << inst->address() << "] " <<
-  // inst->AsString();
+  if (trace != nullptr) {
+    // TODO(hcindyl): Use protobuf to store the serialized trace.
+    *trace << "["
+           << "0x" << std::setfill('0') << std::setw(8) << std::hex
+           << inst->address() << "] " << inst->AsString() << std::endl;
+  }
 
   inst->Execute(nullptr);
   return true;
@@ -326,6 +339,22 @@
     // be executed.
     uint64_t next_pc = pc_operand->AsUint64(0);
     uint64_t next_seq_pc;
+
+    std::fstream trace_file;
+    if (absl::GetFlag(FLAGS_trace)) {
+      std::string trace_path = absl::GetFlag(FLAGS_trace_path);
+      std::string trace_dir =
+          trace_path.substr(0, trace_path.find_last_of('/'));
+      int res = mkdir(trace_dir.c_str(), 0777);
+      if (res == 0 || errno == EEXIST) {
+        trace_file.open(absl::GetFlag(FLAGS_trace_path), std::ios_base::out);
+        std::cout << "Dump trace file at " << absl::GetFlag(FLAGS_trace_path)
+                  << std::endl;
+      } else {
+        std::cerr << "Failed to create " << trace_dir << std::endl;
+      }
+    }
+
     while (!halted_) {
       pc = next_pc;
       auto *inst = decode_cache_->GetDecodedInstruction(pc);
@@ -334,8 +363,9 @@
       // executed will overwrite this.
       SetPc(next_seq_pc);
       bool executed = false;
+      std::fstream *trace_ptr = trace_file.is_open() ? &trace_file : nullptr;
       do {
-        executed = ExecuteInstruction(inst);
+        executed = ExecuteInstruction(inst, trace_ptr);
         counter_num_cycles_.Increment(1);
         state_->AdvanceDelayLines();
       } while (!executed);
@@ -355,6 +385,10 @@
       SetPc(next_pc);
     }
     run_status_ = RunStatus::kHalted;
+
+    if (trace_file.is_open()) {
+      trace_file.close();
+    }
     // Notify that the run has completed.
     run_halted_->Notify();
   }).detach();