| #include "usb_transport_sniffer.h" | |
| #include <android-base/stringprintf.h> | |
| #include <sys/select.h> | |
| #include <sys/time.h> | |
| #include <sys/types.h> | |
| #include <iomanip> | |
| #include <sstream> | |
| namespace fastboot { | |
| UsbTransportSniffer::UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, | |
| const int serial_fd) | |
| : transport_(std::move(transport)), serial_fd_(serial_fd) {} | |
| UsbTransportSniffer::~UsbTransportSniffer() { | |
| Close(); | |
| } | |
| ssize_t UsbTransportSniffer::Read(void* data, size_t len) { | |
| ProcessSerial(); | |
| ssize_t ret = transport_->Read(data, len); | |
| if (ret < 0) { | |
| const char* err = strerror(errno); | |
| std::vector<char> buf(err, err + strlen(err)); | |
| Event e(READ_ERROR, std::move(buf)); | |
| transfers_.push_back(e); | |
| return ret; | |
| } | |
| char* cdata = static_cast<char*>(data); | |
| std::vector<char> buf(cdata, cdata + ret); | |
| Event e(READ, std::move(buf)); | |
| transfers_.push_back(e); | |
| ProcessSerial(); | |
| return ret; | |
| } | |
| ssize_t UsbTransportSniffer::Write(const void* data, size_t len) { | |
| ProcessSerial(); | |
| size_t ret = transport_->Write(data, len); | |
| if (ret != len) { | |
| const char* err = strerror(errno); | |
| std::vector<char> buf(err, err + strlen(err)); | |
| Event e(WRITE_ERROR, std::move(buf)); | |
| transfers_.push_back(e); | |
| return ret; | |
| } | |
| const char* cdata = static_cast<const char*>(data); | |
| std::vector<char> buf(cdata, cdata + len); | |
| Event e(WRITE, std::move(buf)); | |
| transfers_.push_back(e); | |
| ProcessSerial(); | |
| return ret; | |
| } | |
| int UsbTransportSniffer::Close() { | |
| return transport_->Close(); | |
| } | |
| int UsbTransportSniffer::Reset() { | |
| ProcessSerial(); | |
| int ret = transport_->Reset(); | |
| std::vector<char> buf; | |
| Event e(RESET, std::move(buf)); | |
| transfers_.push_back(e); | |
| ProcessSerial(); | |
| return ret; | |
| } | |
| const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() { | |
| return transfers_; | |
| } | |
| /* | |
| * When a test fails, we want a human readable log of everything going on up until | |
| * the failure. This method will look through its log of captured events, and | |
| * create a clean printable string of everything that happened. | |
| */ | |
| std::string UsbTransportSniffer::CreateTrace() { | |
| std::string ret; | |
| const auto no_print = [](char c) -> bool { return !isprint(c); }; | |
| // This lambda creates a humand readable representation of a byte buffer | |
| // It first attempts to figure out whether it should be interpreted as an ASCII buffer, | |
| // and be printed as a string, or just a raw byte-buffer | |
| const auto msg = [&ret, no_print](const std::vector<char>& buf) { | |
| ret += android::base::StringPrintf("(%lu bytes): ", buf.size()); | |
| std::vector<const char>::iterator iter = buf.end(); | |
| const unsigned max_chars = 50; | |
| if (buf.size() > max_chars) { | |
| iter = buf.begin() + max_chars; | |
| } | |
| ret += '"'; | |
| if (std::count_if(buf.begin(), iter, no_print) == 0) { // print as ascii | |
| ret.insert(ret.end(), buf.begin(), iter); | |
| } else { // print as hex | |
| std::stringstream ss; | |
| for (auto c = buf.begin(); c < iter; c++) { | |
| ss << std::hex << std::setw(2) << std::setfill('0') | |
| << static_cast<uint16_t>(static_cast<uint8_t>(*c)); | |
| ss << ','; | |
| } | |
| ret += ss.str(); | |
| } | |
| if (buf.size() > max_chars) { | |
| ret += android::base::StringPrintf("...\"(+%lu bytes)\n", buf.size() - max_chars); | |
| } else { | |
| ret += "\"\n"; | |
| } | |
| }; | |
| // Now we just scan through the log of everything that happened and create a | |
| // printable string for each one | |
| for (const auto& event : transfers_) { | |
| const std::vector<char>& cbuf = event.buf; | |
| const std::string tmp(cbuf.begin(), cbuf.end()); | |
| auto start = transfers_.front().start; | |
| auto durr = event.start - start; | |
| auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(durr).count(); | |
| switch (event.type) { | |
| case READ: | |
| ret += android::base::StringPrintf("[READ %lldms]", millis); | |
| msg(cbuf); | |
| break; | |
| case WRITE: | |
| ret += android::base::StringPrintf("[WRITE %lldms]", millis); | |
| msg(cbuf); | |
| break; | |
| case RESET: | |
| ret += android::base::StringPrintf("[RESET %lldms]\n", millis); | |
| break; | |
| case READ_ERROR: | |
| ret += android::base::StringPrintf("[READ_ERROR %lldms] %s\n", millis, tmp.c_str()); | |
| break; | |
| case WRITE_ERROR: | |
| ret += android::base::StringPrintf("[WRITE_ERROR %lldms] %s\n", millis, | |
| tmp.c_str()); | |
| break; | |
| case SERIAL: | |
| ret += android::base::StringPrintf("[SERIAL %lldms] %s", millis, tmp.c_str()); | |
| if (ret.back() != '\n') ret += '\n'; | |
| break; | |
| } | |
| } | |
| return ret; | |
| } | |
| // This is a quick call to flush any UART logs the device might have sent | |
| // to our internal event log. It will wait up to 10ms for data to appear | |
| void UsbTransportSniffer::ProcessSerial() { | |
| if (serial_fd_ <= 0) return; | |
| fd_set set; | |
| struct timeval timeout; | |
| FD_ZERO(&set); | |
| FD_SET(serial_fd_, &set); | |
| timeout.tv_sec = 0; | |
| timeout.tv_usec = 10000; // 10ms | |
| int count = 0; | |
| int n = 0; | |
| std::vector<char> buf; | |
| buf.resize(1000); | |
| while (select(serial_fd_ + 1, &set, NULL, NULL, &timeout) > 0) { | |
| n = read(serial_fd_, buf.data() + count, buf.size() - count); | |
| if (n > 0) { | |
| count += n; | |
| } else { | |
| break; | |
| } | |
| } | |
| buf.resize(count); | |
| if (count > 0) { | |
| Event e(SERIAL, std::move(buf)); | |
| transfers_.push_back(e); | |
| } | |
| } | |
| } // namespace fastboot |