2012-07-02 03:49:54 +00:00
|
|
|
/*
|
|
|
|
Serval Distributed Numbering Architecture (DNA)
|
2012-11-12 04:04:30 +00:00
|
|
|
Copyright (C) 2012 Serval Project, Inc.
|
2012-07-02 03:49:54 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
2013-10-18 00:04:43 +00:00
|
|
|
#include "fdqueue.h"
|
2013-02-15 20:21:33 +00:00
|
|
|
#include "conf.h"
|
2012-07-02 03:49:54 +00:00
|
|
|
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total *stats_head=NULL;
|
2012-07-02 03:49:54 +00:00
|
|
|
struct call_stats *current_call=NULL;
|
|
|
|
|
2012-07-02 06:34:00 +00:00
|
|
|
void fd_clearstat(struct profile_total *s){
|
2012-07-02 03:49:54 +00:00
|
|
|
s->max_time = 0;
|
|
|
|
s->total_time = 0;
|
2012-07-12 00:45:16 +00:00
|
|
|
s->child_time = 0;
|
2012-07-02 03:49:54 +00:00
|
|
|
s->calls = 0;
|
|
|
|
}
|
|
|
|
|
2012-07-02 06:34:00 +00:00
|
|
|
int fd_tallystats(struct profile_total *total,struct profile_total *a)
|
2012-07-02 03:49:54 +00:00
|
|
|
{
|
|
|
|
total->total_time+=a->total_time;
|
|
|
|
total->calls+=a->calls;
|
|
|
|
if (a->max_time>total->max_time) total->max_time=a->max_time;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-02 06:34:00 +00:00
|
|
|
int fd_showstat(struct profile_total *total, struct profile_total *a)
|
2012-07-02 03:49:54 +00:00
|
|
|
{
|
2013-10-06 19:24:46 +00:00
|
|
|
INFOF("%"PRId64"ms (%2.1f%%) in %d calls (max %"PRId64"ms, avg %.1fms, +child avg %.1fms) : %s",
|
|
|
|
(int64_t) a->total_time,
|
2012-07-12 00:45:16 +00:00
|
|
|
a->total_time*100.0/total->total_time,
|
2012-07-02 03:49:54 +00:00
|
|
|
a->calls,
|
2013-10-06 19:24:46 +00:00
|
|
|
(int64_t) a->max_time,
|
2012-07-12 00:45:16 +00:00
|
|
|
a->total_time*1.00/a->calls,
|
|
|
|
(a->total_time+a->child_time)*1.00/a->calls,
|
2012-07-02 03:49:54 +00:00
|
|
|
a->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-02 04:41:37 +00:00
|
|
|
// sort the list of call times
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total *sort(struct profile_total *list){
|
|
|
|
struct profile_total *first = list;
|
2012-07-02 04:41:37 +00:00
|
|
|
// the left hand list will contain all items that took longer than the first item
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total *left_head = NULL;
|
|
|
|
struct profile_total *left_tail = NULL;
|
2012-07-02 04:41:37 +00:00
|
|
|
// the right hand list will contain all items that took less time than the first item
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total *right_head = NULL;
|
|
|
|
struct profile_total *right_tail = NULL;
|
2012-07-02 04:41:37 +00:00
|
|
|
|
|
|
|
// most of the cpu time is likely to be the same offenders
|
|
|
|
// don't sort a list that's already sorted
|
|
|
|
int left_already_sorted = 1;
|
|
|
|
int right_already_sorted = 1;
|
|
|
|
|
|
|
|
if (!list)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
list = list->_next;
|
|
|
|
first->_next = NULL;
|
|
|
|
|
|
|
|
// split the list into two sub-lists based on the time of the first entry
|
|
|
|
while(list){
|
|
|
|
if (list->total_time > first->total_time){
|
|
|
|
if (left_tail){
|
|
|
|
left_tail->_next = list;
|
|
|
|
if (list->total_time > left_tail->total_time)
|
|
|
|
left_already_sorted = 0;
|
|
|
|
}else
|
|
|
|
left_head=list;
|
|
|
|
left_tail=list;
|
|
|
|
}else{
|
|
|
|
if (right_tail){
|
|
|
|
right_tail->_next = list;
|
|
|
|
if (list->total_time > right_tail->total_time)
|
|
|
|
right_already_sorted = 0;
|
|
|
|
}else
|
|
|
|
right_head=list;
|
|
|
|
right_tail=list;
|
|
|
|
}
|
|
|
|
list = list->_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort the left sub-list
|
|
|
|
if (left_tail){
|
|
|
|
left_tail->_next=NULL;
|
|
|
|
|
|
|
|
if (!left_already_sorted){
|
|
|
|
left_head = sort(left_head);
|
|
|
|
|
|
|
|
// find the tail again
|
|
|
|
left_tail = left_head;
|
|
|
|
while(left_tail->_next)
|
|
|
|
left_tail = left_tail->_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the first item after the left list
|
|
|
|
left_tail->_next = first;
|
|
|
|
}else
|
|
|
|
left_head = first;
|
|
|
|
|
|
|
|
left_tail = first;
|
|
|
|
|
|
|
|
// sort the right sub-list
|
|
|
|
if (right_tail){
|
|
|
|
right_tail->_next=NULL;
|
|
|
|
|
|
|
|
if (!right_already_sorted)
|
|
|
|
right_head = sort(right_head);
|
|
|
|
left_tail->_next = right_head;
|
|
|
|
}
|
|
|
|
|
|
|
|
return left_head;
|
|
|
|
}
|
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
int fd_clearstats()
|
|
|
|
{
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total *stats = stats_head;
|
2012-07-02 03:49:54 +00:00
|
|
|
while(stats!=NULL){
|
|
|
|
fd_clearstat(stats);
|
|
|
|
stats = stats->_next;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fd_showstats()
|
|
|
|
{
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total total={NULL, 0, "Total", 0,0,0};
|
2012-07-02 03:49:54 +00:00
|
|
|
|
2012-07-02 04:41:37 +00:00
|
|
|
stats_head = sort(stats_head);
|
|
|
|
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total *stats = stats_head;
|
2012-07-02 03:49:54 +00:00
|
|
|
while(stats!=NULL){
|
|
|
|
/* Get total time spent doing everything */
|
|
|
|
fd_tallystats(&total,stats);
|
|
|
|
stats = stats->_next;
|
|
|
|
}
|
2013-02-17 06:53:14 +00:00
|
|
|
|
|
|
|
// Show periodic rhizome transfer information, but only
|
|
|
|
// if there are some active rhizome transfers.
|
2013-04-11 05:54:13 +00:00
|
|
|
if (rhizome_active_fetch_count()!=0)
|
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
|
|
|
INFOF("Rhizome transfer progress: %"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64",%"PRIu64" (remaining %"PRIu64")",
|
2013-02-17 06:53:14 +00:00
|
|
|
rhizome_active_fetch_bytes_received(0),
|
|
|
|
rhizome_active_fetch_bytes_received(1),
|
|
|
|
rhizome_active_fetch_bytes_received(2),
|
|
|
|
rhizome_active_fetch_bytes_received(3),
|
2013-04-11 05:54:13 +00:00
|
|
|
rhizome_active_fetch_bytes_received(4),
|
|
|
|
rhizome_active_fetch_bytes_received(5),
|
|
|
|
rhizome_fetch_queue_bytes());
|
2013-02-17 06:53:14 +00:00
|
|
|
|
2013-02-15 20:21:33 +00:00
|
|
|
// Report any functions that take too much time
|
|
|
|
if (!config.debug.timing)
|
|
|
|
{
|
|
|
|
stats = stats_head;
|
|
|
|
while(stats!=NULL){
|
|
|
|
/* If a function spends more than 1 second in any
|
|
|
|
notionally 3 second period, then dob on it */
|
|
|
|
if (stats->total_time>1000
|
|
|
|
&&strcmp(stats->name,"Idle (in poll)"))
|
|
|
|
fd_showstat(&total,stats);
|
|
|
|
stats = stats->_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
INFOF("servald time usage stats:");
|
|
|
|
stats = stats_head;
|
|
|
|
while(stats!=NULL){
|
|
|
|
/* Get total time spent doing everything */
|
|
|
|
if (stats->calls)
|
|
|
|
fd_showstat(&total,stats);
|
|
|
|
stats = stats->_next;
|
|
|
|
}
|
|
|
|
fd_showstat(&total,&total);
|
2012-07-02 03:49:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void fd_periodicstats(struct sched_ent *alarm)
|
|
|
|
{
|
|
|
|
fd_showstats();
|
|
|
|
fd_clearstats();
|
2012-07-30 07:52:38 +00:00
|
|
|
alarm->alarm = gettime_ms()+3000;
|
2012-07-12 00:45:16 +00:00
|
|
|
alarm->deadline = alarm->alarm+1000;
|
2012-07-02 03:49:54 +00:00
|
|
|
schedule(alarm);
|
|
|
|
}
|
|
|
|
|
2013-07-31 05:21:24 +00:00
|
|
|
void dump_stack(int log_level)
|
2012-11-12 04:04:30 +00:00
|
|
|
{
|
2012-07-12 00:45:16 +00:00
|
|
|
struct call_stats *call = current_call;
|
|
|
|
while(call){
|
|
|
|
if (call->totals)
|
2013-07-31 05:21:24 +00:00
|
|
|
LOGF(log_level, "%s",call->totals->name);
|
2012-07-12 00:45:16 +00:00
|
|
|
call=call->prev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-12 04:04:30 +00:00
|
|
|
int fd_func_enter(struct __sourceloc __whence, struct call_stats *this_call)
|
2012-07-02 03:49:54 +00:00
|
|
|
{
|
2013-02-16 17:47:24 +00:00
|
|
|
if (config.debug.profiling)
|
|
|
|
DEBUGF("%s called from %s() %s:%d",
|
|
|
|
__FUNCTION__,__whence.function,__whence.file,__whence.line);
|
|
|
|
|
2012-07-30 07:52:38 +00:00
|
|
|
this_call->enter_time=gettime_ms();
|
2012-07-02 03:49:54 +00:00
|
|
|
this_call->child_time=0;
|
|
|
|
this_call->prev = current_call;
|
|
|
|
current_call = this_call;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-12 04:04:30 +00:00
|
|
|
int fd_func_exit(struct __sourceloc __whence, struct call_stats *this_call)
|
2012-07-02 03:49:54 +00:00
|
|
|
{
|
2012-11-12 04:04:30 +00:00
|
|
|
// If current_call does not match this_call, then all bets are off as to where it points. It
|
|
|
|
// probably points to somewhere on the stack (see the IN() macro) that has since been overwritten,
|
|
|
|
// so no sense in trying to print its contents in a diagnostic message; that would just cause
|
|
|
|
// a SEGV.
|
2013-02-16 17:47:24 +00:00
|
|
|
if (config.debug.profiling)
|
|
|
|
DEBUGF("%s called from %s() %s:%d",
|
|
|
|
__FUNCTION__,__whence.function,__whence.file,__whence.line);
|
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
if (current_call != this_call)
|
2012-11-12 04:04:30 +00:00
|
|
|
FATAL("performance timing stack trace corrupted");
|
2012-07-02 03:49:54 +00:00
|
|
|
|
2012-08-09 02:44:32 +00:00
|
|
|
time_ms_t now = gettime_ms();
|
|
|
|
time_ms_t elapsed = now - this_call->enter_time;
|
2012-07-02 03:49:54 +00:00
|
|
|
current_call = this_call->prev;
|
|
|
|
|
2012-07-12 00:45:16 +00:00
|
|
|
if (this_call->totals && !this_call->totals->_initialised){
|
|
|
|
this_call->totals->_initialised=1;
|
|
|
|
this_call->totals->_next = stats_head;
|
|
|
|
fd_clearstat(this_call->totals);
|
|
|
|
stats_head = this_call->totals;
|
2012-07-02 03:49:54 +00:00
|
|
|
}
|
|
|
|
|
2012-07-02 05:50:30 +00:00
|
|
|
if (current_call)
|
|
|
|
current_call->child_time+=elapsed;
|
|
|
|
|
2012-07-12 00:45:16 +00:00
|
|
|
elapsed-=this_call->child_time;
|
|
|
|
|
|
|
|
if (this_call->totals){
|
|
|
|
this_call->totals->total_time+=elapsed;
|
|
|
|
this_call->totals->child_time+=this_call->child_time;
|
|
|
|
this_call->totals->calls++;
|
|
|
|
|
|
|
|
if (elapsed>this_call->totals->max_time) this_call->totals->max_time=elapsed;
|
|
|
|
}
|
2012-07-02 05:50:30 +00:00
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
return 0;
|
|
|
|
}
|