ART: Fix Trace types, check minimum buf size

Also make streaming mode adhere to the given buffer (and fix the
case where the buffer is too small for a packet). This is important
to not lose too much tracing information when the runtime is destroyed
with an unflushed buffer.

Change-Id: I6525fe4326ac5c3d7c9cda41c54a2a911ca889b7
diff --git a/runtime/base/casts.h b/runtime/base/casts.h
index c7e39a2..f884649 100644
--- a/runtime/base/casts.h
+++ b/runtime/base/casts.h
@@ -18,9 +18,11 @@
 #define ART_RUNTIME_BASE_CASTS_H_
 
 #include <assert.h>
+#include <limits>
 #include <string.h>
 #include <type_traits>
 
+#include "base/logging.h"
 #include "base/macros.h"
 
 namespace art {
@@ -83,6 +85,23 @@
   return dest;
 }
 
+// A version of static_cast that DCHECKs that the value can be precisely represented
+// when converting to Dest.
+template <typename Dest, typename Source>
+inline Dest dchecked_integral_cast(const Source source) {
+  DCHECK(
+      // Check that the value is within the lower limit of Dest.
+      (static_cast<intmax_t>(std::numeric_limits<Dest>::min()) <=
+          static_cast<intmax_t>(std::numeric_limits<Source>::min()) ||
+          source >= static_cast<Source>(std::numeric_limits<Dest>::min())) &&
+      // Check that the value is within the upper limit of Dest.
+      (static_cast<uintmax_t>(std::numeric_limits<Dest>::max()) >=
+          static_cast<uintmax_t>(std::numeric_limits<Source>::max()) ||
+          source <= static_cast<Source>(std::numeric_limits<Dest>::max())));
+
+  return static_cast<Dest>(source);
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_BASE_CASTS_H_
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 8524348..1a7a3e5 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -152,6 +152,7 @@
   if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) {
     Trace::TraceOutputMode output_mode = Trace::GetOutputMode();
     Trace::TraceMode trace_mode = Trace::GetMode();
+    size_t buffer_size = Trace::GetBufferSize();
 
     // Just drop it.
     Trace::Abort();
@@ -176,7 +177,7 @@
                                               proc_name.c_str());
         Trace::Start(trace_file.c_str(),
                      -1,
-                     -1,  // TODO: Expose buffer size.
+                     buffer_size,
                      0,   // TODO: Expose flags.
                      output_mode,
                      trace_mode,
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 5322f9f..9eca517 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -22,6 +22,7 @@
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 #include "cutils/trace.h"
 
+#include "base/casts.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
@@ -329,7 +330,7 @@
   return nullptr;
 }
 
-void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int flags,
+void Trace::Start(const char* trace_filename, int trace_fd, size_t buffer_size, int flags,
                   TraceOutputMode output_mode, TraceMode trace_mode, int interval_us) {
   Thread* self = Thread::Current();
   {
@@ -592,19 +593,15 @@
   }
 }
 
-static constexpr size_t kStreamingBufferSize = 16 * KB;
+static constexpr size_t kMinBufSize = 18U;  // Trace header is up to 18B.
 
-Trace::Trace(File* trace_file, const char* trace_name, int buffer_size, int flags,
+Trace::Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags,
              TraceOutputMode output_mode, TraceMode trace_mode)
     : trace_file_(trace_file),
-      buf_(new uint8_t[output_mode == TraceOutputMode::kStreaming ?
-          kStreamingBufferSize :
-          buffer_size]()),
+      buf_(new uint8_t[std::max(kMinBufSize, buffer_size)]()),
       flags_(flags), trace_output_mode_(output_mode), trace_mode_(trace_mode),
       clock_source_(default_clock_source_),
-      buffer_size_(output_mode == TraceOutputMode::kStreaming ?
-          kStreamingBufferSize :
-          buffer_size),
+      buffer_size_(std::max(kMinBufSize, buffer_size)),
       start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()), cur_offset_(0),
       overflow_(false), interval_us_(0), streaming_lock_(nullptr) {
   uint16_t trace_version = GetTraceVersion(clock_source_);
@@ -621,6 +618,7 @@
     uint16_t record_size = GetRecordSize(clock_source_);
     Append2LE(buf_.get() + 16, record_size);
   }
+  static_assert(18 <= kMinBufSize, "Minimum buffer size not large enough for trace header");
 
   // Update current offset.
   cur_offset_.StoreRelaxed(kTraceHeaderLength);
@@ -875,11 +873,21 @@
 void Trace::WriteToBuf(const uint8_t* src, size_t src_size) {
   int32_t old_offset = cur_offset_.LoadRelaxed();
   int32_t new_offset = old_offset + static_cast<int32_t>(src_size);
-  if (new_offset > buffer_size_) {
+  if (dchecked_integral_cast<size_t>(new_offset) > buffer_size_) {
     // Flush buffer.
     if (!trace_file_->WriteFully(buf_.get(), old_offset)) {
       PLOG(WARNING) << "Failed streaming a tracing event.";
     }
+
+    // Check whether the data is too large for the buffer, then write immediately.
+    if (src_size >= buffer_size_) {
+      if (!trace_file_->WriteFully(src, src_size)) {
+        PLOG(WARNING) << "Failed streaming a tracing event.";
+      }
+      cur_offset_.StoreRelease(0);  // Buffer is empty now.
+      return;
+    }
+
     old_offset = 0;
     new_offset = static_cast<int32_t>(src_size);
   }
@@ -900,7 +908,7 @@
     do {
       old_offset = cur_offset_.LoadRelaxed();
       new_offset = old_offset + GetRecordSize(clock_source_);
-      if (new_offset > buffer_size_) {
+      if (static_cast<size_t>(new_offset) > buffer_size_) {
         overflow_ = true;
         return;
       }
@@ -1034,4 +1042,10 @@
   return the_trace_->trace_mode_;
 }
 
+size_t Trace::GetBufferSize() {
+  MutexLock mu(Thread::Current(), *Locks::trace_lock_);
+  CHECK(the_trace_ != nullptr) << "Trace mode requested, but no trace currently running";
+  return the_trace_->buffer_size_;
+}
+
 }  // namespace art
diff --git a/runtime/trace.h b/runtime/trace.h
index 1ecd4d8..06824b8 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -72,7 +72,7 @@
 
   static void SetDefaultClockSource(TraceClockSource clock_source);
 
-  static void Start(const char* trace_filename, int trace_fd, int buffer_size, int flags,
+  static void Start(const char* trace_filename, int trace_fd, size_t buffer_size, int flags,
                     TraceOutputMode output_mode, TraceMode trace_mode, int interval_us)
       LOCKS_EXCLUDED(Locks::mutator_lock_,
                      Locks::thread_list_lock_,
@@ -136,9 +136,10 @@
 
   static TraceOutputMode GetOutputMode() LOCKS_EXCLUDED(Locks::trace_lock_);
   static TraceMode GetMode() LOCKS_EXCLUDED(Locks::trace_lock_);
+  static size_t GetBufferSize() LOCKS_EXCLUDED(Locks::trace_lock_);
 
  private:
-  Trace(File* trace_file, const char* trace_name, int buffer_size, int flags,
+  Trace(File* trace_file, const char* trace_name, size_t buffer_size, int flags,
         TraceOutputMode output_mode, TraceMode trace_mode);
 
   // The sampling interval in microseconds is passed as an argument.
@@ -202,7 +203,7 @@
   const TraceClockSource clock_source_;
 
   // Size of buf_.
-  const int buffer_size_;
+  const size_t buffer_size_;
 
   // Time trace was created.
   const uint64_t start_time_;