mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-20 22:15:40 +00:00
[MINOR] stats socket: add show sess <id> to dump details about a session
When trying to spot some complex bugs, it's often needed to access information on stuck sessions, which is quite difficult. This new command helps one get detailed information about a session, with flags, timers, states, etc... The buffer data are not dumped yet.
This commit is contained in:
parent
d426a18a04
commit
66dc20a17b
@ -7803,6 +7803,13 @@ show sess
|
|||||||
be huge. This command is restricted and can only be issued on sockets
|
be huge. This command is restricted and can only be issued on sockets
|
||||||
configured for levels "operator" or "admin".
|
configured for levels "operator" or "admin".
|
||||||
|
|
||||||
|
show sess <id>
|
||||||
|
Display a lot of internal information about the specified session identifier.
|
||||||
|
This identifier is the first field at the beginning of the lines in the dumps
|
||||||
|
of "show sess" (it corresponds to the session pointer). Those information are
|
||||||
|
useless to most users but may be used by haproxy developers to troubleshoot a
|
||||||
|
complex bug. The output format is intentionally not documented so that it can
|
||||||
|
freely evolve depending on demands.
|
||||||
|
|
||||||
show stat [<iid> <type> <sid>]
|
show stat [<iid> <type> <sid>]
|
||||||
Dump statistics in the CSV format. By passing <id>, <type> and <sid>, it is
|
Dump statistics in the CSV format. By passing <id>, <type> and <sid>, it is
|
||||||
|
@ -212,7 +212,11 @@ struct session {
|
|||||||
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
|
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
|
||||||
} stats;
|
} stats;
|
||||||
struct {
|
struct {
|
||||||
struct bref bref;
|
struct bref bref; /* back-reference from the session being dumped */
|
||||||
|
void *target; /* session we want to dump, or NULL for all */
|
||||||
|
unsigned int uid; /* if non-null, the uniq_id of the session being dumped */
|
||||||
|
int section; /* section of the session being dumped */
|
||||||
|
int pos; /* last position of the current session's buffer */
|
||||||
} sess;
|
} sess;
|
||||||
struct {
|
struct {
|
||||||
int iid; /* if >= 0, ID of the proxy to filter on */
|
int iid; /* if >= 0, ID of the proxy to filter on */
|
||||||
|
244
src/dumpstats.c
244
src/dumpstats.c
@ -61,7 +61,7 @@ const char stats_sock_usage_msg[] =
|
|||||||
" show info : report information about the running process\n"
|
" show info : report information about the running process\n"
|
||||||
" show stat : report counters for each proxy and server\n"
|
" show stat : report counters for each proxy and server\n"
|
||||||
" show errors : report last request and response errors for each proxy\n"
|
" show errors : report last request and response errors for each proxy\n"
|
||||||
" show sess : report the list of current sessions\n"
|
" show sess [id] : report the list of current sessions or dump this session\n"
|
||||||
" get weight : report a server's current weight\n"
|
" get weight : report a server's current weight\n"
|
||||||
" set weight : change a server's weight\n"
|
" set weight : change a server's weight\n"
|
||||||
" set timeout : change a timeout setting\n"
|
" set timeout : change a timeout setting\n"
|
||||||
@ -316,6 +316,12 @@ int stats_sock_parse_request(struct stream_interface *si, char *line)
|
|||||||
si->st0 = STAT_CLI_PRINT;
|
si->st0 = STAT_CLI_PRINT;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (*args[2])
|
||||||
|
s->data_ctx.sess.target = (void *)strtoul(args[2], NULL, 0);
|
||||||
|
else
|
||||||
|
s->data_ctx.sess.target = NULL;
|
||||||
|
s->data_ctx.sess.section = 0; /* start with session status */
|
||||||
|
s->data_ctx.sess.pos = 0;
|
||||||
si->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer
|
si->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer
|
||||||
}
|
}
|
||||||
else if (strcmp(args[1], "errors") == 0) {
|
else if (strcmp(args[1], "errors") == 0) {
|
||||||
@ -2298,6 +2304,209 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is called to send output to the response buffer. It dumps a
|
||||||
|
* complete session state onto the output buffer <rep>. The session has to be
|
||||||
|
* set in data_ctx.sess.target. It returns 0 if the output buffer is full and
|
||||||
|
* it needs to be called again, otherwise non-zero. It is designed to be called
|
||||||
|
* from stats_dump_sess_to_buffer() below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* returns 1 if dump is not complete */
|
||||||
|
int stats_dump_full_sess_to_buffer(struct session *s, struct buffer *rep)
|
||||||
|
{
|
||||||
|
struct tm tm;
|
||||||
|
struct chunk msg;
|
||||||
|
struct session *sess;
|
||||||
|
extern const char *monthname[12];
|
||||||
|
char pn[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
|
chunk_init(&msg, trash, sizeof(trash));
|
||||||
|
sess = s->data_ctx.sess.target;
|
||||||
|
|
||||||
|
if (s->data_ctx.sess.section > 0 && s->data_ctx.sess.uid != sess->uniq_id) {
|
||||||
|
/* session changed, no need to go any further */
|
||||||
|
chunk_printf(&msg, " *** session terminated while we were watching it ***\n");
|
||||||
|
if (buffer_feed_chunk(rep, &msg) >= 0)
|
||||||
|
return 0;
|
||||||
|
s->data_ctx.sess.target = NULL;
|
||||||
|
s->data_ctx.sess.uid = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (s->data_ctx.sess.section) {
|
||||||
|
case 0: /* main status of the session */
|
||||||
|
s->data_ctx.sess.uid = sess->uniq_id;
|
||||||
|
s->data_ctx.sess.section = 1;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
chunk_printf(&msg,
|
||||||
|
"%p: id=%u, proto=%s",
|
||||||
|
sess,
|
||||||
|
sess->uniq_id,
|
||||||
|
sess->listener->proto->name);
|
||||||
|
|
||||||
|
switch (sess->listener->proto->sock_family) {
|
||||||
|
case AF_INET:
|
||||||
|
inet_ntop(AF_INET,
|
||||||
|
(const void *)&((struct sockaddr_in *)&sess->cli_addr)->sin_addr,
|
||||||
|
pn, sizeof(pn));
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" source=%s:%d\n",
|
||||||
|
pn,
|
||||||
|
ntohs(((struct sockaddr_in *)&sess->cli_addr)->sin_port));
|
||||||
|
break;
|
||||||
|
case AF_INET6:
|
||||||
|
inet_ntop(AF_INET6,
|
||||||
|
(const void *)&((struct sockaddr_in6 *)(&sess->cli_addr))->sin6_addr,
|
||||||
|
pn, sizeof(pn));
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" source=%s:%d\n",
|
||||||
|
pn,
|
||||||
|
ntohs(((struct sockaddr_in6 *)&sess->cli_addr)->sin6_port));
|
||||||
|
break;
|
||||||
|
case AF_UNIX:
|
||||||
|
default:
|
||||||
|
/* no more information to print right now */
|
||||||
|
chunk_printf(&msg, "\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" flags=0x%x, conn_retries=%d, srv_conn=%p, pend_pos=%p\n",
|
||||||
|
sess->flags, sess->conn_retries, sess->srv_conn, sess->pend_pos);
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" frontend=%s (id=%u mode=%s), listener=%s (id=%u)\n",
|
||||||
|
sess->fe->id, sess->fe->uuid, sess->fe->mode ? "http" : "tcp",
|
||||||
|
sess->listener ? sess->listener->name ? sess->listener->name : "?" : "?",
|
||||||
|
sess->listener ? sess->listener->luid : 0);
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" backend=%s (id=%u mode=%s) server=%s (id=%u)\n",
|
||||||
|
sess->be->id, sess->be->uuid, sess->be->mode ? "http" : "tcp",
|
||||||
|
sess->srv ? sess->srv->id : "<none>",
|
||||||
|
sess->srv ? sess->srv->puid : 0);
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" task=%p (state=0x%02x nice=%d calls=%d exp=%s%s)\n",
|
||||||
|
sess->task,
|
||||||
|
sess->task->state,
|
||||||
|
sess->task->nice, sess->task->calls,
|
||||||
|
sess->task->expire ?
|
||||||
|
tick_is_expired(sess->task->expire, now_ms) ? "<PAST>" :
|
||||||
|
human_time(TICKS_TO_MS(sess->task->expire - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
||||||
|
task_in_rq(sess->task) ? ", running" : "");
|
||||||
|
|
||||||
|
get_localtime(sess->logs.accept_date.tv_sec, &tm);
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" task created [%02d/%s/%04d:%02d:%02d:%02d.%06d] (age=%s)\n",
|
||||||
|
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(sess->logs.accept_date.tv_usec),
|
||||||
|
human_time(now.tv_sec - sess->logs.accept_date.tv_sec, 1));
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" si[0]=%p (state=%d flags=0x%02x fd=%d exp=%s, et=0x%03x)\n",
|
||||||
|
&sess->si[0],
|
||||||
|
sess->si[0].state,
|
||||||
|
sess->si[0].flags,
|
||||||
|
sess->si[0].fd,
|
||||||
|
sess->si[0].exp ?
|
||||||
|
tick_is_expired(sess->si[0].exp, now_ms) ? "<PAST>" :
|
||||||
|
human_time(TICKS_TO_MS(sess->si[0].exp - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
||||||
|
sess->si[0].err_type);
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" si[1]=%p (state=%d flags=0x%02x fd=%d exp=%s, et=0x%03x)\n",
|
||||||
|
&sess->si[1],
|
||||||
|
sess->si[1].state,
|
||||||
|
sess->si[1].flags,
|
||||||
|
sess->si[1].fd,
|
||||||
|
sess->si[1].exp ?
|
||||||
|
tick_is_expired(sess->si[1].exp, now_ms) ? "<PAST>" :
|
||||||
|
human_time(TICKS_TO_MS(sess->si[1].exp - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
||||||
|
sess->si[1].err_type);
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" txn=%p (flags=0x%x meth=%d status=%d req.st=%d rsp.st=%d)\n",
|
||||||
|
&sess->txn, sess->txn.flags, sess->txn.meth, sess->txn.status,
|
||||||
|
sess->txn.req.msg_state, sess->txn.rsp.msg_state);
|
||||||
|
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" req=%p (f=0x%06x an=0x%x l=%d sndmx=%d pipe=%d fwd=%ld)\n"
|
||||||
|
" an_exp=%s",
|
||||||
|
sess->req,
|
||||||
|
sess->req->flags, sess->req->analysers,
|
||||||
|
sess->req->l, sess->req->send_max,
|
||||||
|
sess->req->pipe ? sess->req->pipe->data : 0,
|
||||||
|
sess->req->to_forward,
|
||||||
|
sess->req->analyse_exp ?
|
||||||
|
human_time(TICKS_TO_MS(sess->req->analyse_exp - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" rex=%s",
|
||||||
|
sess->req->rex ?
|
||||||
|
human_time(TICKS_TO_MS(sess->req->rex - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" wex=%s\n"
|
||||||
|
" data=%p r=%d w=%d lr=%d total=%lld\n",
|
||||||
|
sess->req->wex ?
|
||||||
|
human_time(TICKS_TO_MS(sess->req->wex - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
||||||
|
sess->req->data,
|
||||||
|
sess->req->r - sess->req->data,
|
||||||
|
sess->req->w - sess->req->data,
|
||||||
|
sess->req->lr - sess->req->data,
|
||||||
|
sess->req->total);
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" res=%p (f=0x%06x an=0x%x l=%d sndmx=%d pipe=%d fwd=%ld)\n"
|
||||||
|
" an_exp=%s",
|
||||||
|
sess->rep,
|
||||||
|
sess->rep->flags, sess->rep->analysers,
|
||||||
|
sess->rep->l, sess->rep->send_max,
|
||||||
|
sess->rep->pipe ? sess->rep->pipe->data : 0,
|
||||||
|
sess->rep->to_forward,
|
||||||
|
sess->rep->analyse_exp ?
|
||||||
|
human_time(TICKS_TO_MS(sess->rep->analyse_exp - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" rex=%s",
|
||||||
|
sess->rep->rex ?
|
||||||
|
human_time(TICKS_TO_MS(sess->rep->rex - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
||||||
|
|
||||||
|
chunk_printf(&msg,
|
||||||
|
" wex=%s\n"
|
||||||
|
" data=%p r=%d w=%d lr=%d total=%lld\n",
|
||||||
|
sess->rep->wex ?
|
||||||
|
human_time(TICKS_TO_MS(sess->rep->wex - now_ms),
|
||||||
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
||||||
|
sess->rep->data,
|
||||||
|
sess->rep->r - sess->rep->data,
|
||||||
|
sess->rep->w - sess->rep->data,
|
||||||
|
sess->rep->lr - sess->rep->data,
|
||||||
|
sess->rep->total);
|
||||||
|
|
||||||
|
if (buffer_feed_chunk(rep, &msg) >= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* use other states to dump the contents */
|
||||||
|
}
|
||||||
|
/* end of dump */
|
||||||
|
s->data_ctx.sess.uid = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* This function is called to send output to the response buffer.
|
/* This function is called to send output to the response buffer.
|
||||||
* It dumps the sessions states onto the output buffer <rep>.
|
* It dumps the sessions states onto the output buffer <rep>.
|
||||||
@ -2352,6 +2561,22 @@ int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
|
|||||||
|
|
||||||
curr_sess = LIST_ELEM(s->data_ctx.sess.bref.ref, struct session *, list);
|
curr_sess = LIST_ELEM(s->data_ctx.sess.bref.ref, struct session *, list);
|
||||||
|
|
||||||
|
if (s->data_ctx.sess.target) {
|
||||||
|
if (s->data_ctx.sess.target != curr_sess)
|
||||||
|
goto next_sess;
|
||||||
|
|
||||||
|
LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users);
|
||||||
|
/* call the proper dump() function and return if we're missing space */
|
||||||
|
if (!stats_dump_full_sess_to_buffer(s, rep))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* session dump complete */
|
||||||
|
LIST_DEL(&s->data_ctx.sess.bref.users);
|
||||||
|
LIST_INIT(&s->data_ctx.sess.bref.users);
|
||||||
|
s->data_ctx.sess.target = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
chunk_printf(&msg,
|
chunk_printf(&msg,
|
||||||
"%p: proto=%s",
|
"%p: proto=%s",
|
||||||
curr_sess,
|
curr_sess,
|
||||||
@ -2476,8 +2701,25 @@ int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next_sess:
|
||||||
s->data_ctx.sess.bref.ref = curr_sess->list.n;
|
s->data_ctx.sess.bref.ref = curr_sess->list.n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s->data_ctx.sess.target) {
|
||||||
|
/* specified session not found */
|
||||||
|
if (s->data_ctx.sess.section > 0)
|
||||||
|
chunk_printf(&msg, " *** session terminated while we were watching it ***\n");
|
||||||
|
else
|
||||||
|
chunk_printf(&msg, "Session not found.\n");
|
||||||
|
|
||||||
|
if (buffer_feed_chunk(rep, &msg) >= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
s->data_ctx.sess.target = NULL;
|
||||||
|
s->data_ctx.sess.uid = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
s->data_state = DATA_ST_FIN;
|
s->data_state = DATA_ST_FIN;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user