Bluetooth native dumpsys logging support (5/5)

Added 'btsnooz' debug tool to interpret BTSnoop traces from a bugreport

Bug: 18508263
Change-Id: I774c712300e805e7072c7a453e84b2a06eb67984
diff --git a/tools/btsnooz/Android.mk b/tools/btsnooz/Android.mk
new file mode 100644
index 0000000..5364b0f
--- /dev/null
+++ b/tools/btsnooz/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+supported_platforms := linux # For now... Needs to be tested on MacOS/Windows
+cur_platform := $(filter $(HOST_OS),$(supported_platforms))
+
+ifdef cur_platform
+
+# Host executable
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := btsnooz
+LOCAL_SRC_FILES := btsnooz.cpp btsnooz_utils.cpp
+LOCAL_C_INCLUDES := external/zlib system/bt
+LOCAL_CFLAGS += -std=c++11 -W -Wall
+LOCAL_STATIC_LIBRARIES := libz
+
+ifeq ($(HOST_OS),linux)
+  LOCAL_LDLIBS += -lresolv
+endif
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif #cur_platform
diff --git a/tools/btsnooz/btsnooz.cpp b/tools/btsnooz/btsnooz.cpp
new file mode 100644
index 0000000..567a0ea
--- /dev/null
+++ b/tools/btsnooz/btsnooz.cpp
@@ -0,0 +1,87 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2015 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "btsnooz_utils.h"
+
+int main(int argc, char *argv[]) {
+  if (argc > 3) {
+    std::cerr << "Usage: " << argv[0] << " [input_file] [output_file]\n";
+    return 1;
+  }
+
+  std::vector<char> buffer;
+
+  int read = 0;
+  if (argc < 3) {
+    std::cerr << "<Reading from stdin>\n";
+    read = readLog(std::cin, buffer);
+
+  } else {
+    std::cerr << "<Reading " << argv[1] << ">\n";
+    std::ifstream ff(argv[1]);
+    read = readLog(ff, buffer);
+    ff.close();
+  }
+
+  if (read == 0) {
+    std::cerr << "File not found or not BTSNOOP data block....\n";
+    return 2;
+  }
+
+  std::cerr << std::setw(8) << read << " bytes of base64 data read\n";
+
+  read = base64Decode(buffer);
+  if (read <= 0) {
+    std::cerr << "Decoding base64 data failed...\n";
+    return 3;
+  }
+
+  std::cerr << std::setw(8) << read << " bytes of compressed data decoded\n";
+
+  std::vector<uint8_t> uncompressed;
+  read = inflate(buffer, uncompressed);
+  if (read <= 0) {
+    std::cerr << "Error inflating data...\n";
+    return 4;
+  }
+
+  std::cerr << std::setw(8) << read << " bytes of data inflated\n";
+
+  if (argc < 2) {
+    std::cerr << "<Writing to stdout>\n";
+    read = writeBtSnoop(std::cout, uncompressed);
+
+  } else {
+    const int arg = argc > 2 ? 2 : 1;
+    std::cerr << "<Writing " << argv[arg] << ">\n";
+    std::ofstream ff(argv[arg]);
+    read = writeBtSnoop(ff, uncompressed);
+    ff.close();
+  }
+
+  std::cerr << std::setw(8) << read << " btsnoop packets written\n";
+
+  return 0;
+}
diff --git a/tools/btsnooz/btsnooz_utils.cpp b/tools/btsnooz/btsnooz_utils.cpp
new file mode 100644
index 0000000..b743997
--- /dev/null
+++ b/tools/btsnooz/btsnooz_utils.cpp
@@ -0,0 +1,244 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2015 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <iostream>
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <string.h> // for memcpy
+#include <vector>
+#include <resolv.h>
+#include <zlib.h>
+
+extern "C" {
+#include "btif/include/btif_debug_btsnoop.h"
+#include "hci/include/bt_hci_bdroid.h"
+#include "stack/include/bt_types.h"
+#include "stack/include/hcidefs.h"
+}
+
+// Epoch in microseconds since 01/01/0000.
+#define BTSNOOP_EPOCH_DELTA 0x00dcddb30f2f8000ULL
+
+#define INITIAL_BUFFER_SIZE 131072
+#define INFLATE_BUFFER  16384
+
+#define LOG_PREFIX  "--- BEGIN:BTSNOOP_LOG_SUMMARY"
+#define LOG_POSTFIX  "--- END:BTSNOOP_LOG_SUMMARY"
+
+#define H4_DIRECTION_SENT  0
+#define H4_DIRECTION_RECEIVED  1
+
+static uint8_t packetTypeToFlags(const uint8_t type) {
+  switch (type << 8) {
+    case MSG_HC_TO_STACK_HCI_ERR:
+    case MSG_HC_TO_STACK_HCI_ACL:
+    case MSG_HC_TO_STACK_HCI_SCO:
+    case MSG_HC_TO_STACK_HCI_EVT:
+    case MSG_HC_TO_STACK_L2C_SEG_XMIT:
+      return H4_DIRECTION_RECEIVED;
+
+    case MSG_STACK_TO_HC_HCI_ACL:
+    case MSG_STACK_TO_HC_HCI_SCO:
+    case MSG_STACK_TO_HC_HCI_CMD:
+      return H4_DIRECTION_SENT;
+
+    default:
+      break;
+  }
+  return 0;
+}
+
+static uint8_t packetTypeToHciType(const uint8_t type) {
+  switch (type << 8 & 0xFF00) {
+    case MSG_STACK_TO_HC_HCI_CMD:
+      return HCIT_TYPE_COMMAND;
+
+    case MSG_HC_TO_STACK_HCI_EVT:
+      return HCIT_TYPE_EVENT;
+
+    case MSG_STACK_TO_HC_HCI_ACL:
+    case MSG_HC_TO_STACK_HCI_ACL:
+      return HCIT_TYPE_ACL_DATA;
+
+    case MSG_STACK_TO_HC_HCI_SCO:
+    case MSG_HC_TO_STACK_HCI_SCO:
+      return HCIT_TYPE_SCO_DATA;
+
+    default:
+      break;
+  }
+  return 0;
+}
+
+size_t writeBtSnoop(std::ostream &out, std::vector<uint8_t> &in) {
+  if (in.size() < sizeof(btsnooz_preamble_t))
+    return 0;
+
+  // Get preamble
+
+  uint8_t *p = in.data();
+  btsnooz_preamble_t *preamble = reinterpret_cast<btsnooz_preamble_t*>(p);
+  if (preamble->version != BTSNOOZ_CURRENT_VERSION)
+    return 0;
+
+  // Write header
+
+  const uint8_t header[] = {
+    0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00,
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xea
+  };
+
+  out.write(reinterpret_cast<const char*>(header), sizeof(header));
+
+  // Calculate first timestamp
+
+  uint64_t first_ts = preamble->last_timestamp_ms + BTSNOOP_EPOCH_DELTA;
+  size_t left = in.size() - sizeof(btsnooz_preamble_t);
+  p = in.data() + sizeof(btsnooz_preamble_t);
+
+  while (left > sizeof(btsnooz_header_t)) {
+    btsnooz_header_t *p_hdr = reinterpret_cast<btsnooz_header_t*>(p);
+    p += sizeof(btsnooz_header_t) + (p_hdr->length - 1);
+    left -= sizeof(btsnooz_header_t) + (p_hdr->length - 1);
+
+    first_ts -= p_hdr->delta_time_ms;
+  }
+
+  // Process packets
+
+  size_t packets = 0;
+  left = in.size() - sizeof(btsnooz_preamble_t);
+  p = in.data() + sizeof(btsnooz_preamble_t);
+
+  while (left > sizeof(btsnooz_header_t)) {
+    btsnooz_header_t *p_hdr = reinterpret_cast<btsnooz_header_t*>(p);
+    p += sizeof(btsnooz_header_t);
+    left -= sizeof(btsnooz_header_t);
+
+    const uint32_t h_length = htonl(p_hdr->length);
+    out.write(reinterpret_cast<const char*>(&h_length), 4);
+    out.write(reinterpret_cast<const char*>(&h_length), 4);
+
+    const uint32_t h_flags = htonl(packetTypeToFlags(p_hdr->type));
+    out.write(reinterpret_cast<const char*>(&h_flags), 4);
+
+    const uint32_t h_dropped = 0;
+    out.write(reinterpret_cast<const char*>(&h_dropped), 4);
+
+    first_ts += p_hdr->delta_time_ms;
+    const uint32_t h_time_hi = htonl(first_ts >> 32);
+    const uint32_t h_time_lo = htonl(first_ts & 0xFFFFFFFF);
+    out.write(reinterpret_cast<const char*>(&h_time_hi), 4);
+    out.write(reinterpret_cast<const char*>(&h_time_lo), 4);
+
+    const uint8_t type = packetTypeToHciType(p_hdr->type);
+    out.write(reinterpret_cast<const char*>(&type), 1);
+
+    out.write(reinterpret_cast<const char*>(p), p_hdr->length - 1);
+
+    p += p_hdr->length - 1;
+    left -= p_hdr->length - 1;
+
+    ++packets;
+  }
+
+  return packets;
+}
+
+int readLog(std::istream &in, std::vector<char> &buffer) {
+  buffer.reserve(INITIAL_BUFFER_SIZE);
+
+  std::string line;
+
+  const std::string log_prefix(LOG_PREFIX);
+  const std::string log_postfix(LOG_POSTFIX);
+
+  bool in_block = false;
+
+  while (std::getline(in, line)) {
+    // Ensure line endings aren't wonky...
+
+    if (!line.empty() && line[line.size() - 1] == '\r')
+      line.erase(line.end() - 1);
+
+    // Detect block
+
+    if (!in_block) {
+      if (line.compare(0, log_prefix.length(), log_prefix) == 0)
+        in_block = true;
+      continue;
+    }
+
+    if (line.compare(0, log_postfix.length(), log_postfix) == 0)
+      break;
+
+    // Process data
+
+    buffer.insert(buffer.end(), line.begin(), line.end());
+  }
+
+  if (buffer.size() != 0)
+    buffer.push_back(0);
+
+  return buffer.size();
+}
+
+int base64Decode(std::vector<char> &buffer) {
+  char *p = buffer.data();
+  return b64_pton(p, reinterpret_cast<uint8_t*>(p), buffer.size());
+}
+
+int inflate(std::vector<char> &in, std::vector<uint8_t> &out) {
+  out.reserve(in.size());
+
+  uint8_t buffer[INFLATE_BUFFER];
+  z_stream zs;
+
+  int ret = inflateInit(&zs);
+  if (Z_OK != ret)
+    return -1;
+
+  // Copy preamble as-is
+
+  for (size_t i = 0; i != sizeof(btsnooz_preamble_t); ++i) {
+    out.push_back(in[i]);
+  }
+
+  // De-compress data
+
+  zs.avail_in = in.size() - sizeof(btsnooz_preamble_t);;
+  zs.next_in = reinterpret_cast<uint8_t*>(in.data()) + sizeof(btsnooz_preamble_t);
+
+  do {
+    zs.avail_out = INFLATE_BUFFER;
+    zs.next_out = buffer;
+
+    ret = inflate(&zs, Z_NO_FLUSH);
+
+    size_t read = INFLATE_BUFFER - zs.avail_out;
+    uint8_t *p = buffer;
+    while (read--)
+      out.push_back(*p++);
+  } while (zs.avail_out == 0);
+
+  inflateEnd(&zs);
+
+  return out.size();
+}
diff --git a/tools/btsnooz/btsnooz_utils.h b/tools/btsnooz/btsnooz_utils.h
new file mode 100644
index 0000000..94ada83
--- /dev/null
+++ b/tools/btsnooz/btsnooz_utils.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2015 Google, Inc.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <iostream>
+#include <vector>
+
+size_t writeBtSnoop(std::ostream &out, std::vector<uint8_t> &in);
+int readLog(std::istream &in, std::vector<char> &buffer);
+int base64Decode(std::vector<char> &buffer);
+int inflate(std::vector<char> &in, std::vector<uint8_t> &out);