drm_hwcomposer: reimplement Dump for DrmDisplayCompositor

Also fixes hwc_dump sometimes failing to null terminate its output buffer.

TEST=dumpsys SurfaceFlinger

Change-Id: Ibf93cfd496a07a9375d78a8b239c2c7876aff986
diff --git a/drm_hwcomposer.h b/drm_hwcomposer.h
index 1490438..66c2c81 100644
--- a/drm_hwcomposer.h
+++ b/drm_hwcomposer.h
@@ -171,7 +171,7 @@
     return *this;
   }
 
-  operator bool() {
+  operator bool() const {
     return importer_ != NULL;
   }
 
diff --git a/drmdisplaycomposition.cpp b/drmdisplaycomposition.cpp
index b374634..49bacad 100644
--- a/drmdisplaycomposition.cpp
+++ b/drmdisplaycomposition.cpp
@@ -328,4 +328,170 @@
 
   return 0;
 }
+
+static const char *DrmCompositionTypeToString(DrmCompositionType type) {
+  switch (type) {
+    case DRM_COMPOSITION_TYPE_EMPTY:
+      return "EMPTY";
+    case DRM_COMPOSITION_TYPE_FRAME:
+      return "FRAME";
+    case DRM_COMPOSITION_TYPE_DPMS:
+      return "DPMS";
+    case DRM_COMPOSITION_TYPE_MODESET:
+      return "MODESET";
+    default:
+      return "<invalid>";
+  }
+}
+
+static const char *DPMSModeToString(int dpms_mode) {
+  switch (dpms_mode) {
+    case DRM_MODE_DPMS_ON:
+      return "ON";
+    case DRM_MODE_DPMS_OFF:
+      return "OFF";
+    default:
+      return "<invalid>";
+  }
+}
+
+static void DumpBuffer(const DrmHwcBuffer &buffer, std::ostringstream *out) {
+  if (!buffer) {
+    *out << "buffer=<invalid>";
+    return;
+  }
+
+  *out << "buffer[w/h/format]=";
+  *out << buffer->width << "/" << buffer->height << "/" << buffer->format;
+}
+
+static const char *TransformToString(DrmHwcTransform transform) {
+  switch (transform) {
+    case DrmHwcTransform::kIdentity:
+      return "IDENTITY";
+    case DrmHwcTransform::kFlipH:
+      return "FLIPH";
+    case DrmHwcTransform::kFlipV:
+      return "FLIPV";
+    case DrmHwcTransform::kRotate90:
+      return "ROTATE90";
+    case DrmHwcTransform::kRotate180:
+      return "ROTATE180";
+    case DrmHwcTransform::kRotate270:
+      return "ROTATE270";
+    default:
+      return "<invalid>";
+  }
+}
+
+static const char *BlendingToString(DrmHwcBlending blending) {
+  switch (blending) {
+    case DrmHwcBlending::kNone:
+      return "NONE";
+    case DrmHwcBlending::kPreMult:
+      return "PREMULT";
+    case DrmHwcBlending::kCoverage:
+      return "COVERAGE";
+    default:
+      return "<invalid>";
+  }
+}
+
+static void DumpRegion(const DrmCompositionRegion &region,
+                       std::ostringstream *out) {
+  *out << "frame";
+  region.frame.Dump(out);
+  *out << " source_layers=(";
+
+  const std::vector<size_t> &source_layers = region.source_layers;
+  for (size_t i = 0; i < source_layers.size(); i++) {
+    *out << source_layers[i];
+    if (i < source_layers.size() - 1) {
+      *out << " ";
+    }
+  }
+
+  *out << ")";
+}
+
+void DrmDisplayComposition::Dump(std::ostringstream *out) const {
+  *out << "----DrmDisplayComposition"
+       << " crtc=" << (crtc_ ? crtc_->id() : -1)
+       << " type=" << DrmCompositionTypeToString(type_);
+
+  switch (type_) {
+    case DRM_COMPOSITION_TYPE_DPMS:
+      *out << " dpms_mode=" << DPMSModeToString(dpms_mode_);
+      break;
+    case DRM_COMPOSITION_TYPE_MODESET:
+      *out << " display_mode=" << display_mode_.h_display() << "x"
+           << display_mode_.v_display();
+      break;
+    default:
+      break;
+  }
+
+  *out << " timeline[current/squash/pre-comp/done]=" << timeline_current_ << "/"
+       << timeline_squash_done_ << "/" << timeline_pre_comp_done_ << "/"
+       << timeline_ << "\n";
+
+  *out << "    Layers: count=" << layers_.size() << "\n";
+  for (size_t i = 0; i < layers_.size(); i++) {
+    const DrmHwcLayer &layer = layers_[i];
+    *out << "      [" << i << "] ";
+
+    DumpBuffer(layer.buffer, out);
+
+    *out << " transform=" << TransformToString(layer.transform)
+         << " blending[a=" << (int)layer.alpha
+         << "]=" << BlendingToString(layer.blending) << " source_crop";
+    layer.source_crop.Dump(out);
+    *out << " display_frame";
+    layer.display_frame.Dump(out);
+
+    *out << "\n";
+  }
+
+  *out << "    Planes: count=" << composition_planes_.size() << "\n";
+  for (size_t i = 0; i < composition_planes_.size(); i++) {
+    const DrmCompositionPlane &comp_plane = composition_planes_[i];
+    *out << "      [" << i << "]"
+         << " plane=" << (comp_plane.plane ? comp_plane.plane->id() : -1)
+         << " source_layer=";
+    if (comp_plane.source_layer <= DrmCompositionPlane::kSourceLayerMax) {
+      *out << comp_plane.source_layer;
+    } else {
+      switch (comp_plane.source_layer) {
+        case DrmCompositionPlane::kSourceNone:
+          *out << "NONE";
+          break;
+        case DrmCompositionPlane::kSourcePreComp:
+          *out << "PRECOMP";
+          break;
+        case DrmCompositionPlane::kSourceSquash:
+          *out << "SQUASH";
+          break;
+        default:
+          *out << "<invalid>";
+          break;
+      }
+    }
+
+    *out << "\n";
+  }
+
+  *out << "    Squash Regions: count=" << squash_regions_.size() << "\n";
+  for (size_t i = 0; i < squash_regions_.size(); i++) {
+    *out << "      [" << i << "] ";
+    DumpRegion(squash_regions_[i], out);
+    *out << "\n";
+  }
+
+  *out << "    Pre-Comp Regions: count=" << pre_comp_regions_.size() << "\n";
+  for (size_t i = 0; i < pre_comp_regions_.size(); i++) {
+    *out << "      [" << i << "] ";
+    DumpRegion(pre_comp_regions_[i], out);
+    *out << "\n";
+  }
+}
 }
diff --git a/drmdisplaycomposition.h b/drmdisplaycomposition.h
index 814ca24..2ac93e0 100644
--- a/drmdisplaycomposition.h
+++ b/drmdisplaycomposition.h
@@ -23,6 +23,7 @@
 #include "glworker.h"
 #include "importer.h"
 
+#include <sstream>
 #include <vector>
 
 #include <hardware/gralloc.h>
@@ -123,6 +124,8 @@
     return importer_;
   }
 
+  void Dump(std::ostringstream *out) const;
+
  private:
   bool validate_composition_type(DrmCompositionType desired);
 
diff --git a/drmdisplaycompositor.cpp b/drmdisplaycompositor.cpp
index 9f349c0..1d22564 100644
--- a/drmdisplaycompositor.cpp
+++ b/drmdisplaycompositor.cpp
@@ -109,6 +109,34 @@
   }
 }
 
+void SquashState::Dump(std::ostringstream *out) const {
+  *out << "----SquashState generation=" << generation_number_
+       << " history=" << valid_history_ << "\n"
+       << "    Regions: count=" << regions_.size() << "\n";
+  for (size_t i = 0; i < regions_.size(); i++) {
+    const Region &region = regions_[i];
+    *out << "      [" << i << "]"
+         << " history=" << region.change_history << " rect";
+    region.rect.Dump(out);
+    *out << " layers=(";
+    bool first = true;
+    for (size_t layer_index = 0; layer_index < kMaxLayers; layer_index++) {
+      if ((region.layer_refs &
+           std::bitset<kMaxLayers>((size_t)1 << layer_index))
+              .any()) {
+        if (!first)
+          *out << " ";
+        first = false;
+        *out << layer_index;
+      }
+    }
+    *out << ")";
+    if (region.squashed)
+      *out << " squashed";
+    *out << "\n";
+  }
+}
+
 DrmDisplayCompositor::DrmDisplayCompositor()
     : drm_(NULL),
       display_(-1),
@@ -712,5 +740,33 @@
 }
 
 void DrmDisplayCompositor::Dump(std::ostringstream *out) const {
+  int ret = pthread_mutex_lock(&lock_);
+  if (ret)
+    return;
+
+  uint64_t num_frames = dump_frames_composited_;
+  dump_frames_composited_ = 0;
+
+  struct timespec ts;
+  ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+  if (ret) {
+    pthread_mutex_unlock(&lock_);
+    return;
+  }
+
+  uint64_t cur_ts = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec;
+  uint64_t num_ms = (cur_ts - dump_last_timestamp_ns_) / (1000 * 1000);
+  float fps = num_ms ? (num_frames * 1000.0f) / (num_ms) : 0.0f;
+
+  *out << "--DrmDisplayCompositor[" << display_
+       << "]: num_frames=" << num_frames << " num_ms=" << num_ms
+       << " fps=" << fps << "\n";
+
+  dump_last_timestamp_ns_ = cur_ts;
+
+  if (active_composition_)
+    active_composition_->Dump(out);
+
+  pthread_mutex_unlock(&lock_);
 }
 }
diff --git a/drmdisplaycompositor.h b/drmdisplaycompositor.h
index 22e1efc..cacaa66 100644
--- a/drmdisplaycompositor.h
+++ b/drmdisplaycompositor.h
@@ -69,6 +69,8 @@
                      const std::vector<bool> &changed_regions);
   void RecordSquashed(const std::vector<bool> &squashed_regions);
 
+  void Dump(std::ostringstream *out) const;
+
  private:
   size_t generation_number_ = 0;
   unsigned valid_history_ = 0;
diff --git a/hwcomposer.cpp b/hwcomposer.cpp
index 6a0f4cd..ac7fdac 100644
--- a/hwcomposer.cpp
+++ b/hwcomposer.cpp
@@ -330,7 +330,9 @@
 
   ctx->drm.compositor()->Dump(&out);
   std::string out_str = out.str();
-  strncpy(buff, out_str.c_str(), std::min((size_t)buff_len, out_str.length()));
+  strncpy(buff, out_str.c_str(),
+          std::min((size_t)buff_len, out_str.length() + 1));
+  buff[buff_len - 1] = '\0';
 }
 
 static int hwc_prepare(hwc_composer_device_1_t *dev, size_t num_displays,
diff --git a/seperate_rects.h b/seperate_rects.h
index 1e0a267..0703c97 100644
--- a/seperate_rects.h
+++ b/seperate_rects.h
@@ -18,6 +18,8 @@
 #define DRM_HWCOMPOSER_SEPERATE_RECTS_H_
 
 #include <stdint.h>
+
+#include <sstream>
 #include <vector>
 
 namespace seperate_rects {
@@ -76,6 +78,11 @@
   TFloat area() const {
     return width() * height();
   }
+
+  void Dump(std::ostringstream *out) const {
+    *out << "[x/y/w/h]=" << left << "/" << top << "/" << width() << "/"
+         << height();
+  }
 };
 
 template <typename TUInt>