zeek/src/Stats.cc
Jon Siwek 7e5115460c Fix three bugs with 'when' and 'return when' statements. Addresses #946
- 'when' statements were problematic when used in a function/event/hook
  that had local variables with an assigned function value.  This was
  because 'when' blocks operate on a clone of the frame and the cloning
  process serializes locals and the serialization of functions had an
  infinite cycle in it (ID -> BroFunc -> ID -> BroFunc ...).  The ID
  was only used for the function name and type information, so
  refactoring Func and subclasses to depend on those two things instead
  fixes the issue.

- 'return when' blocks, specifically, didn't work whenever execution
  of the containing function's body does another function call before
  reaching the 'return when' block, because of an assertion.  This was
  was due to logic in CallExpr::Eval always clearing the CallExpr
  associated with the Frame after doing the call, instead of restoring
  any previous CallExpr, which the code in Trigger::Eval expected to
  have available.

- An assert could be reached when the condition of a 'when' statement
  depended on checking the value of global state variables.  The assert
  in Trigger::QueueTrigger that checks that the Trigger isn't disabled
  would get hit because Trigger::Eval/Timeout disable themselves after
  running, but don't unregister themselves from the NotifierRegistry,
  which keeps calling QueueTrigger for every state access of the global.
2013-02-19 11:38:17 -06:00

543 lines
14 KiB
C++

#include "Conn.h"
#include "File.h"
#include "Event.h"
#include "NetVar.h"
#include "Sessions.h"
#include "Stats.h"
#include "Scope.h"
#include "cq.h"
#include "DNS_Mgr.h"
#include "Trigger.h"
#include "threading/Manager.h"
int killed_by_inactivity = 0;
uint64 tot_ack_events = 0;
uint64 tot_ack_bytes = 0;
uint64 tot_gap_events = 0;
uint64 tot_gap_bytes = 0;
class ProfileTimer : public Timer {
public:
ProfileTimer(double t, ProfileLogger* l, double i)
: Timer(t, TIMER_PROFILE)
{
logger = l;
interval = i;
}
void Dispatch(double t, int is_expire);
protected:
double interval;
ProfileLogger* logger;
};
void ProfileTimer::Dispatch(double t, int is_expire)
{
logger->Log();
// Reinstall timer.
if ( ! is_expire )
timer_mgr->Add(new ProfileTimer(network_time + interval,
logger, interval));
}
ProfileLogger::ProfileLogger(BroFile* arg_file, double interval)
: SegmentStatsReporter()
{
file = arg_file;
log_count = 0;
timer_mgr->Add(new ProfileTimer(1, this, interval));
}
ProfileLogger::~ProfileLogger()
{
file->Close();
}
void ProfileLogger::Log()
{
if ( terminating )
// Connections have been flushed already.
return;
file->Write(fmt("%.06f ------------------------\n", network_time));
// Do expensive profiling only occasionally.
bool expensive = false;
if ( expensive_profiling_multiple )
expensive = (++log_count) % expensive_profiling_multiple == 0;
// Memory information.
struct rusage r;
getrusage(RUSAGE_SELF, &r);
struct timeval tv_utime = r.ru_utime;
struct timeval tv_stime = r.ru_stime;
unsigned int total, malloced;
get_memory_usage(&total, &malloced);
static unsigned int first_total = 0;
static double first_rtime = 0;
static double first_utime = 0;
static double first_stime = 0;
double rtime = current_time();
double utime = double(tv_utime.tv_sec) + double(tv_utime.tv_usec) / 1e6;
double stime = double(tv_stime.tv_sec) + double(tv_stime.tv_usec) / 1e6;
if ( first_total == 0 )
{
first_total = total;
first_rtime = rtime;
first_utime = utime;
first_stime = stime;
file->Write(fmt("%.06f Command line: ", network_time ));
for ( int i = 0; i < bro_argc; i++ )
{
file->Write(bro_argv[i]);
file->Write(" ");
}
file->Write(fmt("\n%.06f ------------------------\n", network_time));
}
file->Write(fmt("%.06f Memory: total=%dK total_adj=%dK malloced: %dK\n",
network_time, total / 1024, (total - first_total) / 1024,
malloced / 1024));
file->Write(fmt("%.06f Run-time: user+sys=%.1f user=%.1f sys=%.1f real=%.1f\n",
network_time, (utime + stime) - (first_utime + first_stime),
utime - first_utime, stime - first_stime, rtime - first_rtime));
int conn_mem_use = expensive ? sessions->ConnectionMemoryUsage() : 0;
file->Write(fmt("%.06f Conns: total=%d current=%d/%d ext=%d mem=%dK avg=%.1f table=%dK connvals=%dK\n",
network_time,
Connection::TotalConnections(),
Connection::CurrentConnections(),
sessions->CurrentConnections(),
Connection::CurrentExternalConnections(),
conn_mem_use,
expensive ? (conn_mem_use / double(sessions->CurrentConnections())) : 0,
expensive ? sessions->MemoryAllocation() / 1024 : 0,
expensive ? sessions->ConnectionMemoryUsageConnVals() / 1024 : 0
));
SessionStats s;
sessions->GetStats(s);
file->Write(fmt("%.06f Conns: tcp=%d/%d udp=%d/%d icmp=%d/%d\n",
network_time,
s.num_TCP_conns, s.max_TCP_conns,
s.num_UDP_conns, s.max_UDP_conns,
s.num_ICMP_conns, s.max_ICMP_conns
));
sessions->tcp_stats.PrintStats(file,
fmt("%.06f TCP-States:", network_time));
// Alternatively, if you prefer more compact output...
/*
file->Write(fmt("%.8f TCP-States: I=%d S=%d SA=%d SR=%d E=%d EF=%d ER=%d F=%d P=%d\n",
network_time,
sessions->tcp_stats.StateInactive(),
sessions->tcp_stats.StateRequest(),
sessions->tcp_stats.StateSuccRequest(),
sessions->tcp_stats.StateRstRequest(),
sessions->tcp_stats.StateEstablished(),
sessions->tcp_stats.StateHalfClose(),
sessions->tcp_stats.StateHalfRst(),
sessions->tcp_stats.StateClosed(),
sessions->tcp_stats.StatePartial()
));
*/
file->Write(fmt("%.06f Connections expired due to inactivity: %d\n",
network_time, killed_by_inactivity));
file->Write(fmt("%.06f Total reassembler data: %dK\n", network_time,
Reassembler::TotalMemoryAllocation() / 1024));
// Signature engine.
if ( expensive && rule_matcher )
{
RuleMatcher::Stats stats;
rule_matcher->GetStats(&stats);
file->Write(fmt("%06f RuleMatcher: matchers=%d dfa_states=%d ncomputed=%d "
"mem=%dK avg_nfa_states=%d\n", network_time, stats.matchers,
stats.dfa_states, stats.computed, stats.mem / 1024, stats.avg_nfa_states));
}
file->Write(fmt("%.06f Timers: current=%d max=%d mem=%dK lag=%.2fs\n",
network_time,
timer_mgr->Size(), timer_mgr->PeakSize(),
int(cq_memory_allocation() +
(timer_mgr->Size() * padded_sizeof(ConnectionTimer))) / 1024,
network_time - timer_mgr->LastTimestamp()));
DNS_Mgr::Stats dstats;
dns_mgr->GetStats(&dstats);
file->Write(fmt("%.06f DNS_Mgr: requests=%lu succesful=%lu failed=%lu pending=%lu cached_hosts=%lu cached_addrs=%lu\n",
network_time,
dstats.requests, dstats.successful, dstats.failed, dstats.pending,
dstats.cached_hosts, dstats.cached_addresses));
Trigger::Stats tstats;
Trigger::GetStats(&tstats);
file->Write(fmt("%.06f Triggers: total=%lu pending=%lu\n", network_time, tstats.total, tstats.pending));
unsigned int* current_timers = TimerMgr::CurrentTimers();
for ( int i = 0; i < NUM_TIMER_TYPES; ++i )
{
if ( current_timers[i] )
file->Write(fmt("%.06f %s = %d\n", network_time,
timer_type_to_string((TimerType) i),
current_timers[i]));
}
file->Write(fmt("%0.6f Threads: current=%d\n", network_time, thread_mgr->NumThreads()));
const threading::Manager::msg_stats_list& thread_stats = thread_mgr->GetMsgThreadStats();
for ( threading::Manager::msg_stats_list::const_iterator i = thread_stats.begin();
i != thread_stats.end(); ++i )
{
threading::MsgThread::Stats s = i->second;
file->Write(fmt("%0.6f %-25s in=%" PRIu64 " out=%" PRIu64 " pending=%" PRIu64 "/%" PRIu64
" (#queue r/w: in=%" PRIu64 "/%" PRIu64 " out=%" PRIu64 "/%" PRIu64 ")"
"\n",
network_time,
i->first.c_str(),
s.sent_in, s.sent_out,
s.pending_in, s.pending_out,
s.queue_in_stats.num_reads, s.queue_in_stats.num_writes,
s.queue_out_stats.num_reads, s.queue_out_stats.num_writes
));
}
// Script-level state.
unsigned int size, mem = 0;
PDict(ID)* globals = global_scope()->Vars();
if ( expensive )
{
int total_table_entries = 0;
int total_table_rentries = 0;
file->Write(fmt("%.06f Global_sizes > 100k: %dK\n",
network_time, mem / 1024));
ID* id;
IterCookie* c = globals->InitForIteration();
while ( (id = globals->NextEntry(c)) )
// We don't show/count internal globals as they are always
// contained in some other global user-visible container.
if ( id->HasVal() && ! id->IsInternalGlobal() )
{
Val* v = id->ID_Val();
size = id->ID_Val()->MemoryAllocation();
mem += size;
bool print = false;
int entries = -1;
int rentries = -1;
if ( size > 100 * 1024 )
print = true;
if ( v->Type()->Tag() == TYPE_TABLE )
{
entries = v->AsTable()->Length();
total_table_entries += entries;
// ### 100 shouldn't be hardwired
// in here.
if ( entries >= 100 )
print = true;
rentries = v->AsTableVal()->RecursiveSize();
total_table_rentries += rentries;
if ( rentries >= 100 ) // ### or here
print = true;
}
if ( print )
{
file->Write(fmt("%.06f %s = %dK",
network_time, id->Name(),
size / 1024));
if ( entries >= 0 )
file->Write(fmt(" (%d/%d entries)\n",
entries, rentries));
else
file->Write("\n");
}
}
file->Write(fmt("%.06f Global_sizes total: %dK\n",
network_time, mem / 1024));
file->Write(fmt("%.06f Total number of table entries: %d/%d\n",
network_time,
total_table_entries, total_table_rentries));
}
// Create an event so that scripts can log their information, too.
// (and for consistency we dispatch it *now*)
if ( profiling_update )
{
val_list* vl = new val_list;
Ref(file);
vl->append(new Val(file));
vl->append(new Val(expensive, TYPE_BOOL));
mgr.Dispatch(new Event(profiling_update, vl));
}
}
void ProfileLogger::SegmentProfile(const char* name, const Location* loc,
double dtime, int dmem)
{
if ( name )
file->Write(fmt("%.06f segment-%s dt=%.06f dmem=%d\n",
network_time, name, dtime, dmem));
else if ( loc )
file->Write(fmt("%.06f segment-%s:%d dt=%.06f dmem=%d\n",
network_time,
loc->filename ? loc->filename : "nofile",
loc->first_line,
dtime, dmem));
else
file->Write(fmt("%.06f segment-XXX dt=%.06f dmem=%d\n",
network_time, dtime, dmem));
}
SampleLogger::SampleLogger()
{
static TableType* load_sample_info = 0;
if ( ! load_sample_info )
load_sample_info = internal_type("load_sample_info")->AsTableType();
load_samples = new TableVal(load_sample_info);
}
SampleLogger::~SampleLogger()
{
Unref(load_samples);
}
void SampleLogger::FunctionSeen(const Func* func)
{
load_samples->Assign(new StringVal(func->Name()), 0);
}
void SampleLogger::LocationSeen(const Location* loc)
{
load_samples->Assign(new StringVal(loc->filename), 0);
}
void SampleLogger::SegmentProfile(const char* /* name */,
const Location* /* loc */,
double dtime, int dmem)
{
val_list* vl = new val_list(2);
vl->append(load_samples->Ref());
vl->append(new IntervalVal(dtime, Seconds));
vl->append(new Val(dmem, TYPE_INT));
mgr.QueueEvent(load_sample, vl);
}
void SegmentProfiler::Init()
{
getrusage(RUSAGE_SELF, &initial_rusage);
}
void SegmentProfiler::Report()
{
struct rusage final_rusage;
getrusage(RUSAGE_SELF, &final_rusage);
double start_time =
double(initial_rusage.ru_utime.tv_sec) +
double(initial_rusage.ru_utime.tv_usec) / 1e6 +
double(initial_rusage.ru_stime.tv_sec) +
double(initial_rusage.ru_stime.tv_usec) / 1e6;
double stop_time =
double(final_rusage.ru_utime.tv_sec) +
double(final_rusage.ru_utime.tv_usec) / 1e6 +
double(final_rusage.ru_stime.tv_sec) +
double(final_rusage.ru_stime.tv_usec) / 1e6;
int start_mem = initial_rusage.ru_maxrss * 1024;
int stop_mem = initial_rusage.ru_maxrss * 1024;
double dtime = stop_time - start_time;
int dmem = stop_mem - start_mem;
reporter->SegmentProfile(name, loc, dtime, dmem);
}
TCPStateStats::TCPStateStats()
{
for ( int i = 0; i < TCP_ENDPOINT_RESET + 1; ++i )
for ( int j = 0; j < TCP_ENDPOINT_RESET + 1; ++j )
state_cnt[i][j] = 0;
}
void TCPStateStats::ChangeState(EndpointState o_prev, EndpointState o_now,
EndpointState r_prev, EndpointState r_now)
{
--state_cnt[o_prev][r_prev];
++state_cnt[o_now][r_now];
}
void TCPStateStats::FlipState(EndpointState orig, EndpointState resp)
{
--state_cnt[orig][resp];
++state_cnt[resp][orig];
}
unsigned int TCPStateStats::NumStatePartial() const
{
unsigned int sum = 0;
for ( int i = 0; i < TCP_ENDPOINT_RESET + 1; ++i )
{
sum += state_cnt[TCP_ENDPOINT_PARTIAL][i];
sum += state_cnt[i][TCP_ENDPOINT_PARTIAL];
}
return sum;
}
void TCPStateStats::PrintStats(BroFile* file, const char* prefix)
{
file->Write(prefix);
file->Write(" Inact. Syn. SA Part. Est. Fin. Rst.\n");
for ( int i = 0; i < TCP_ENDPOINT_RESET + 1; ++i )
{
file->Write(prefix);
switch ( i ) {
#define STATE_STRING(state, str) \
case state: \
file->Write(str); \
break;
STATE_STRING(TCP_ENDPOINT_INACTIVE, "Inact.");
STATE_STRING(TCP_ENDPOINT_SYN_SENT, "Syn. ");
STATE_STRING(TCP_ENDPOINT_SYN_ACK_SENT, "SA ");
STATE_STRING(TCP_ENDPOINT_PARTIAL, "Part. ");
STATE_STRING(TCP_ENDPOINT_ESTABLISHED, "Est. ");
STATE_STRING(TCP_ENDPOINT_CLOSED, "Fin. ");
STATE_STRING(TCP_ENDPOINT_RESET, "Rst. ");
}
file->Write(" ");
for ( int j = 0; j < TCP_ENDPOINT_RESET + 1; ++j )
{
unsigned int n = state_cnt[i][j];
if ( n > 0 )
{
char buf[32];
safe_snprintf(buf, sizeof(buf), "%-8d", state_cnt[i][j]);
file->Write(buf);
}
else
file->Write(" ");
}
file->Write("\n");
}
}
PacketProfiler::PacketProfiler(unsigned int mode, double freq,
BroFile* arg_file)
{
update_mode = mode;
update_freq = freq;
file = arg_file;
last_Utime = last_Stime = last_Rtime = 0.0;
last_timestamp = time = 0.0;
pkt_cnt = byte_cnt = 0;
file->Write("time dt npkts nbytes dRtime dUtime dStime dmem\n");
}
PacketProfiler::~PacketProfiler()
{
file->Close();
}
void PacketProfiler::ProfilePkt(double t, unsigned int bytes)
{
if ( last_timestamp == 0.0 )
{
struct rusage res;
struct timeval ptimestamp;
getrusage(RUSAGE_SELF, &res);
gettimeofday(&ptimestamp, 0);
get_memory_usage(&last_mem, 0);
last_Utime = res.ru_utime.tv_sec + res.ru_utime.tv_usec / 1e6;
last_Stime = res.ru_stime.tv_sec + res.ru_stime.tv_usec / 1e6;
last_Rtime = ptimestamp.tv_sec + ptimestamp.tv_usec / 1e6;
last_timestamp = t;
}
if ( (update_mode == MODE_TIME && t > last_timestamp+update_freq) ||
(update_mode == MODE_PACKET && double(pkt_cnt) > update_freq) ||
(update_mode == MODE_VOLUME && double(pkt_cnt) > update_freq) )
{
struct rusage res;
struct timeval ptimestamp;
getrusage(RUSAGE_SELF, &res);
gettimeofday(&ptimestamp, 0);
double curr_Utime =
res.ru_utime.tv_sec + res.ru_utime.tv_usec / 1e6;
double curr_Stime =
res.ru_stime.tv_sec + res.ru_stime.tv_usec / 1e6;
double curr_Rtime =
ptimestamp.tv_sec + ptimestamp.tv_usec / 1e6;
unsigned int curr_mem;
get_memory_usage(&curr_mem, 0);
file->Write(fmt("%.06f %.03f %d %d %.03f %.03f %.03f %d\n",
t, time-last_timestamp, pkt_cnt, byte_cnt,
curr_Rtime - last_Rtime,
curr_Utime - last_Utime,
curr_Stime - last_Stime,
curr_mem - last_mem));
last_Utime = curr_Utime;
last_Stime = curr_Stime;
last_Rtime = curr_Rtime;
last_mem = curr_mem;
last_timestamp = t;
pkt_cnt = 0;
byte_cnt = 0;
}
++pkt_cnt;
byte_cnt += bytes;
time = t;
}