#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
|