Add simple profiling class to accumulate Stmt usage stats across runs.

Use the BROFILER_FILE environment variable to point to a file in
which Stmt usage statistics from Bro script-layer can be output.

This should be able to be used to check Bro script coverage that
that e.g. the entire test suite covers.
This commit is contained in:
Jon Siwek 2011-11-27 15:57:18 -06:00
parent c8839da069
commit 8f8290c852
8 changed files with 181 additions and 1 deletions

82
src/Brofiler.cc Normal file
View file

@ -0,0 +1,82 @@
#include <cstdio>
#include <utility>
#include <algorithm>
#include "Brofiler.h"
#include "util.h"
Brofiler::Brofiler()
: delim('\t')
{
}
Brofiler::~Brofiler()
{
}
void Brofiler::ReadStats()
{
char* bf = getenv("BROFILER_FILE");
if ( ! bf ) return;
FILE* f = fopen(bf, "r");
if ( ! f )
{
fprintf(stderr, "Failed to open Brofiler file '%s' for reading\n", bf);
return;
}
char line[16384];
string delimiter;
delimiter = delim;
while( fgets(line, sizeof(line), f) )
{
line[strlen(line) - 1] = 0; //remove newline
string cnt(strtok(line, delimiter.c_str()));
string location(strtok(0, delimiter.c_str()));
string desc(strtok(0, delimiter.c_str()));
pair<string, string> location_desc(location, desc);
uint64 count;
atoi_n(cnt.size(), cnt.c_str(), 0, 10, count);
usage_map[location_desc] = count;
}
fclose(f);
}
void Brofiler::WriteStats()
{
char* bf = getenv("BROFILER_FILE");
if ( ! bf ) return;
FILE* f = fopen(bf, "w");
if ( ! f )
{
fprintf(stderr, "Failed to open Brofiler file '%s' for writing\n", bf);
return;
}
for ( list<const Stmt*>::const_iterator it = stmts.begin();
it != stmts.end(); ++it )
{
ODesc location_info;
(*it)->GetLocationInfo()->Describe(&location_info);
ODesc desc_info;
(*it)->Describe(&desc_info);
string desc(desc_info.Description());
for_each(desc.begin(), desc.end(), canonicalize_desc());
pair<string, string> location_desc(location_info.Description(), desc);
if ( usage_map.find(location_desc) != usage_map.end() )
usage_map[location_desc] += (*it)->GetAccessCount();
else
usage_map[location_desc] = (*it)->GetAccessCount();
}
map<pair<string, string>, uint64 >::const_iterator it;
for ( it = usage_map.begin(); it != usage_map.end(); ++it )
{
fprintf(f, "%"PRIu64"%c%s%c%s\n", it->second, delim,
it->first.first.c_str(), delim, it->first.second.c_str());
}
fclose(f);
}

65
src/Brofiler.h Normal file
View file

@ -0,0 +1,65 @@
#ifndef BROFILER_H_
#define BROFILER_H_
#include <map>
#include <utility>
#include <list>
#include <Stmt.h>
/**
* A simple class for managing stats of Bro script coverage across Bro runs.
*/
class Brofiler {
public:
Brofiler();
virtual ~Brofiler();
/**
* Imports Bro script Stmt usage information from file pointed to by
* environment variable BROFILER_FILE.
*/
void ReadStats();
/**
* Combines usage stats from current run with any read from ReadStats(),
* then writes information to file pointed to by environment variable
* BROFILER_FILE.
*/
void WriteStats();
void SetDelim(char d) { delim = d; }
/**
* The current, global Brofiler instance creates this list at parse-time.
*/
list<const Stmt*> stmts;
private:
/**
*
* This maps Stmt location-desc pairs to the total number of times that
* Stmt has been executed. The map can be initialized from a file at
* startup time and modified at shutdown time before writing back
* to a file.
*/
map<pair<string, string>, uint64> usage_map;
/**
* The character to use to delimit Brofiler output files. Default is '\t'.
*/
char delim;
/**
* A canonicalization routine for Stmt descriptions containing characters
* that don't agree with the output format of Brofiler.
*/
struct canonicalize_desc {
void operator() (char& c)
{
if ( c == '\n' ) c = ' ';
}
};
};
#endif /* BROFILER_H_ */

View file

@ -281,6 +281,7 @@ set(bro_SRCS
BPF_Program.cc BPF_Program.cc
BroDoc.cc BroDoc.cc
BroDocObj.cc BroDocObj.cc
Brofiler.cc
BroString.cc BroString.cc
CCL.cc CCL.cc
ChunkedIO.cc ChunkedIO.cc

View file

@ -258,6 +258,7 @@ static BroFile* print_stdout = 0;
Val* PrintStmt::DoExec(val_list* vals, stmt_flow_type& /* flow */) const Val* PrintStmt::DoExec(val_list* vals, stmt_flow_type& /* flow */) const
{ {
RegisterAccess();
if ( ! print_stdout ) if ( ! print_stdout )
print_stdout = new BroFile(stdout); print_stdout = new BroFile(stdout);

View file

@ -52,6 +52,7 @@ public:
void RegisterAccess() const { last_access = network_time; access_count++; } void RegisterAccess() const { last_access = network_time; access_count++; }
void AccessStats(ODesc* d) const; void AccessStats(ODesc* d) const;
uint32 GetAccessCount() const { return access_count; }
virtual void Describe(ODesc* d) const; virtual void Describe(ODesc* d) const;

View file

@ -47,9 +47,12 @@ extern "C" void OPENSSL_add_all_algorithms_conf(void);
#include "ConnCompressor.h" #include "ConnCompressor.h"
#include "DPM.h" #include "DPM.h"
#include "BroDoc.h" #include "BroDoc.h"
#include "Brofiler.h"
#include "binpac_bro.h" #include "binpac_bro.h"
Brofiler brofiler;
#ifndef HAVE_STRSEP #ifndef HAVE_STRSEP
extern "C" { extern "C" {
char* strsep(char**, const char*); char* strsep(char**, const char*);
@ -260,6 +263,8 @@ void terminate_bro()
terminating = true; terminating = true;
brofiler.WriteStats();
EventHandlerPtr bro_done = internal_handler("bro_done"); EventHandlerPtr bro_done = internal_handler("bro_done");
if ( bro_done ) if ( bro_done )
mgr.QueueEvent(bro_done, new val_list); mgr.QueueEvent(bro_done, new val_list);
@ -335,6 +340,7 @@ static void bro_new_handler()
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
brofiler.ReadStats();
bro_argc = argc; bro_argc = argc;
bro_argv = new char* [argc]; bro_argv = new char* [argc];

View file

@ -80,10 +80,12 @@
#include "Reporter.h" #include "Reporter.h"
#include "BroDoc.h" #include "BroDoc.h"
#include "BroDocObj.h" #include "BroDocObj.h"
#include "Brofiler.h"
#include <list> #include <list>
#include <string> #include <string>
extern Brofiler brofiler;
extern BroDoc* current_reST_doc; extern BroDoc* current_reST_doc;
extern int generate_documentation; extern int generate_documentation;
extern std::list<std::string>* reST_doc_comments; extern std::list<std::string>* reST_doc_comments;
@ -1330,93 +1332,111 @@ stmt:
{ {
set_location(@1, @3); set_location(@1, @3);
$$ = new PrintStmt($2); $$ = new PrintStmt($2);
brofiler.stmts.push_back($$);
} }
| TOK_EVENT event ';' | TOK_EVENT event ';'
{ {
set_location(@1, @3); set_location(@1, @3);
$$ = new EventStmt($2); $$ = new EventStmt($2);
brofiler.stmts.push_back($$);
} }
| TOK_IF '(' expr ')' stmt | TOK_IF '(' expr ')' stmt
{ {
set_location(@1, @4); set_location(@1, @4);
$$ = new IfStmt($3, $5, new NullStmt()); $$ = new IfStmt($3, $5, new NullStmt());
//brofiler.stmts.push_back($$);
} }
| TOK_IF '(' expr ')' stmt TOK_ELSE stmt | TOK_IF '(' expr ')' stmt TOK_ELSE stmt
{ {
set_location(@1, @4); set_location(@1, @4);
$$ = new IfStmt($3, $5, $7); $$ = new IfStmt($3, $5, $7);
//brofiler.stmts.push_back($$);
} }
| TOK_SWITCH expr '{' case_list '}' | TOK_SWITCH expr '{' case_list '}'
{ {
set_location(@1, @2); set_location(@1, @2);
$$ = new SwitchStmt($2, $4); $$ = new SwitchStmt($2, $4);
//brofiler.stmts.push_back($$);
} }
| for_head stmt | for_head stmt
{ $1->AsForStmt()->AddBody($2); } {
$1->AsForStmt()->AddBody($2);
//brofiler.stmts.push_back($1);
}
| TOK_NEXT ';' | TOK_NEXT ';'
{ {
set_location(@1, @2); set_location(@1, @2);
$$ = new NextStmt; $$ = new NextStmt;
brofiler.stmts.push_back($$);
} }
| TOK_BREAK ';' | TOK_BREAK ';'
{ {
set_location(@1, @2); set_location(@1, @2);
$$ = new BreakStmt; $$ = new BreakStmt;
brofiler.stmts.push_back($$);
} }
| TOK_RETURN ';' | TOK_RETURN ';'
{ {
set_location(@1, @2); set_location(@1, @2);
$$ = new ReturnStmt(0); $$ = new ReturnStmt(0);
brofiler.stmts.push_back($$);
} }
| TOK_RETURN expr ';' | TOK_RETURN expr ';'
{ {
set_location(@1, @2); set_location(@1, @2);
$$ = new ReturnStmt($2); $$ = new ReturnStmt($2);
brofiler.stmts.push_back($$);
} }
| TOK_ADD expr ';' | TOK_ADD expr ';'
{ {
set_location(@1, @3); set_location(@1, @3);
$$ = new AddStmt($2); $$ = new AddStmt($2);
brofiler.stmts.push_back($$);
} }
| TOK_DELETE expr ';' | TOK_DELETE expr ';'
{ {
set_location(@1, @3); set_location(@1, @3);
$$ = new DelStmt($2); $$ = new DelStmt($2);
brofiler.stmts.push_back($$);
} }
| TOK_LOCAL local_id opt_type init_class opt_init opt_attr ';' | TOK_LOCAL local_id opt_type init_class opt_init opt_attr ';'
{ {
set_location(@1, @7); set_location(@1, @7);
$$ = add_local($2, $3, $4, $5, $6, VAR_REGULAR); $$ = add_local($2, $3, $4, $5, $6, VAR_REGULAR);
brofiler.stmts.push_back($$);
} }
| TOK_CONST local_id opt_type init_class opt_init opt_attr ';' | TOK_CONST local_id opt_type init_class opt_init opt_attr ';'
{ {
set_location(@1, @6); set_location(@1, @6);
$$ = add_local($2, $3, $4, $5, $6, VAR_CONST); $$ = add_local($2, $3, $4, $5, $6, VAR_CONST);
brofiler.stmts.push_back($$);
} }
| TOK_WHEN '(' expr ')' stmt | TOK_WHEN '(' expr ')' stmt
{ {
set_location(@3, @5); set_location(@3, @5);
$$ = new WhenStmt($3, $5, 0, 0, false); $$ = new WhenStmt($3, $5, 0, 0, false);
brofiler.stmts.push_back($$);
} }
| TOK_WHEN '(' expr ')' stmt TOK_TIMEOUT expr '{' stmt_list '}' | TOK_WHEN '(' expr ')' stmt TOK_TIMEOUT expr '{' stmt_list '}'
{ {
set_location(@3, @8); set_location(@3, @8);
$$ = new WhenStmt($3, $5, $9, $7, false); $$ = new WhenStmt($3, $5, $9, $7, false);
brofiler.stmts.push_back($$);
} }
@ -1424,18 +1444,21 @@ stmt:
{ {
set_location(@4, @6); set_location(@4, @6);
$$ = new WhenStmt($4, $6, 0, 0, true); $$ = new WhenStmt($4, $6, 0, 0, true);
brofiler.stmts.push_back($$);
} }
| TOK_RETURN TOK_WHEN '(' expr ')' stmt TOK_TIMEOUT expr '{' stmt_list '}' | TOK_RETURN TOK_WHEN '(' expr ')' stmt TOK_TIMEOUT expr '{' stmt_list '}'
{ {
set_location(@4, @9); set_location(@4, @9);
$$ = new WhenStmt($4, $6, $10, $8, true); $$ = new WhenStmt($4, $6, $10, $8, true);
brofiler.stmts.push_back($$);
} }
| expr ';' | expr ';'
{ {
set_location(@1, @2); set_location(@1, @2);
$$ = new ExprStmt($1); $$ = new ExprStmt($1);
brofiler.stmts.push_back($$);
} }
| ';' | ';'

View file

@ -345,6 +345,7 @@ template<class T> int atoi_n(int len, const char* s, const char** end, int base,
// Instantiate the ones we need. // Instantiate the ones we need.
template int atoi_n<int>(int len, const char* s, const char** end, int base, int& result); template int atoi_n<int>(int len, const char* s, const char** end, int base, int& result);
template int atoi_n<int64_t>(int len, const char* s, const char** end, int base, int64_t& result); template int atoi_n<int64_t>(int len, const char* s, const char** end, int base, int64_t& result);
template int atoi_n<uint64_t>(int len, const char* s, const char** end, int base, uint64_t& result);
char* uitoa_n(uint64 value, char* str, int n, int base, const char* prefix) char* uitoa_n(uint64 value, char* str, int n, int base, const char* prefix)
{ {