mirror of
https://github.com/zeek/zeek.git
synced 2025-10-16 21:48:21 +00:00

- '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.
543 lines
14 KiB
C++
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;
|
|
}
|