Added -s, --stat option to print statistics
Print, for each stream, time delta from SSL/TLS handshake and each SYN_STREAM. The time deltas are measured after SYN_REPLY received and all data received. For example, the following output: SYN_REPLY: X(Y) means SYN_REPLY was received X ms after SSL/TLS handshake and Y ms after the corresponding SYN_STREAM was sent.
This commit is contained in:
parent
991ded912d
commit
e2332abc1d
|
@ -66,22 +66,46 @@ struct Config {
|
||||||
bool remote_name;
|
bool remote_name;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool get_assets;
|
bool get_assets;
|
||||||
|
bool stat;
|
||||||
int spdy_version;
|
int spdy_version;
|
||||||
int timeout;
|
int timeout;
|
||||||
std::string certfile;
|
std::string certfile;
|
||||||
std::string keyfile;
|
std::string keyfile;
|
||||||
int window_bits;
|
int window_bits;
|
||||||
Config():null_out(false), remote_name(false), verbose(false),
|
Config():null_out(false), remote_name(false), verbose(false),
|
||||||
get_assets(false), spdy_version(-1), timeout(-1), window_bits(-1)
|
get_assets(false), stat(false),
|
||||||
|
spdy_version(-1), timeout(-1), window_bits(-1)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RequestStat {
|
||||||
|
timeval on_syn_stream_time;
|
||||||
|
timeval on_syn_reply_time;
|
||||||
|
timeval on_complete_time;
|
||||||
|
RequestStat()
|
||||||
|
{
|
||||||
|
on_syn_stream_time.tv_sec = -1;
|
||||||
|
on_syn_stream_time.tv_usec = -1;
|
||||||
|
on_syn_reply_time.tv_sec = -1;
|
||||||
|
on_syn_reply_time.tv_usec = -1;
|
||||||
|
on_complete_time.tv_sec = -1;
|
||||||
|
on_complete_time.tv_usec = -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void record_time(timeval *tv)
|
||||||
|
{
|
||||||
|
gettimeofday(tv, 0);
|
||||||
|
}
|
||||||
|
|
||||||
struct Request {
|
struct Request {
|
||||||
uri::UriStruct us;
|
uri::UriStruct us;
|
||||||
spdylay_gzip *inflater;
|
spdylay_gzip *inflater;
|
||||||
HtmlParser *html_parser;
|
HtmlParser *html_parser;
|
||||||
// Recursion level: 0: first entity, 1: entity linked from first entity
|
// Recursion level: 0: first entity, 1: entity linked from first entity
|
||||||
int level;
|
int level;
|
||||||
|
RequestStat stat;
|
||||||
|
std::string status;
|
||||||
Request(const uri::UriStruct& us, int level = 0)
|
Request(const uri::UriStruct& us, int level = 0)
|
||||||
: us(us), inflater(0), html_parser(0), level(level)
|
: us(us), inflater(0), html_parser(0), level(level)
|
||||||
{}
|
{}
|
||||||
|
@ -114,6 +138,30 @@ struct Request {
|
||||||
fin);
|
fin);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void record_syn_stream_time()
|
||||||
|
{
|
||||||
|
record_time(&stat.on_syn_stream_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void record_syn_reply_time()
|
||||||
|
{
|
||||||
|
record_time(&stat.on_syn_reply_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void record_complete_time()
|
||||||
|
{
|
||||||
|
record_time(&stat.on_complete_time);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SessionStat {
|
||||||
|
timeval on_handshake_time;
|
||||||
|
SessionStat()
|
||||||
|
{
|
||||||
|
on_handshake_time.tv_sec = -1;
|
||||||
|
on_handshake_time.tv_usec = -1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpdySession {
|
struct SpdySession {
|
||||||
|
@ -127,6 +175,7 @@ struct SpdySession {
|
||||||
size_t complete;
|
size_t complete;
|
||||||
std::string hostport;
|
std::string hostport;
|
||||||
Spdylay *sc;
|
Spdylay *sc;
|
||||||
|
SessionStat stat;
|
||||||
SpdySession():complete(0) {}
|
SpdySession():complete(0) {}
|
||||||
~SpdySession()
|
~SpdySession()
|
||||||
{
|
{
|
||||||
|
@ -165,6 +214,10 @@ struct SpdySession {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void record_handshake_time()
|
||||||
|
{
|
||||||
|
record_time(&stat.on_handshake_time);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
|
@ -251,6 +304,7 @@ void check_stream_id(spdylay_session *session,
|
||||||
Request *req = (Request*)spdylay_session_get_stream_user_data(session,
|
Request *req = (Request*)spdylay_session_get_stream_user_data(session,
|
||||||
stream_id);
|
stream_id);
|
||||||
spdySession->streams[stream_id] = req;
|
spdySession->streams[stream_id] = req;
|
||||||
|
req->record_syn_stream_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_ctrl_send_callback2
|
void on_ctrl_send_callback2
|
||||||
|
@ -260,16 +314,9 @@ void on_ctrl_send_callback2
|
||||||
if(type == SPDYLAY_SYN_STREAM) {
|
if(type == SPDYLAY_SYN_STREAM) {
|
||||||
check_stream_id(session, type, frame, user_data);
|
check_stream_id(session, type, frame, user_data);
|
||||||
}
|
}
|
||||||
}
|
if(config.verbose) {
|
||||||
|
on_ctrl_send_callback(session, type, frame, user_data);
|
||||||
void on_ctrl_send_callback3
|
|
||||||
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
if(type == SPDYLAY_SYN_STREAM) {
|
|
||||||
check_stream_id(session, type, frame, user_data);
|
|
||||||
}
|
}
|
||||||
on_ctrl_send_callback(session, type, frame, user_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_response_header
|
void check_response_header
|
||||||
|
@ -294,6 +341,8 @@ void check_response_header
|
||||||
for(size_t i = 0; nv[i]; i += 2) {
|
for(size_t i = 0; nv[i]; i += 2) {
|
||||||
if(strcmp("content-encoding", nv[i]) == 0) {
|
if(strcmp("content-encoding", nv[i]) == 0) {
|
||||||
gzip = util::strieq("gzip", nv[i+1]) || util::strieq("deflate", nv[i+1]);
|
gzip = util::strieq("gzip", nv[i+1]) || util::strieq("deflate", nv[i+1]);
|
||||||
|
} else if(strcmp(":status", nv[i]) == 0) {
|
||||||
|
req->status = nv[i+1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(gzip) {
|
if(gzip) {
|
||||||
|
@ -312,15 +361,16 @@ void on_ctrl_recv_callback2
|
||||||
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
|
if(type == SPDYLAY_SYN_REPLY) {
|
||||||
|
Request *req = (Request*)spdylay_session_get_stream_user_data
|
||||||
|
(session, frame->syn_reply.stream_id);
|
||||||
|
assert(req);
|
||||||
|
req->record_syn_reply_time();
|
||||||
|
}
|
||||||
check_response_header(session, type, frame, user_data);
|
check_response_header(session, type, frame, user_data);
|
||||||
}
|
if(config.verbose) {
|
||||||
|
on_ctrl_recv_callback(session, type, frame, user_data);
|
||||||
void on_ctrl_recv_callback3
|
}
|
||||||
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
|
|
||||||
void *user_data)
|
|
||||||
{
|
|
||||||
check_response_header(session, type, frame, user_data);
|
|
||||||
on_ctrl_recv_callback(session, type, frame, user_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_stream_close_callback
|
void on_stream_close_callback
|
||||||
|
@ -332,6 +382,7 @@ void on_stream_close_callback
|
||||||
spdySession->streams.find(stream_id);
|
spdySession->streams.find(stream_id);
|
||||||
if(itr != spdySession->streams.end()) {
|
if(itr != spdySession->streams.end()) {
|
||||||
update_html_parser(spdySession, (*itr).second, 0, 0, 1);
|
update_html_parser(spdySession, (*itr).second, 0, 0, 1);
|
||||||
|
(*itr).second->record_complete_time();
|
||||||
++spdySession->complete;
|
++spdySession->complete;
|
||||||
if(spdySession->all_requests_processed()) {
|
if(spdySession->all_requests_processed()) {
|
||||||
spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
|
spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
|
||||||
|
@ -339,6 +390,52 @@ void on_stream_close_callback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t time_delta(const timeval& a, const timeval& b)
|
||||||
|
{
|
||||||
|
int64_t res;
|
||||||
|
res = a.tv_usec - b.tv_usec;
|
||||||
|
int off = 0;
|
||||||
|
if(res < 0) {
|
||||||
|
res += 1000000;
|
||||||
|
off = -1;
|
||||||
|
}
|
||||||
|
res += (a.tv_sec + off - b.tv_sec)*1000000;
|
||||||
|
return res/1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_stats(const SpdySession& spdySession)
|
||||||
|
{
|
||||||
|
std::cout << "***** Statistics *****" << std::endl;
|
||||||
|
for(size_t i = 0; i < spdySession.reqvec.size(); ++i) {
|
||||||
|
const Request *req = spdySession.reqvec[i];
|
||||||
|
std::cout << "#" << i+1 << ": " << uri::construct(req->us) << std::endl;
|
||||||
|
std::cout << " Status: " << req->status << std::endl;
|
||||||
|
std::cout << " Delta (ms) from SSL/TLS handshake(SYN_STREAM):"
|
||||||
|
<< std::endl;
|
||||||
|
if(req->stat.on_syn_reply_time.tv_sec >= 0) {
|
||||||
|
std::cout << " SYN_REPLY: "
|
||||||
|
<< time_delta(req->stat.on_syn_reply_time,
|
||||||
|
spdySession.stat.on_handshake_time)
|
||||||
|
<< "("
|
||||||
|
<< time_delta(req->stat.on_syn_reply_time,
|
||||||
|
req->stat.on_syn_stream_time)
|
||||||
|
<< ")"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
if(req->stat.on_complete_time.tv_sec >= 0) {
|
||||||
|
std::cout << " Completed: "
|
||||||
|
<< time_delta(req->stat.on_complete_time,
|
||||||
|
spdySession.stat.on_handshake_time)
|
||||||
|
<< "("
|
||||||
|
<< time_delta(req->stat.on_complete_time,
|
||||||
|
req->stat.on_syn_stream_time)
|
||||||
|
<< ")"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int communicate(const std::string& host, uint16_t port,
|
int communicate(const std::string& host, uint16_t port,
|
||||||
SpdySession& spdySession,
|
SpdySession& spdySession,
|
||||||
const spdylay_session_callbacks *callbacks)
|
const spdylay_session_callbacks *callbacks)
|
||||||
|
@ -386,6 +483,7 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
if(ssl_handshake(ssl, fd) == -1) {
|
if(ssl_handshake(ssl, fd) == -1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
spdySession.record_handshake_time();
|
||||||
make_non_block(fd);
|
make_non_block(fd);
|
||||||
set_tcp_nodelay(fd);
|
set_tcp_nodelay(fd);
|
||||||
int spdy_version = spdylay_npn_get_version(
|
int spdy_version = spdylay_npn_get_version(
|
||||||
|
@ -459,6 +557,9 @@ int communicate(const std::string& host, uint16_t port,
|
||||||
SSL_CTX_free(ssl_ctx);
|
SSL_CTX_free(ssl_ctx);
|
||||||
shutdown(fd, SHUT_WR);
|
shutdown(fd, SHUT_WR);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
if(config.stat) {
|
||||||
|
print_stats(spdySession);
|
||||||
|
}
|
||||||
return ok ? 0 : -1;
|
return ok ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,17 +570,14 @@ int run(char **uris, int n)
|
||||||
callbacks.send_callback = send_callback;
|
callbacks.send_callback = send_callback;
|
||||||
callbacks.recv_callback = recv_callback;
|
callbacks.recv_callback = recv_callback;
|
||||||
callbacks.on_stream_close_callback = on_stream_close_callback;
|
callbacks.on_stream_close_callback = on_stream_close_callback;
|
||||||
|
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback2;
|
||||||
|
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
|
||||||
if(config.verbose) {
|
if(config.verbose) {
|
||||||
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback3;
|
|
||||||
callbacks.on_data_recv_callback = on_data_recv_callback;
|
callbacks.on_data_recv_callback = on_data_recv_callback;
|
||||||
callbacks.on_ctrl_send_callback = on_ctrl_send_callback3;
|
|
||||||
callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;
|
callbacks.on_invalid_ctrl_recv_callback = on_invalid_ctrl_recv_callback;
|
||||||
callbacks.on_ctrl_recv_parse_error_callback =
|
callbacks.on_ctrl_recv_parse_error_callback =
|
||||||
on_ctrl_recv_parse_error_callback;
|
on_ctrl_recv_parse_error_callback;
|
||||||
callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;
|
callbacks.on_unknown_ctrl_recv_callback = on_unknown_ctrl_recv_callback;
|
||||||
} else {
|
|
||||||
callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback2;
|
|
||||||
callbacks.on_ctrl_send_callback = on_ctrl_send_callback2;
|
|
||||||
}
|
}
|
||||||
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
|
||||||
ssl_debug = config.verbose;
|
ssl_debug = config.verbose;
|
||||||
|
@ -515,7 +613,7 @@ int run(char **uris, int n)
|
||||||
|
|
||||||
void print_usage(std::ostream& out)
|
void print_usage(std::ostream& out)
|
||||||
{
|
{
|
||||||
out << "Usage: spdycat [-Oanv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n"
|
out << "Usage: spdycat [-Oansv23] [-t <SECONDS>] [-w <WINDOW_BITS>] [--cert=<CERT>]\n"
|
||||||
<< " [--key=<KEY>] <URI>..."
|
<< " [--key=<KEY>] <URI>..."
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
@ -542,6 +640,7 @@ void print_help(std::ostream& out)
|
||||||
<< " resource. Only links whose origins are the\n"
|
<< " resource. Only links whose origins are the\n"
|
||||||
<< " same with the linking resource will be\n"
|
<< " same with the linking resource will be\n"
|
||||||
<< " downloaded.\n"
|
<< " downloaded.\n"
|
||||||
|
<< " -s, --stat Print statistics.\n"
|
||||||
<< " --cert=<CERT> Use the specified client certificate file.\n"
|
<< " --cert=<CERT> Use the specified client certificate file.\n"
|
||||||
<< " The file must be in PEM format.\n"
|
<< " The file must be in PEM format.\n"
|
||||||
<< " --key=<KEY> Use the client private key file. The file\n"
|
<< " --key=<KEY> Use the client private key file. The file\n"
|
||||||
|
@ -562,13 +661,15 @@ int main(int argc, char **argv)
|
||||||
{"timeout", required_argument, 0, 't' },
|
{"timeout", required_argument, 0, 't' },
|
||||||
{"window-bits", required_argument, 0, 'w' },
|
{"window-bits", required_argument, 0, 'w' },
|
||||||
{"get-assets", no_argument, 0, 'a' },
|
{"get-assets", no_argument, 0, 'a' },
|
||||||
|
{"stat", no_argument, 0, 's' },
|
||||||
{"cert", required_argument, &flag, 1 },
|
{"cert", required_argument, &flag, 1 },
|
||||||
{"key", required_argument, &flag, 2 },
|
{"key", required_argument, &flag, 2 },
|
||||||
{"help", no_argument, 0, 'h' },
|
{"help", no_argument, 0, 'h' },
|
||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c = getopt_long(argc, argv, "Oanhv23t:w:", long_options, &option_index);
|
int c = getopt_long(argc, argv, "Oanhv23st:w:", long_options,
|
||||||
|
&option_index);
|
||||||
if(c == -1) {
|
if(c == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -615,6 +716,9 @@ int main(int argc, char **argv)
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
#endif // !HAVE_LIBXML2
|
#endif // !HAVE_LIBXML2
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
config.stat = true;
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
case 0:
|
case 0:
|
||||||
|
|
Loading…
Reference in New Issue