summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndre Eisenbach <eisenbach@google.com>2014-12-05 09:40:20 -0800
committerAndre Eisenbach <eisenbach@google.com>2015-05-11 17:57:11 -0700
commit89f5e411d9ef31436741288a2267e46dd744e273 (patch)
treef701404c9c1db42b66b2c4f1b57e402994a7b899
parenta778a64c053acdb99b7a14b5837b4f12d83009a1 (diff)
downloadandroid_system_bt-89f5e411d9ef31436741288a2267e46dd744e273.tar.gz
android_system_bt-89f5e411d9ef31436741288a2267e46dd744e273.tar.bz2
android_system_bt-89f5e411d9ef31436741288a2267e46dd744e273.zip
Bluetooth native dumpsys logging support (2/5)
Includes support for BTSnoop logging in memory. Bug: 18508263 Change-Id: I175da528cbcdc00d40622647d518a74210cfe6fd
-rwxr-xr-xbta/gatt/bta_gattc_act.c9
-rw-r--r--bta/gatt/bta_gatts_act.c8
-rw-r--r--btif/include/btif_debug.h31
-rw-r--r--btif/include/btif_debug_btsnoop.h43
-rw-r--r--btif/include/btif_debug_conn.h34
-rw-r--r--btif/src/bluetooth.c9
-rw-r--r--btif/src/btif_debug.c47
-rw-r--r--btif/src/btif_debug_btsnoop.c151
-rw-r--r--btif/src/btif_debug_conn.c108
-rw-r--r--hci/Android.mk1
-rw-r--r--hci/include/btsnoop_mem.h36
-rw-r--r--hci/src/btsnoop.c5
-rw-r--r--hci/src/btsnoop_mem.c61
-rw-r--r--include/bt_target.h5
-rw-r--r--main/Android.mk7
-rw-r--r--osi/Android.mk4
-rw-r--r--osi/include/ringbuffer.h60
-rw-r--r--osi/src/ringbuffer.c125
-rw-r--r--osi/test/ringbuffer_test.cpp139
19 files changed, 880 insertions, 3 deletions
diff --git a/bta/gatt/bta_gattc_act.c b/bta/gatt/bta_gattc_act.c
index aa9fcae2f..c5f7f60ae 100755
--- a/bta/gatt/bta_gattc_act.c
+++ b/bta/gatt/bta_gattc_act.c
@@ -37,6 +37,8 @@
#include "bta_hh_int.h"
#endif
+#include "btif/include/btif_debug_conn.h"
+
#include <string.h>
#include "osi/include/log.h"
@@ -1803,6 +1805,13 @@ static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id,
__FUNCTION__, gattc_if, connected, conn_id, reason);
}
+ bt_bdaddr_t bdaddr;
+ bdcpy(bdaddr.address, bda);
+ if (connected)
+ btif_debug_conn_state(bdaddr, BTIF_DEBUG_CONNECTED, GATT_CONN_UNKNOWN);
+ else
+ btif_debug_conn_state(bdaddr, BTIF_DEBUG_DISCONNECTED, reason);
+
if ((p_buf = (tBTA_GATTC_DATA *) GKI_getbuf(sizeof(tBTA_GATTC_DATA))) != NULL)
{
memset(p_buf, 0, sizeof(tBTA_GATTC_DATA));
diff --git a/bta/gatt/bta_gatts_act.c b/bta/gatt/bta_gatts_act.c
index bdd7f2919..faabde356 100644
--- a/bta/gatt/bta_gatts_act.c
+++ b/bta/gatt/bta_gatts_act.c
@@ -34,6 +34,7 @@
#include "bta_gatts_int.h"
#include "bta_gatts_co.h"
#include "btm_ble_api.h"
+#include "btif/include/btif_debug_conn.h"
#include <string.h>
static void bta_gatts_nv_save_cback(BOOLEAN is_saved, tGATTS_HNDL_RANGE *p_hndl_range);
@@ -923,6 +924,13 @@ static void bta_gatts_conn_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id,
APPL_TRACE_DEBUG("bta_gatts_conn_cback bda :%02x-%02x-%02x-%02x-%02x-%02x ",
bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
+ bt_bdaddr_t bdaddr;
+ bdcpy(bdaddr.address, bda);
+ if (connected)
+ btif_debug_conn_state(bdaddr, BTIF_DEBUG_CONNECTED, GATT_CONN_UNKNOWN);
+ else
+ btif_debug_conn_state(bdaddr, BTIF_DEBUG_DISCONNECTED, reason);
+
p_reg = bta_gatts_find_app_rcb_by_app_if(gatt_if);
if (p_reg && p_reg->p_cback)
diff --git a/btif/include/btif_debug.h b/btif/include/btif_debug.h
new file mode 100644
index 000000000..d0fa0c76f
--- /dev/null
+++ b/btif/include/btif_debug.h
@@ -0,0 +1,31 @@
+/******************************************************************************
+ *
+ * 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 <stdint.h>
+
+// Debug API
+
+void btif_debug_init(void);
+void btif_debug_dump(int fd);
+
+// Debug helpers
+
+// Timestamp in us
+uint64_t btif_debug_ts(void);
diff --git a/btif/include/btif_debug_btsnoop.h b/btif/include/btif_debug_btsnoop.h
new file mode 100644
index 000000000..8c8ef0fa9
--- /dev/null
+++ b/btif/include/btif_debug_btsnoop.h
@@ -0,0 +1,43 @@
+/******************************************************************************
+ *
+ * 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 <stdint.h>
+
+#define BTSNOOZ_CURRENT_VERSION 0x01
+
+// The preamble is stored un-encrypted as the first part
+// of the file.
+typedef struct btsnooz_preamble_t {
+ uint8_t version;
+ uint64_t last_timestamp_ms;
+} __attribute__((__packed__)) btsnooz_preamble_t;
+
+// One header for each HCI packet
+typedef struct btsnooz_header_t {
+ uint16_t length;
+ uint32_t delta_time_ms;
+ uint8_t type;
+} __attribute__((__packed__)) btsnooz_header_t;
+
+// Initializes btsnoop memory logging and registers
+void btif_debug_btsnoop_init(void);
+
+// Writes btsnoop data base64 encoded to fd
+void btif_debug_btsnoop_dump(int fd);
diff --git a/btif/include/btif_debug_conn.h b/btif/include/btif_debug_conn.h
new file mode 100644
index 000000000..bf1bd6aa5
--- /dev/null
+++ b/btif/include/btif_debug_conn.h
@@ -0,0 +1,34 @@
+/******************************************************************************
+ *
+ * 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 <hardware/bluetooth.h>
+
+#include "gatt_api.h"
+
+typedef enum {
+ BTIF_DEBUG_CONNECTED = 1,
+ BTIF_DEBUG_DISCONNECTED
+} btif_debug_conn_state_t;
+
+// Report a connection state change
+void btif_debug_conn_state(const bt_bdaddr_t bda, const btif_debug_conn_state_t state,
+ const tGATT_DISCONN_REASON disconnect_reason);
+
+void btif_debug_conn_dump(int fd);
diff --git a/btif/src/bluetooth.c b/btif/src/bluetooth.c
index 4558705d4..f842e5ca3 100644
--- a/btif/src/bluetooth.c
+++ b/btif/src/bluetooth.c
@@ -46,7 +46,9 @@
#define LOG_TAG "bt_bluedroid"
#include "btif_api.h"
+#include "btif_debug.h"
#include "btsnoop.h"
+#include "btsnoop_mem.h"
#include "bt_utils.h"
#include "osi/include/osi.h"
#include "osi/include/allocation_tracker.h"
@@ -128,6 +130,7 @@ static int init(bt_callbacks_t *callbacks) {
bt_hal_cbacks = callbacks;
stack_manager_get_interface()->init_stack();
+ btif_debug_init();
return BT_STATUS_SUCCESS;
}
@@ -307,6 +310,11 @@ static int read_energy_info()
return BT_STATUS_SUCCESS;
}
+static void dump(int fd)
+{
+ btif_debug_dump(fd);
+}
+
static const void* get_profile_interface (const char *profile_id)
{
LOG_INFO("get_profile_interface %s", profile_id);
@@ -441,6 +449,7 @@ static const bt_interface_t bluetoothInterface = {
config_hci_snoop_log,
set_os_callouts,
read_energy_info,
+ dump
};
const bt_interface_t* bluetooth__get_bluetooth_interface ()
diff --git a/btif/src/btif_debug.c b/btif/src/btif_debug.c
new file mode 100644
index 000000000..bd7a84479
--- /dev/null
+++ b/btif/src/btif_debug.c
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ * 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 <unistd.h>
+#include <sys/time.h>
+
+#include "btif/include/btif_debug.h"
+#include "btif/include/btif_debug_btsnoop.h"
+#include "btif/include/btif_debug_conn.h"
+#include "include/bt_target.h"
+
+void btif_debug_init(void) {
+#if defined(BTSNOOP_MEM) && (BTSNOOP_MEM == TRUE)
+ btif_debug_btsnoop_init();
+#endif
+}
+
+void btif_debug_dump(int fd) {
+ btif_debug_conn_dump(fd);
+#if defined(BTSNOOP_MEM) && (BTSNOOP_MEM == TRUE)
+ btif_debug_btsnoop_dump(fd);
+#endif
+
+ close(fd);
+}
+
+// TODO: Find a better place for this to enable additional re-use
+uint64_t btif_debug_ts(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000000LL) + tv.tv_usec;
+}
diff --git a/btif/src/btif_debug_btsnoop.c b/btif/src/btif_debug_btsnoop.c
new file mode 100644
index 000000000..2271b7514
--- /dev/null
+++ b/btif/src/btif_debug_btsnoop.c
@@ -0,0 +1,151 @@
+/******************************************************************************
+ *
+ * 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 <assert.h>
+#include <resolv.h>
+#include <zlib.h>
+
+#include "btif/include/btif_debug.h"
+#include "btif/include/btif_debug_btsnoop.h"
+#include "hci/include/btsnoop_mem.h"
+#include "include/bt_target.h"
+#include "osi/include/ringbuffer.h"
+
+#define REDUCE_HCI_TYPE_TO_SIGNIFICANT_BITS(type) (type >> 8)
+
+// Total btsnoop memory log buffer size
+#ifndef BTSNOOP_MEM_BUFFER_SIZE
+static const size_t BTSNOOP_MEM_BUFFER_SIZE = (128 * 1024);
+#endif
+
+// Block size for copying buffers (for compression/encoding etc.)
+static const size_t BLOCK_SIZE = 16384;
+
+// Maximum line length in bugreport (should be multiple of 4 for base64 output)
+static const uint8_t MAX_LINE_LENGTH = 128;
+
+static ringbuffer_t *buffer = NULL;
+static uint64_t last_timestamp_ms = 0;
+
+static void btsnoop_cb(const uint16_t type, const uint8_t *data, const size_t length) {
+ btsnooz_header_t header;
+
+ // Make room in the ring buffer
+
+ while (ringbuffer_available(buffer) < (length + sizeof(btsnooz_header_t))) {
+ ringbuffer_pop(buffer, (uint8_t *)&header, sizeof(btsnooz_header_t));
+ ringbuffer_delete(buffer, header.length - 1);
+ }
+
+ // Insert data
+
+ const uint64_t now = btif_debug_ts();
+
+ header.type = REDUCE_HCI_TYPE_TO_SIGNIFICANT_BITS(type);
+ header.length = length;
+ header.delta_time_ms = last_timestamp_ms ? now - last_timestamp_ms : 0;
+ last_timestamp_ms = now;
+
+ ringbuffer_insert(buffer, (uint8_t *)&header, sizeof(btsnooz_header_t));
+ ringbuffer_insert(buffer, data, length - 1);
+}
+
+static bool btsnoop_compress(ringbuffer_t *rb_dst, ringbuffer_t *rb_src) {
+ assert(rb_dst != NULL);
+ assert(rb_src != NULL);
+
+ z_stream zs = {0};
+ if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
+ return false;
+
+ bool rc = true;
+ uint8_t block_src[BLOCK_SIZE];
+ uint8_t block_dst[BLOCK_SIZE];
+
+ while (ringbuffer_size(rb_src) > 0) {
+ zs.avail_in = ringbuffer_pop(rb_src, block_src, BLOCK_SIZE);
+ zs.next_in = block_src;
+
+ do {
+ zs.avail_out = BLOCK_SIZE;
+ zs.next_out = block_dst;
+
+ int err = deflate(&zs, ringbuffer_size(rb_src) == 0 ? Z_FINISH : Z_NO_FLUSH);
+ if (err == Z_STREAM_ERROR) {
+ rc = false;
+ break;
+ }
+
+ const size_t length = BLOCK_SIZE - zs.avail_out;
+ ringbuffer_insert(rb_dst, block_dst, length);
+ } while (zs.avail_out == 0);
+ }
+
+ deflateEnd(&zs);
+ return rc;
+}
+
+void btif_debug_btsnoop_init(void) {
+ if (buffer == NULL)
+ buffer = ringbuffer_init(BTSNOOP_MEM_BUFFER_SIZE);
+ btsnoop_mem_set_callback(btsnoop_cb);
+}
+
+void btif_debug_btsnoop_dump(int fd) {
+ dprintf(fd, "\n--- BEGIN:BTSNOOP_LOG_SUMMARY (%zu bytes in) ---\n", ringbuffer_size(buffer));
+
+ ringbuffer_t *ringbuffer = ringbuffer_init(BTSNOOP_MEM_BUFFER_SIZE);
+ if (ringbuffer == NULL) {
+ dprintf(fd, "%s Unable to allocate memory for compression", __func__);
+ return;
+ }
+
+ // Prepend preamble
+
+ btsnooz_preamble_t preamble;
+ preamble.version = BTSNOOZ_CURRENT_VERSION;
+ preamble.last_timestamp_ms = last_timestamp_ms;
+ ringbuffer_insert(ringbuffer, (uint8_t *)&preamble, sizeof(btsnooz_preamble_t));
+
+ // Compress data
+
+ bool rc = btsnoop_compress(ringbuffer, buffer);
+ if (rc == false) {
+ dprintf(fd, "%s Log compression failed", __func__);
+ goto error;
+ }
+
+ // Base64 encode & output
+
+ uint8_t b64_in[3] = {0};
+ char b64_out[5] = {0};
+
+ size_t i = sizeof(btsnooz_preamble_t);
+ while (ringbuffer_size(ringbuffer) > 0) {
+ size_t read = ringbuffer_pop(ringbuffer, b64_in, 3);
+ if (i > 0 && i % MAX_LINE_LENGTH == 0)
+ dprintf(fd, "\n");
+ i += b64_ntop(b64_in, read, b64_out, 5);
+ dprintf(fd, b64_out);
+ }
+
+ dprintf(fd, "\n--- END:BTSNOOP_LOG_SUMMARY (%zu bytes out) ---\n", i);
+
+error:
+ ringbuffer_free(ringbuffer);
+}
diff --git a/btif/src/btif_debug_conn.c b/btif/src/btif_debug_conn.c
new file mode 100644
index 000000000..5155c851b
--- /dev/null
+++ b/btif/src/btif_debug_conn.c
@@ -0,0 +1,108 @@
+/******************************************************************************
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "btif/include/btif_debug.h"
+#include "btif/include/btif_debug_conn.h"
+
+#define NUM_CONNECTION_EVENTS 16
+
+typedef struct conn_event_t {
+ uint64_t ts;
+ btif_debug_conn_state_t state;
+ bt_bdaddr_t bda;
+ tGATT_DISCONN_REASON disconnect_reason;
+} conn_event_t;
+
+static conn_event_t connection_events[NUM_CONNECTION_EVENTS];
+static uint8_t current_event = 0;
+
+static char *format_ts(const uint64_t ts, char *buffer, int len) {
+ const uint64_t ms = ts / 1000;
+ const time_t secs = ms / 1000;
+ struct tm *ptm = localtime(&secs);
+
+ strftime(buffer, len, "%m-%d %H:%M:%S.%%03u", ptm);
+ snprintf(buffer, len, buffer, (uint16_t)(ms % 1000));
+
+ return buffer;
+}
+
+static char *format_state(const btif_debug_conn_state_t state) {
+ switch (state) {
+ case BTIF_DEBUG_CONNECTED:
+ return "CONNECTED ";
+ case BTIF_DEBUG_DISCONNECTED:
+ return "DISCONNECTED";
+ }
+ return "UNKNOWN";
+}
+
+static void next_event() {
+ ++current_event;
+ if (current_event == NUM_CONNECTION_EVENTS)
+ current_event = 0;
+}
+
+void btif_debug_conn_state(const bt_bdaddr_t bda, const btif_debug_conn_state_t state,
+ const tGATT_DISCONN_REASON disconnect_reason) {
+ next_event();
+
+ conn_event_t *evt = &connection_events[current_event];
+ evt->ts = btif_debug_ts();
+ evt->state = state;
+ evt->disconnect_reason = disconnect_reason;
+ memcpy(&evt->bda, &bda, sizeof(bt_bdaddr_t));
+}
+
+void btif_debug_conn_dump(int fd) {
+ const uint8_t current_event_local = current_event; // Cache to avoid threading issues
+ uint8_t dump_event = current_event_local;
+ char buffer[30] = {0};
+
+ dprintf(fd, "\nConnection Events:\n");
+ if (connection_events[dump_event].ts == 0)
+ dprintf(fd, " None\n");
+
+ while (connection_events[dump_event].ts) {
+ conn_event_t *evt = &connection_events[dump_event];
+ dprintf(fd, " %s %s %%s",
+ format_ts(evt->ts, buffer, sizeof(buffer)),
+ format_state(evt->state),
+ bdaddr_to_string(&evt->bda, buffer, sizeof(buffer))
+ );
+ if (evt->state == BTIF_DEBUG_DISCONNECTED) {
+ dprintf(fd," reason=%d\n", evt->disconnect_reason);
+ } else {
+ dprintf(fd,"\n");
+ }
+
+ // Go to previous event; wrap if needed
+ if (dump_event > 0)
+ --dump_event;
+ else
+ dump_event = NUM_CONNECTION_EVENTS - 1;
+
+ // Check if we dumped all events
+ if (dump_event == current_event_local)
+ break;
+ }
+}
diff --git a/hci/Android.mk b/hci/Android.mk
index 7c69464e5..af87285d1 100644
--- a/hci/Android.mk
+++ b/hci/Android.mk
@@ -4,6 +4,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
src/btsnoop.c \
+ src/btsnoop_mem.c \
src/btsnoop_net.c \
src/buffer_allocator.c \
src/hci_audio.c \
diff --git a/hci/include/btsnoop_mem.h b/hci/include/btsnoop_mem.h
new file mode 100644
index 000000000..fd8c56063
--- /dev/null
+++ b/hci/include/btsnoop_mem.h
@@ -0,0 +1,36 @@
+/******************************************************************************
+ *
+ * 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 <stdint.h>
+
+#include "bt_types.h"
+
+// Callback invoked for each HCI packet.
+// Highlander mode - there can be only one...
+typedef void (*btsnoop_data_cb)(const uint16_t type, const uint8_t *p_data, const size_t len);
+
+// This call sets the (one and only) callback that will
+// be invoked once for each HCI packet/event.
+void btsnoop_mem_set_callback(btsnoop_data_cb cb);
+
+// This function is invoked every time an HCI packet
+// is sent/received. Packets will be filtered and then
+// forwarded to the |btsnoop_data_cb|.
+void btsnoop_mem_capture(const BT_HDR *p_buf);
diff --git a/hci/src/btsnoop.c b/hci/src/btsnoop.c
index 538fc9515..990623e38 100644
--- a/hci/src/btsnoop.c
+++ b/hci/src/btsnoop.c
@@ -32,7 +32,8 @@
#include <sys/time.h>
#include <unistd.h>
-#include "btsnoop.h"
+#include "hci/include/btsnoop.h"
+#include "hci/include/btsnoop_mem.h"
#include "bt_types.h"
#include "hci_layer.h"
#include "osi/include/log.h"
@@ -101,6 +102,8 @@ static void set_api_wants_to_log(bool value) {
static void capture(const BT_HDR *buffer, bool is_received) {
const uint8_t *p = buffer->data + buffer->offset;
+ btsnoop_mem_capture(buffer);
+
if (logfile_fd == INVALID_FD)
return;
diff --git a/hci/src/btsnoop_mem.c b/hci/src/btsnoop_mem.c
new file mode 100644
index 000000000..55efc1be2
--- /dev/null
+++ b/hci/src/btsnoop_mem.c
@@ -0,0 +1,61 @@
+/******************************************************************************
+ *
+ * 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 <assert.h>
+#include <time.h>
+
+#include "hci/include/btsnoop_mem.h"
+
+static btsnoop_data_cb data_callback = NULL;
+
+void btsnoop_mem_set_callback(btsnoop_data_cb cb) {
+ data_callback = cb;
+}
+
+void btsnoop_mem_capture(const BT_HDR *packet) {
+ if (!data_callback)
+ return;
+
+ assert(packet);
+
+ const uint8_t *data = &packet->data[packet->offset];
+ const uint16_t type = packet->event & BT_EVT_MASK;
+ size_t length = 0;
+
+ switch (type) {
+ case BT_EVT_TO_LM_HCI_CMD:
+ if (packet->len > 2)
+ length = data[2] + 4;
+ break;
+
+ case BT_EVT_TO_BTU_HCI_EVT:
+ if (packet->len > 1)
+ length = data[1] + 3;
+ break;
+
+ // Ignore data for privacy
+ case BT_EVT_TO_LM_HCI_ACL:
+ case BT_EVT_TO_LM_HCI_SCO:
+ case BT_EVT_TO_BTU_HCI_ACL:
+ case BT_EVT_TO_BTU_HCI_SCO:
+ break;
+ }
+
+ if (length)
+ (*data_callback)(type, data, length);
+}
diff --git a/include/bt_target.h b/include/bt_target.h
index 4fb2a0f4c..4efe8903c 100644
--- a/include/bt_target.h
+++ b/include/bt_target.h
@@ -1701,6 +1701,11 @@ The maximum number of payload octets that the local device can receive in a sing
**
******************************************************************************/
+/* Enable/disable BTSnoop memory logging */
+#ifndef BTSNOOP_MEM
+#define BTSNOOP_MEM TRUE
+#endif
+
#include "bt_trace.h"
#endif /* BT_TARGET_H */
diff --git a/main/Android.mk b/main/Android.mk
index 9f07e37b1..d5cf8dd36 100644
--- a/main/Android.mk
+++ b/main/Android.mk
@@ -24,6 +24,9 @@ LOCAL_SRC_FILES += \
../btif/src/btif_config.c \
../btif/src/btif_config_transcode.cpp \
../btif/src/btif_core.c \
+ ../btif/src/btif_debug.c \
+ ../btif/src/btif_debug_btsnoop.c \
+ ../btif/src/btif_debug_conn.c \
../btif/src/btif_dm.c \
../btif/src/btif_gatt.c \
../btif/src/btif_gatt_client.c \
@@ -107,7 +110,8 @@ LOCAL_C_INCLUDES+= . \
$(LOCAL_PATH)/../audio_a2dp_hw \
$(LOCAL_PATH)/../utils/include \
$(bdroid_C_INCLUDES) \
- external/tinyxml2
+ external/tinyxml2 \
+ external/zlib
LOCAL_CFLAGS += -DBUILDCFG $(bdroid_CFLAGS) -Wno-error=maybe-uninitialized -Wno-error=uninitialized -Wno-error=unused-parameter
LOCAL_CONLYFLAGS := -std=c99
@@ -127,6 +131,7 @@ LOCAL_SHARED_LIBRARIES := \
libdl \
liblog \
libpower \
+ libz
LOCAL_STATIC_LIBRARIES := \
libbt-brcm_bta \
diff --git a/osi/Android.mk b/osi/Android.mk
index c11f8fc1d..537e91d11 100644
--- a/osi/Android.mk
+++ b/osi/Android.mk
@@ -43,6 +43,7 @@ LOCAL_SRC_FILES := \
./src/list.c \
./src/non_repeating_timer.c \
./src/reactor.c \
+ ./src/ringbuffer.c \
./src/semaphore.c \
./src/socket.c \
./src/thread.c
@@ -82,9 +83,10 @@ LOCAL_SRC_FILES := \
./test/hash_map_test.cpp \
./test/list_test.cpp \
./test/reactor_test.cpp \
+ ./test/ringbuffer_test.cpp \
./test/thread_test.cpp
-LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS := -Wall
LOCAL_MODULE := net_test_osi
LOCAL_MODULE_TAGS := tests
LOCAL_SHARED_LIBRARIES := liblog
diff --git a/osi/include/ringbuffer.h b/osi/include/ringbuffer.h
new file mode 100644
index 000000000..8446d6c14
--- /dev/null
+++ b/osi/include/ringbuffer.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ * 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 <stdint.h>
+
+typedef struct ringbuffer_t ringbuffer_t;
+
+// NOTE:
+// None of the functions below are thread safe when it comes to accessing the
+// *rb pointer. It is *NOT* possible to insert and pop/delete at the same time.
+// Callers must protect the *rb pointer separately.
+
+// Create a ringbuffer with the specified size
+// Returns NULL if memory allocation failed. Resulting pointer must be freed
+// using |ringbuffer_free|.
+ringbuffer_t* ringbuffer_init(const size_t size);
+
+// Frees the ringbuffer structure and buffer
+// Save to call with NULL.
+void ringbuffer_free(ringbuffer_t *rb);
+
+// Returns remaining buffer size
+size_t ringbuffer_available(const ringbuffer_t *rb);
+
+// Returns size of data in buffer
+size_t ringbuffer_size(const ringbuffer_t *rb);
+
+// Attempts to insert up to |length| bytes of data at |p| into the buffer
+// Return actual number of bytes added. Can be less than |length| if buffer
+// is full.
+size_t ringbuffer_insert(ringbuffer_t *rb, const uint8_t *p, size_t length);
+
+// Peek |length| number of bytes from the ringbuffer into the buffer |p|
+// Return the actual number of bytes peeked. Can be less than |length| if
+// there is less than |length| data available.
+size_t ringbuffer_peek(const ringbuffer_t *rb, uint8_t *p, size_t length);
+
+// Does the same as |ringbuffer_peek|, but also advances the ring buffer head
+size_t ringbuffer_pop(ringbuffer_t *rb, uint8_t *p, size_t length);
+
+// Deletes |length| bytes from the ringbuffer starting from the head
+// Return actual number of bytes deleted.
+size_t ringbuffer_delete(ringbuffer_t *rb, size_t length);
diff --git a/osi/src/ringbuffer.c b/osi/src/ringbuffer.c
new file mode 100644
index 000000000..131f5b1c9
--- /dev/null
+++ b/osi/src/ringbuffer.c
@@ -0,0 +1,125 @@
+/******************************************************************************
+ *
+ * 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 <assert.h>
+#include <stdlib.h>
+
+#include "osi/include/allocator.h"
+#include "osi/include/ringbuffer.h"
+
+struct ringbuffer_t {
+ size_t total;
+ size_t available;
+ uint8_t *base;
+ uint8_t *head;
+ uint8_t *tail;
+};
+
+ringbuffer_t* ringbuffer_init(const size_t size) {
+ ringbuffer_t* p = osi_calloc(sizeof(ringbuffer_t));
+ if (p == 0)
+ return NULL;
+
+ p->base = osi_calloc(size);
+ if (p->base == 0) {
+ osi_free(p);
+ return NULL;
+ }
+
+ p->head = p->tail = p->base;
+ p->total = p->available = size;
+
+ return p;
+}
+
+void ringbuffer_free(ringbuffer_t *rb) {
+ if (rb != NULL)
+ osi_free(rb->base);
+ osi_free(rb);
+}
+
+size_t ringbuffer_available(const ringbuffer_t *rb) {
+ assert(rb);
+ return rb->available;
+}
+
+size_t ringbuffer_size(const ringbuffer_t *rb) {
+ assert(rb);
+ return rb->total - rb->available;
+}
+
+size_t ringbuffer_insert(ringbuffer_t *rb, const uint8_t *p, size_t length) {
+ assert(rb);
+ assert(p);
+
+ if (length > ringbuffer_available(rb))
+ length = ringbuffer_available(rb);
+
+ for (size_t i = 0; i != length; ++i) {
+ *rb->tail++ = *p++;
+ if (rb->tail >= (rb->base + rb->total))
+ rb->tail = rb->base;
+ }
+
+ rb->available -= length;
+ return length;
+}
+
+size_t ringbuffer_delete(ringbuffer_t *rb, size_t length) {
+ assert(rb);
+
+ if (length > ringbuffer_size(rb))
+ length = ringbuffer_size(rb);
+
+ rb->head += length;
+ if (rb->head >= (rb->base + rb->total))
+ rb->head -= rb->total;
+
+ rb->available += length;
+ return length;
+}
+
+size_t ringbuffer_peek(const ringbuffer_t *rb, uint8_t *p, size_t length) {
+ assert(rb);
+ assert(p);
+
+ uint8_t *b = rb->head;
+ size_t copied = 0;
+
+ while (copied < length && copied < ringbuffer_size(rb)) {
+ *p++ = *b++;
+ if (b >= (rb->base + rb->total))
+ b = rb->base;
+ ++copied;
+ }
+
+ return copied;
+}
+
+size_t ringbuffer_pop(ringbuffer_t *rb, uint8_t *p, size_t length) {
+ assert(rb);
+ assert(p);
+
+ const size_t copied = ringbuffer_peek(rb, p, length);
+ rb->head += copied;
+ if (rb->head >= (rb->base + rb->total))
+ rb->head -= rb->total;
+
+ rb->available += copied;
+ return copied;
+}
diff --git a/osi/test/ringbuffer_test.cpp b/osi/test/ringbuffer_test.cpp
new file mode 100644
index 000000000..2ebdeca0b
--- /dev/null
+++ b/osi/test/ringbuffer_test.cpp
@@ -0,0 +1,139 @@
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "ringbuffer.h"
+#include "osi.h"
+}
+
+TEST(RingbufferTest, test_new_simple) {
+ ringbuffer_t *rb = ringbuffer_init(4096);
+ ASSERT_TRUE(rb != NULL);
+ EXPECT_EQ(4096, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+ ringbuffer_free(rb);
+}
+
+TEST(RingbufferTest, test_insert_basic) {
+ ringbuffer_t *rb = ringbuffer_init(16);
+
+ uint8_t buffer[10] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A};
+ ringbuffer_insert(rb, buffer, 10);
+ EXPECT_EQ(10, ringbuffer_size(rb));
+ EXPECT_EQ(6, ringbuffer_available(rb));
+
+ uint8_t peek[10] = {0};
+ size_t peeked = ringbuffer_peek(rb, peek, 10);
+ EXPECT_EQ(10, ringbuffer_size(rb)); // Ensure size doesn't change
+ EXPECT_EQ(6, ringbuffer_available(rb));
+ EXPECT_EQ(10, peeked);
+ ASSERT_TRUE(0 == memcmp(buffer, peek, peeked));
+
+ ringbuffer_free(rb);
+}
+
+TEST(RingbufferTest, test_insert_full) {
+ ringbuffer_t *rb = ringbuffer_init(5);
+
+ uint8_t aa[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ uint8_t bb[] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
+ uint8_t peek[5] = {0};
+
+ size_t added = ringbuffer_insert(rb, aa, 7);
+ EXPECT_EQ(5, added);
+ EXPECT_EQ(0, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ added = ringbuffer_insert(rb, bb, 5);
+ EXPECT_EQ(0, added);
+ EXPECT_EQ(0, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ size_t peeked = ringbuffer_peek(rb, peek, 5);
+ EXPECT_EQ(5, peeked);
+ EXPECT_EQ(0, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ ASSERT_TRUE(0 == memcmp(aa, peek, peeked));
+
+ ringbuffer_free(rb);
+}
+
+TEST(RingbufferTest, test_multi_insert_delete) {
+ ringbuffer_t *rb = ringbuffer_init(16);
+ EXPECT_EQ(16, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+
+ // Insert some bytes
+
+ uint8_t aa[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
+ size_t added = ringbuffer_insert(rb, aa, sizeof(aa));
+ EXPECT_EQ(8, added);
+ EXPECT_EQ(8, ringbuffer_available(rb));
+ EXPECT_EQ(8, ringbuffer_size(rb));
+
+ uint8_t bb[] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
+ ringbuffer_insert(rb, bb, sizeof(bb));
+ EXPECT_EQ(3, ringbuffer_available(rb));
+ EXPECT_EQ(13, ringbuffer_size(rb));
+
+ uint8_t content[] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
+ uint8_t peek[16] = {0};
+ size_t peeked = ringbuffer_peek(rb, peek, 16);
+ EXPECT_EQ(13, peeked);
+ ASSERT_TRUE(0 == memcmp(content, peek, peeked));
+
+ // Delete some bytes
+
+ ringbuffer_delete(rb, sizeof(aa));
+ EXPECT_EQ(11, ringbuffer_available(rb));
+ EXPECT_EQ(5, ringbuffer_size(rb));
+
+ // Add some more to wrap buffer
+
+ uint8_t cc[] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
+ ringbuffer_insert(rb, cc, sizeof(cc));
+ EXPECT_EQ(2, ringbuffer_available(rb));
+ EXPECT_EQ(14, ringbuffer_size(rb));
+
+ uint8_t content2[] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xCC, 0xCC};
+ peeked = ringbuffer_peek(rb, peek, 7);
+ EXPECT_EQ(7, peeked);
+ ASSERT_TRUE(0 == memcmp(content2, peek, peeked));
+
+ // Pop buffer
+
+ memset(peek, 0, 16);
+ size_t popped = ringbuffer_pop(rb, peek, 7);
+ EXPECT_EQ(7, popped);
+ EXPECT_EQ(9, ringbuffer_available(rb));
+ ASSERT_TRUE(0 == memcmp(content2, peek, peeked));
+
+ // Add more again to check head motion
+
+ uint8_t dd[] = { 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD };
+ added = ringbuffer_insert(rb, dd, sizeof(dd));
+ EXPECT_EQ(8, added);
+ EXPECT_EQ(1, ringbuffer_available(rb));
+
+ // Delete everything
+
+ ringbuffer_delete(rb, 16);
+ EXPECT_EQ(16, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+
+ // Add small token
+
+ uint8_t ae[] = { 0xAE, 0xAE, 0xAE };
+ added = ringbuffer_insert(rb, ae, sizeof(ae));
+ EXPECT_EQ(13, ringbuffer_available(rb));
+
+ // Get everything
+
+ popped = ringbuffer_pop(rb, peek, 16);
+ EXPECT_EQ(added, popped);
+ EXPECT_EQ(16, ringbuffer_available(rb));
+ EXPECT_EQ(0, ringbuffer_size(rb));
+ ASSERT_TRUE(0 == memcmp(ae, peek, popped));
+
+ ringbuffer_free(rb);
+}