Improve dump output code. (#52)
authorМихаил Куринной/AI Ecosystem Lab /SRR/Engineer/삼성전자 <m.kurinnoi@samsung.com>
Wed, 30 Jan 2019 15:43:20 +0000 (18:43 +0300)
committerAlexander Soldatov/AI Ecosystem Lab /SRR/Staff Engineer/삼성전자 <soldatov.a@samsung.com>
Wed, 30 Jan 2019 15:43:20 +0000 (18:43 +0300)
* Improve dump output code.

Proposed changes provide more flexible design for dump output
code in order to implement more output modules in future.

* Revise fprintf().

Use incremental buffer.
Setup errno, since heaptrack use it in writeError().

* Add error output in outStreamFILE constructor.

* Add more comments.

* Provide proper errno in Putc() and Puts().

* Cosmetic changes.

* Fix misspell.

* Revise fprintf().

src/track/CMakeLists.txt
src/track/heaptrack_inject.cpp
src/track/libheaptrack.cpp
src/track/libheaptrack.h
src/track/objectgraph.h
src/track/outstream/outstream.cpp [new file with mode: 0644]
src/track/outstream/outstream.h [new file with mode: 0644]
src/track/outstream/outstream_file.cpp [new file with mode: 0644]
src/track/outstream/outstream_file.h [new file with mode: 0644]
src/track/tracetree.h

index 162cacd081625ae11a730558b9f8b67f333bec2d..6ad255dc8bc60b32f18a0b63e92c8facfbf884ac 100644 (file)
@@ -18,6 +18,8 @@ endif()
 add_library(heaptrack_preload MODULE
     heaptrack_preload.cpp
     libheaptrack.cpp
+    outstream/outstream.cpp
+    outstream/outstream_file.cpp
 )
 
 target_compile_options(heaptrack_preload PRIVATE "-ftls-model=initial-exec")
@@ -44,6 +46,8 @@ install(TARGETS heaptrack_preload
 add_library(heaptrack_inject MODULE
     heaptrack_inject.cpp
     libheaptrack.cpp
+    outstream/outstream.cpp
+    outstream/outstream_file.cpp
 )
 
 target_link_libraries(heaptrack_inject LINK_PRIVATE
index 5684649fb52ad8d326956dc1181ef1907ab9e3c9..1aeff881a27c984d8aed4c0618ac0b9cf0597728 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "libheaptrack.h"
 #include "util/config.h"
+#include "outstream/outstream.h"
 
 #include <cstdlib>
 #include <cstring>
@@ -550,7 +551,7 @@ void heaptrack_inject(const char* outputFileName) noexcept
         isExiting = true;
     });
 
-    heaptrack_init(outputFileName, []() { overwrite_symbols(); }, [](FILE* out) { fprintf(out, "A\n"); },
+    heaptrack_init(outputFileName, []() { overwrite_symbols(); }, [](outStream* out) { fprintf(out, "A\n"); },
                    []() {
                        bool do_shutdown = true;
                        dl_iterate_phdr(&iterate_phdrs, &do_shutdown);
index 99f6499f1fa2bd6b6712059b0fca779a98bca6bf..d50cc1d38bd0c7d45337352867fa998477ebbbe5 100644 (file)
@@ -53,6 +53,7 @@
 #include "objectgraph.h"
 #include "util/config.h"
 #include "util/libunwind_config.h"
+#include "outstream/outstream_file.h"
 
 /**
  * uncomment this to get extended debug code for known pointers
@@ -85,12 +86,12 @@ atomic<bool> s_atexit{false};
  */
 atomic<bool> s_forceCleanup{false};
 
-void writeVersion(FILE* out)
+void writeVersion(outStream* out)
 {
     fprintf(out, "v %x %x\n", HEAPTRACK_VERSION, HEAPTRACK_FILE_FORMAT_VERSION);
 }
 
-void writeExe(FILE* out)
+void writeExe(outStream* out)
 {
     const int BUF_SIZE = 1023;
     char buf[BUF_SIZE + 1];
@@ -101,7 +102,7 @@ void writeExe(FILE* out)
     }
 }
 
-void writeCommandLine(FILE* out)
+void writeCommandLine(outStream* out)
 {
     fputc('X', out);
     const int BUF_SIZE = 4096;
@@ -120,12 +121,12 @@ void writeCommandLine(FILE* out)
     fputc('\n', out);
 }
 
-void writeSystemInfo(FILE* out)
+void writeSystemInfo(outStream* out)
 {
     fprintf(out, "I %lx %lx\n", sysconf(_SC_PAGESIZE), sysconf(_SC_PHYS_PAGES));
 }
 
-FILE* createFile(const char* fileName)
+outStream* createFile(const char* fileName)
 {
     string outputFileName;
     if (fileName) {
@@ -134,10 +135,10 @@ FILE* createFile(const char* fileName)
 
     if (outputFileName == "-" || outputFileName == "stdout") {
         debugLog<VerboseOutput>("%s", "will write to stdout");
-        return stdout;
+        return OpenStream<outStreamFILE, FILE*>(stdout);
     } else if (outputFileName == "stderr") {
         debugLog<VerboseOutput>("%s", "will write to stderr");
-        return stderr;
+        return OpenStream<outStreamFILE, FILE*>(stderr);
     }
 
     if (outputFileName.empty()) {
@@ -147,10 +148,8 @@ FILE* createFile(const char* fileName)
 
     boost::replace_all(outputFileName, "$$", to_string(getpid()));
 
-    auto out = fopen(outputFileName.c_str(), "w");
+    auto out = OpenStream<outStreamFILE, const char*>(outputFileName.c_str());
     debugLog<VerboseOutput>("will write to %s/%p\n", outputFileName.c_str(), out);
-    // we do our own locking, this speeds up the writing significantly
-    __fsetlocking(out, FSETLOCKING_BYCALLER);
     return out;
 }
 
@@ -223,7 +222,7 @@ public:
             });
         });
 
-        FILE* out = createFile(fileName);
+        outStream* out = createFile(fileName);
 
         if (!out) {
             fprintf(stderr, "ERROR: Failed to open heaptrack output file: %s\n", fileName);
@@ -706,7 +705,7 @@ private:
 
     struct LockedData
     {
-        LockedData(FILE* out, heaptrack_callback_t stopCallback)
+        LockedData(outStream* out, heaptrack_callback_t stopCallback)
             : out(out)
             , stopCallback(stopCallback)
         {
@@ -773,7 +772,7 @@ private:
             }
 
             if (out) {
-                fclose(out);
+                delete out;
             }
 
             if (procSmaps) {
@@ -791,7 +790,7 @@ private:
          *       Esp. in multi-threaded environments this is much faster
          *       to produce non-per-line-interleaved output.
          */
-        FILE* out = nullptr;
+        outStream* out = nullptr;
 
         /// /proc/self/smaps file stream to read address range data from
         FILE* procSmaps = nullptr;
index 843591059d9242724ac7d79c2e8ba093f91f4dd8..b9c15b5e5e785077bd2b4efc07913f2c41eeb83b 100644 (file)
@@ -74,7 +74,8 @@ extern "C" {
 #endif
 
 typedef void (*heaptrack_callback_t)();
-typedef void (*heaptrack_callback_initialized_t)(FILE*);
+class outStream;
+typedef void (*heaptrack_callback_initialized_t)(outStream*);
 
 void heaptrack_init(const char* outputFileName, heaptrack_callback_t initCallbackBefore,
                     heaptrack_callback_initialized_t initCallbackAfter, heaptrack_callback_t stopCallback);
index 8d6c8c3673c7e125a75d1914fde8b2ba236459d2..2ca5f6ffe0ee968c87e963641caec9d73a1ef5cd 100644 (file)
@@ -16,7 +16,7 @@ public:
     void *classId;
     bool visited;
 
-    void print(size_t gcCounter, FILE *out) {
+    void print(size_t gcCounter, outStream *out) {
         // To make things more compact, if the node was already visited, don't
         // traverse its children. It's enough to note that it is there.
         if (visited) {
@@ -63,7 +63,7 @@ public:
         keyIt->second.children.push_back(&(valIt->second));
     }
 
-    void print(size_t gcCounter, FILE* out) {
+    void print(size_t gcCounter, outStream* out) {
         auto it = m_graph.find(nullptr);
         if (it == m_graph.end()) {
             return;
diff --git a/src/track/outstream/outstream.cpp b/src/track/outstream/outstream.cpp
new file mode 100644 (file)
index 0000000..ebc4175
--- /dev/null
@@ -0,0 +1,47 @@
+#include <cstdarg>
+#include <cstdio>
+#include <memory>
+#include <cassert>
+
+#include "outstream.h"
+
+int fprintf(outStream *stream, const char* format, ...) noexcept
+{
+    assert(stream && format);
+
+    // heaptrack creates only one outStream instance and provide its own locking, we are safe here
+    static std::unique_ptr<char[]> Buf;
+    static size_t BufSize = 0;
+    int tmpStrSize = 0;
+    va_list argptr;
+
+    for (;;) {
+        va_start(argptr, format);
+        tmpStrSize = std::vsnprintf(Buf.get(), BufSize, format, argptr);
+        va_end(argptr);
+        if (tmpStrSize > -1
+            && static_cast<size_t>(tmpStrSize) < BufSize) {
+            break;
+        } else if (tmpStrSize > -1) {
+            size_t needBufSize = tmpStrSize + sizeof('\0');
+            Buf.reset(new (std::nothrow) char[needBufSize]);
+            if (!Buf.get()) {
+                BufSize = 0;
+                errno = ENOMEM;
+                return -1;
+            }
+            BufSize = needBufSize;
+        } else {
+            errno = EIO;
+            return -1;
+        }
+    }
+
+    int ret = stream->Puts(Buf.get());
+    if (ret > 0) {
+        // make proper return code, since it different from fputs()
+        ret = tmpStrSize;
+    }
+
+    return ret;
+}
diff --git a/src/track/outstream/outstream.h b/src/track/outstream/outstream.h
new file mode 100644 (file)
index 0000000..0d3084d
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef OUTSTREAM_H
+#define OUTSTREAM_H
+
+// Note, since heaptrack IO was designed in C-style, make sure,
+// that fprintf(), fputc() and fputs() don't throw exceptions
+// and return same value behavior as stdoi's fprintf(), fputc()
+// and fputs() respectively.
+
+// Make sure, that errno provide proper error code in Putc() and Puts(),
+// since heaptrack use it in writeError().
+
+class outStream {
+public:
+    outStream() = default;
+    virtual ~outStream() = default;
+    outStream(const outStream &) = delete;
+    outStream &operator = (const outStream &) = delete;
+
+    virtual int Putc(int Char) noexcept = 0;
+    virtual int Puts(const char *String) noexcept = 0;
+};
+
+template <class Implementation, class Initialization>
+outStream *OpenStream(Initialization InitValue) noexcept
+{
+    outStream *Result = nullptr;
+    try {
+        Result = new Implementation(InitValue);
+    } catch (...) {
+        Result = nullptr;
+    }
+    return Result;
+}
+
+int fprintf(outStream *stream, const char* format, ...) noexcept;
+
+inline int fputc(int ch, outStream *stream) noexcept
+{
+    return stream->Putc(ch);
+}
+
+inline int fputs(const char *str, outStream *stream) noexcept
+{
+    return stream->Puts(str);
+}
+
+#endif // OUTSTREAM_H
diff --git a/src/track/outstream/outstream_file.cpp b/src/track/outstream/outstream_file.cpp
new file mode 100644 (file)
index 0000000..7b32567
--- /dev/null
@@ -0,0 +1,60 @@
+#include <cassert>\r
+#include <stdexcept>\r
+#include <cstdio>\r
+#include <cstring>\r
+#include <stdio_ext.h>\r
+\r
+#include "outstream_file.h"\r
+\r
+outStreamFILE::outStreamFILE(const char *FileName) :\r
+    Stream_(nullptr),\r
+    Owner_(false)\r
+{\r
+    assert(FileName);\r
+\r
+    Stream_ = fopen(FileName, "w");\r
+    if (!Stream_) {\r
+        fprintf(stderr, "WARNING! Failed to open file %s: %s\n", FileName, strerror(errno));\r
+        throw std::runtime_error("Unable to open stream");\r
+    }\r
+\r
+    Owner_ = true;\r
+    // from heaptrack code: we do our own locking, this speeds up the writing significantly\r
+    __fsetlocking(Stream_, FSETLOCKING_BYCALLER);\r
+}\r
+\r
+outStreamFILE::outStreamFILE(FILE *FileStream) :\r
+    Stream_(nullptr),\r
+    Owner_(false)\r
+{\r
+    assert(FileStream);\r
+    Stream_ = FileStream;\r
+}\r
+\r
+outStreamFILE::~outStreamFILE()\r
+{\r
+    if (Owner_ && Stream_) {\r
+        fclose(Stream_);\r
+    }\r
+}\r
+\r
+int outStreamFILE::Putc(int Char) noexcept\r
+{\r
+    if (!Stream_) {\r
+        errno = EIO;\r
+        return -1;\r
+    }\r
+    return fputc(Char, Stream_);\r
+}\r
+\r
+int outStreamFILE::Puts(const char *String) noexcept\r
+{\r
+    if (!Stream_) {\r
+        errno = EIO;\r
+        return -1;\r
+    } else if (!String) {\r
+        errno = EINVAL;\r
+        return -1;\r
+    }\r
+    return fputs(String, Stream_);\r
+}\r
diff --git a/src/track/outstream/outstream_file.h b/src/track/outstream/outstream_file.h
new file mode 100644 (file)
index 0000000..b4167d2
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef OUTSTREAMFILE_H
+#define OUTSTREAMFILE_H
+
+#include "outstream.h"
+
+class outStreamFILE final : public outStream
+{
+public:
+    outStreamFILE() = delete;
+    explicit outStreamFILE(const char *FileName);
+    explicit outStreamFILE(FILE *FileStream);
+    ~outStreamFILE();
+
+    outStreamFILE(const outStreamFILE &) = delete;
+    outStreamFILE &operator = (const outStreamFILE &) = delete;
+
+    outStreamFILE(outStreamFILE &&other) :
+        Stream_(other.Stream_),
+        Owner_(other.Owner_)
+    {
+        other.Stream_ = nullptr;
+        other.Owner_ = false;
+    }
+    outStreamFILE &operator = (outStreamFILE &&other) {
+        Stream_ = other.Stream_;
+        Owner_ = other.Owner_;
+        other.Stream_ = nullptr;
+        other.Owner_ = false;
+        return *this;
+    }
+
+    int Putc(int Char) noexcept override;
+    int Puts(const char *String) noexcept override;
+
+private:
+    FILE *Stream_;
+    bool Owner_;
+};
+
+#endif // OUTSTREAMFILE_H
index 81e9df7391c17fd3b316c5dc9d61ec201c2984b8..752cc508bdc3a2d45939ce49ac737dd18b6531d0 100644 (file)
@@ -31,6 +31,7 @@
 #include <unordered_set>
 
 #include "trace.h"
+#include "outstream/outstream.h"
 
 #include "../profiler/src/stackentry.h"
 
@@ -68,7 +69,7 @@ public:
      *
      * Unknown instruction pointers will be printed to @p out.
      */
-    uint32_t index(const Trace& trace, FILE* out)
+    uint32_t index(const Trace& trace, outStream* out)
     {
         uint32_t index = 0;
         TraceEdge* parent = &m_root;