[MemProf] Add interface for reseting the profile file descriptor (#73714)

Add __memprof_profile_reset() interface which can be used to facilitate
dumping multiple rounds of profiles from a single binary run. This
closes the current file descriptor and resets the internal file
descriptor to invalid (-1), which ensures the underlying writer reopens
the recorded profile filename. This can be used once the client is done
moving or copying a dumped profile, to prepare for reinvoking profile
dumping.

GitOrigin-RevId: ae86239e86e9e289167fad2d7465d254852ecb82
diff --git a/include/sanitizer/memprof_interface.h b/include/sanitizer/memprof_interface.h
index fe0a2fc..4660a78 100644
--- a/include/sanitizer/memprof_interface.h
+++ b/include/sanitizer/memprof_interface.h
@@ -59,6 +59,12 @@
 /// \returns 0 on success.
 int SANITIZER_CDECL __memprof_profile_dump(void);
 
+/// Closes the existing file descriptor, if it is valid and not stdout or
+/// stderr, and resets the internal state such that the profile filename is
+/// reopened on the next profile dump attempt. This can be used to enable
+/// multiple rounds of profiling on the same binary.
+void SANITIZER_CDECL __memprof_profile_reset(void);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/lib/memprof/memprof_allocator.cpp b/lib/memprof/memprof_allocator.cpp
index efdfa5a..af46ffd 100644
--- a/lib/memprof/memprof_allocator.cpp
+++ b/lib/memprof/memprof_allocator.cpp
@@ -738,3 +738,13 @@
   // detected during the dumping process.
   return 0;
 }
+
+void __memprof_profile_reset() {
+  if (report_file.fd != kInvalidFd && report_file.fd != kStdoutFd &&
+      report_file.fd != kStderrFd) {
+    CloseFile(report_file.fd);
+    // Setting the file descriptor to kInvalidFd ensures that we will reopen the
+    // file when invoking Write again.
+    report_file.fd = kInvalidFd;
+  }
+}
diff --git a/lib/memprof/memprof_interface_internal.h b/lib/memprof/memprof_interface_internal.h
index 0aca4af..318bc41 100644
--- a/lib/memprof/memprof_interface_internal.h
+++ b/lib/memprof/memprof_interface_internal.h
@@ -49,6 +49,7 @@
 SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE extern char
     __memprof_profile_filename[1];
 SANITIZER_INTERFACE_ATTRIBUTE int __memprof_profile_dump();
+SANITIZER_INTERFACE_ATTRIBUTE void __memprof_profile_reset();
 
 SANITIZER_INTERFACE_ATTRIBUTE void __memprof_load(uptr p);
 SANITIZER_INTERFACE_ATTRIBUTE void __memprof_store(uptr p);
diff --git a/test/memprof/TestCases/profile_reset.cpp b/test/memprof/TestCases/profile_reset.cpp
new file mode 100644
index 0000000..c9bbc44
--- /dev/null
+++ b/test/memprof/TestCases/profile_reset.cpp
@@ -0,0 +1,39 @@
+// Test to ensure that multiple rounds of dumping, using the
+// __memprof_profile_reset interface to close the initial file
+// and cause the profile to be reopened, works as expected.
+
+// RUN: %clangxx_memprof  %s -o %t
+
+// RUN: rm -f %t.log.*
+// RUN: %env_memprof_opts=print_text=true:log_path=%t.log %run %t
+
+// Check both outputs, starting with the renamed initial dump, then remove it so
+// that the second glob matches a single file.
+// RUN: FileCheck %s < %t.log.*.sv
+// RUN: rm -f %t.log.*.sv
+// RUN: FileCheck %s < %t.log.*
+// CHECK: Memory allocation stack id
+
+#include <sanitizer/memprof_interface.h>
+#include <stdio.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+int main(int argc, char **argv) {
+  char *x = (char *)malloc(10);
+  memset(x, 0, 10);
+  free(x);
+  __memprof_profile_dump();
+  // Save the initial dump in a different file.
+  std::string origname = __sanitizer_get_report_path();
+  std::string svname = origname + ".sv";
+  rename(origname.c_str(), svname.c_str());
+  // This should cause the current file descriptor to be closed and the
+  // the internal state reset so that the profile filename is reopened
+  // on the next write.
+  __memprof_profile_reset();
+  // This will dump to origname again.
+  __memprof_profile_dump();
+  return 0;
+}