diff options
author | Andre Eisenbach <eisenbach@google.com> | 2014-12-05 09:40:20 -0800 |
---|---|---|
committer | Andre Eisenbach <eisenbach@google.com> | 2015-05-11 17:57:11 -0700 |
commit | 89f5e411d9ef31436741288a2267e46dd744e273 (patch) | |
tree | f701404c9c1db42b66b2c4f1b57e402994a7b899 | |
parent | a778a64c053acdb99b7a14b5837b4f12d83009a1 (diff) | |
download | android_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-x | bta/gatt/bta_gattc_act.c | 9 | ||||
-rw-r--r-- | bta/gatt/bta_gatts_act.c | 8 | ||||
-rw-r--r-- | btif/include/btif_debug.h | 31 | ||||
-rw-r--r-- | btif/include/btif_debug_btsnoop.h | 43 | ||||
-rw-r--r-- | btif/include/btif_debug_conn.h | 34 | ||||
-rw-r--r-- | btif/src/bluetooth.c | 9 | ||||
-rw-r--r-- | btif/src/btif_debug.c | 47 | ||||
-rw-r--r-- | btif/src/btif_debug_btsnoop.c | 151 | ||||
-rw-r--r-- | btif/src/btif_debug_conn.c | 108 | ||||
-rw-r--r-- | hci/Android.mk | 1 | ||||
-rw-r--r-- | hci/include/btsnoop_mem.h | 36 | ||||
-rw-r--r-- | hci/src/btsnoop.c | 5 | ||||
-rw-r--r-- | hci/src/btsnoop_mem.c | 61 | ||||
-rw-r--r-- | include/bt_target.h | 5 | ||||
-rw-r--r-- | main/Android.mk | 7 | ||||
-rw-r--r-- | osi/Android.mk | 4 | ||||
-rw-r--r-- | osi/include/ringbuffer.h | 60 | ||||
-rw-r--r-- | osi/src/ringbuffer.c | 125 | ||||
-rw-r--r-- | osi/test/ringbuffer_test.cpp | 139 |
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); +} |