diff options
author | Ruchi Kandoi <kandoiruchi@google.com> | 2017-04-17 17:23:56 -0700 |
---|---|---|
committer | Ruchi Kandoi <kandoiruchi@google.com> | 2017-07-12 16:27:07 -0700 |
commit | 1ebdcc2c6a35acaeb3aad33e739615cfc232d78d (patch) | |
tree | 35703ff78692b1bc7a3c0ecfa561dfa09a71e4f1 | |
parent | adf0b8af38b8a012f17fc039b9ca5a69c5993128 (diff) | |
download | android_hardware_broadcom_nfc-1ebdcc2c6a35acaeb3aad33e739615cfc232d78d.tar.gz android_hardware_broadcom_nfc-1ebdcc2c6a35acaeb3aad33e739615cfc232d78d.tar.bz2 android_hardware_broadcom_nfc-1ebdcc2c6a35acaeb3aad33e739615cfc232d78d.zip |
Add NFC snoop logging to log all NCI communication
Adds ring buffer mechanism to store recent NCI traffic.
This buffer is then dumped into the bugreport for further debugging.
Bug: 63636926
Test: dumpsys nfc
Change-Id: I65e6e0268f70cf959f7d510dbd4cdc625d01e4d0
Signed-off-by: Ruchi Kandoi <kandoiruchi@google.com>
(cherry picked from commit dc56bd6a50442f2f1b09a1c558751c44ea363710)
-rw-r--r-- | src/Android.bp | 2 | ||||
-rw-r--r-- | src/adaptation/NfcAdaptation.cpp | 15 | ||||
-rw-r--r-- | src/adaptation/debug_nfcsnoop.cc | 182 | ||||
-rw-r--r-- | src/adaptation/ringbuffer.cc | 116 | ||||
-rw-r--r-- | src/include/NfcAdaptation.h | 1 | ||||
-rw-r--r-- | src/include/debug_nfcsnoop.h | 50 | ||||
-rw-r--r-- | src/include/ringbuffer.h | 62 | ||||
-rw-r--r-- | src/nfc/nfc/nfc_ncif.cc | 4 |
8 files changed, 432 insertions, 0 deletions
diff --git a/src/Android.bp b/src/Android.bp index 669d63c..39d3007 100644 --- a/src/Android.bp +++ b/src/Android.bp @@ -12,6 +12,7 @@ cc_library_shared { "libhardware", "libmetricslogger", "libpower", + "libz", // Treble configuration "libhidlbase", @@ -55,6 +56,7 @@ cc_library_shared { "nfc/tags/*.c", "adaptation/*.c", "adaptation/*.cpp", + "adaptation/*.cc", "gki/common/*.c", "gki/ulinux/*.c", "nfca_version.c", diff --git a/src/adaptation/NfcAdaptation.cpp b/src/adaptation/NfcAdaptation.cpp index 4194ff6..c09339c 100644 --- a/src/adaptation/NfcAdaptation.cpp +++ b/src/adaptation/NfcAdaptation.cpp @@ -22,6 +22,9 @@ #include <hwbinder/ProcessState.h> #include <pthread.h> #include "NfcAdaptation.h" +#include "debug_nfcsnoop.h" +#include "nfc_target.h" + extern "C" { #include "gki.h" #include "nfa_api.h" @@ -227,6 +230,7 @@ void NfcAdaptation::Initialize() { mHalCallback = NULL; memset(&mHalEntryFuncs, 0, sizeof(mHalEntryFuncs)); InitializeHalDeviceContext(); + debug_nfcsnoop_init(); ALOGD("%s: exit", func); } @@ -257,6 +261,17 @@ void NfcAdaptation::Finalize() { /******************************************************************************* ** +** Function: NfcAdaptation::Dump +** +** Description: Native support for dumpsys function. +** +** Returns: None. +** +*******************************************************************************/ +void NfcAdaptation::Dump(int fd) { debug_nfcsnoop_dump(fd); } + +/******************************************************************************* +** ** Function: NfcAdaptation::signal() ** ** Description: signal the CondVar to release the thread that is waiting diff --git a/src/adaptation/debug_nfcsnoop.cc b/src/adaptation/debug_nfcsnoop.cc new file mode 100644 index 0000000..b7229d6 --- /dev/null +++ b/src/adaptation/debug_nfcsnoop.cc @@ -0,0 +1,182 @@ +/****************************************************************************** + * + * Copyright (C) 2017 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 <mutex> + +#include "bt_types.h" +#include "include/debug_nfcsnoop.h" +#include "include/ringbuffer.h" +#include "nfc_int.h" +#include "nfc_types.h" + +#define USEC_PER_SEC 1000000ULL + +// Total nfcsnoop memory log buffer size +#ifndef NFCSNOOP_MEM_BUFFER_SIZE +static const size_t NFCSNOOP_MEM_BUFFER_SIZE = (256 * 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 std::mutex buffer_mutex; +static ringbuffer_t* buffer = NULL; +static uint64_t last_timestamp_ms = 0; + +static void nfcsnoop_cb(const uint8_t* data, const size_t length, + bool is_received, const uint64_t timestamp_us) { + nfcsnooz_header_t header; + + std::lock_guard<std::mutex> lock(buffer_mutex); + + // Make room in the ring buffer + + while (ringbuffer_available(buffer) < (length + sizeof(nfcsnooz_header_t))) { + ringbuffer_pop(buffer, (uint8_t*)&header, sizeof(nfcsnooz_header_t)); + ringbuffer_delete(buffer, header.length - 1); + } + + // Insert data + header.length = length; + header.is_received = is_received ? 1 : 0; + header.delta_time_ms = + last_timestamp_ms ? timestamp_us - last_timestamp_ms : 0; + last_timestamp_ms = timestamp_us; + + ringbuffer_insert(buffer, (uint8_t*)&header, sizeof(nfcsnooz_header_t)); + ringbuffer_insert(buffer, data, length); +} + +static bool nfcsnoop_compress(ringbuffer_t* rb_dst, ringbuffer_t* rb_src) { + assert(rb_dst != NULL); + assert(rb_src != NULL); + + z_stream zs; + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + zs.opaque = Z_NULL; + + 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]; + + const size_t num_blocks = + (ringbuffer_size(rb_src) + BLOCK_SIZE - 1) / BLOCK_SIZE; + for (size_t i = 0; i < num_blocks; ++i) { + zs.avail_in = + ringbuffer_peek(rb_src, i * BLOCK_SIZE, block_src, BLOCK_SIZE); + zs.next_in = block_src; + + do { + zs.avail_out = BLOCK_SIZE; + zs.next_out = block_dst; + + int err = deflate(&zs, (i == num_blocks - 1) ? 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 nfcsnoop_capture(const NFC_HDR* packet, bool is_received) { + struct timeval tv; + gettimeofday(&tv, NULL); + uint64_t timestamp = static_cast<uint64_t>(tv.tv_sec) * USEC_PER_SEC + + static_cast<uint64_t>(tv.tv_usec); + uint8_t* p = (uint8_t*)(packet + 1) + packet->offset; + uint8_t mt = (*(p)&NCI_MT_MASK) >> NCI_MT_SHIFT; + + if (mt == NCI_MT_DATA) { + nfcsnoop_cb(p, NCI_DATA_HDR_SIZE, is_received, timestamp); + } else if (packet->len > 2) { + nfcsnoop_cb(p, p[2] + NCI_MSG_HDR_SIZE, is_received, timestamp); + } +} + +void debug_nfcsnoop_init(void) { + if (buffer == NULL) buffer = ringbuffer_init(NFCSNOOP_MEM_BUFFER_SIZE); +} + +void debug_nfcsnoop_dump(int fd) { + ringbuffer_t* ringbuffer = ringbuffer_init(NFCSNOOP_MEM_BUFFER_SIZE); + if (ringbuffer == NULL) { + dprintf(fd, "%s Unable to allocate memory for compression", __func__); + return; + } + + // Prepend preamble + + nfcsnooz_preamble_t preamble; + preamble.version = NFCSNOOZ_CURRENT_VERSION; + preamble.last_timestamp_ms = last_timestamp_ms; + ringbuffer_insert(ringbuffer, (uint8_t*)&preamble, + sizeof(nfcsnooz_preamble_t)); + + // Compress data + + uint8_t b64_in[3] = {0}; + char b64_out[5] = {0}; + + size_t line_length = 0; + + bool rc; + { + std::lock_guard<std::mutex> lock(buffer_mutex); + dprintf(fd, "--- BEGIN:NFCSNOOP_LOG_SUMMARY (%zu bytes in) ---\n", + ringbuffer_size(buffer)); + rc = nfcsnoop_compress(ringbuffer, buffer); + } + + if (rc == false) { + dprintf(fd, "%s Log compression failed", __func__); + goto error; + } + + // Base64 encode & output + + while (ringbuffer_size(ringbuffer) > 0) { + size_t read = ringbuffer_pop(ringbuffer, b64_in, 3); + if (line_length >= MAX_LINE_LENGTH) { + dprintf(fd, "\n"); + line_length = 0; + } + line_length += b64_ntop(b64_in, read, b64_out, 5); + dprintf(fd, "%s", b64_out); + } + + dprintf(fd, "\n--- END:NFCSNOOP_LOG_SUMMARY ---\n"); + +error: + ringbuffer_free(ringbuffer); +} diff --git a/src/adaptation/ringbuffer.cc b/src/adaptation/ringbuffer.cc new file mode 100644 index 0000000..d7798fd --- /dev/null +++ b/src/adaptation/ringbuffer.cc @@ -0,0 +1,116 @@ +/****************************************************************************** + * + * Copyright (C) 2017 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 "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 = static_cast<ringbuffer_t*>(calloc(1, sizeof(ringbuffer_t))); + + if (p == NULL) return p; + + p->base = static_cast<uint8_t*>(calloc(size, sizeof(uint8_t))); + p->head = p->tail = p->base; + p->total = p->available = size; + + return p; +} + +void ringbuffer_free(ringbuffer_t* rb) { + if (rb != NULL) free(rb->base); + 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, off_t offset, uint8_t* p, + size_t length) { + assert(rb); + assert(p); + assert(offset >= 0); + assert((size_t)offset <= ringbuffer_size(rb)); + + uint8_t* b = ((rb->head - rb->base + offset) % rb->total) + rb->base; + const size_t bytes_to_copy = (offset + length > ringbuffer_size(rb)) + ? ringbuffer_size(rb) - offset + : length; + + for (size_t copied = 0; copied < bytes_to_copy; ++copied) { + *p++ = *b++; + if (b >= (rb->base + rb->total)) b = rb->base; + } + + return bytes_to_copy; +} + +size_t ringbuffer_pop(ringbuffer_t* rb, uint8_t* p, size_t length) { + assert(rb); + assert(p); + + const size_t copied = ringbuffer_peek(rb, 0, p, length); + rb->head += copied; + if (rb->head >= (rb->base + rb->total)) rb->head -= rb->total; + + rb->available += copied; + return copied; +} diff --git a/src/include/NfcAdaptation.h b/src/include/NfcAdaptation.h index 06c0c38..606f66b 100644 --- a/src/include/NfcAdaptation.h +++ b/src/include/NfcAdaptation.h @@ -80,6 +80,7 @@ class NfcAdaptation { static NfcAdaptation& GetInstance(); tHAL_NFC_ENTRY* GetHalEntryFuncs(); void DownloadFirmware(); + void Dump(int fd); private: NfcAdaptation(); diff --git a/src/include/debug_nfcsnoop.h b/src/include/debug_nfcsnoop.h new file mode 100644 index 0000000..2dd7b6b --- /dev/null +++ b/src/include/debug_nfcsnoop.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * + * Copyright (C) 2017 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. + * + ******************************************************************************/ +#ifndef _DEBUG_NFCSNOOP_ +#define _DEBUG_NFCSNOOP_ + +#include <stdint.h> +#include "nfc_target.h" +#include "nfc_types.h" + +#define NFCSNOOZ_CURRENT_VERSION 0x01 + +// The preamble is stored un-encrypted as the first part +// of the file. +typedef struct nfcsnooz_preamble_t { + uint8_t version; + uint64_t last_timestamp_ms; +} __attribute__((__packed__)) nfcsnooz_preamble_t; + +// One header for each NCI packet +typedef struct nfcsnooz_header_t { + uint16_t length; + uint32_t delta_time_ms; + uint8_t is_received; +} __attribute__((__packed__)) nfcsnooz_header_t; + +// Initializes nfcsnoop memory logging and registers +void debug_nfcsnoop_init(void); + +// Writes nfcsnoop data base64 encoded to fd +void debug_nfcsnoop_dump(int fd); + +// capture the packet +void nfcsnoop_capture(const NFC_HDR* packet, bool is_received); + +#endif /* _DEBUG_NFCSNOOP_ */ diff --git a/src/include/ringbuffer.h b/src/include/ringbuffer.h new file mode 100644 index 0000000..d2c310a --- /dev/null +++ b/src/include/ringbuffer.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Copyright (C) 2017 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, starting at |offset|, +// into the buffer |p|. Return the actual number of bytes peeked. Can be less +// than |length| if there is less than |length| data available. |offset| must +// be non-negative. +size_t ringbuffer_peek(const ringbuffer_t* rb, off_t offset, 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/src/nfc/nfc/nfc_ncif.cc b/src/nfc/nfc/nfc_ncif.cc index 1fa59b0..4b94b8c 100644 --- a/src/nfc/nfc/nfc_ncif.cc +++ b/src/nfc/nfc/nfc_ncif.cc @@ -28,6 +28,7 @@ #include <string.h> #include "nfc_target.h" +#include "include/debug_nfcsnoop.h" #include "nci_defs.h" #include "nci_hmsgs.h" #include "nfc_api.h" @@ -207,6 +208,7 @@ uint8_t nfc_ncif_send_data(tNFC_CONN_CB* p_cb, NFC_HDR* p_data) { /* send to HAL */ HAL_WRITE(p); + nfcsnoop_capture(p, false); if (!fragmented) { /* check if there are more data to send */ @@ -310,6 +312,7 @@ void nfc_ncif_send_cmd(NFC_HDR* p_buf) { /* post the p_buf to NCIT task */ p_buf->event = BT_EVT_TO_NFC_NCI; p_buf->layer_specific = 0; + nfcsnoop_capture(p_buf, false); nfc_ncif_check_cmd_queue(p_buf); } @@ -334,6 +337,7 @@ bool nfc_ncif_process_event(NFC_HDR* p_msg) { pp = p; NCI_MSG_PRS_HDR0(pp, mt, pbf, gid); + nfcsnoop_capture(p_msg, true); switch (mt) { case NCI_MT_DATA: NFC_TRACE_DEBUG0("NFC received data"); |