diff --git a/src/Brofiler.cc b/src/Brofiler.cc new file mode 100644 index 0000000000..fc23f10a7d --- /dev/null +++ b/src/Brofiler.cc @@ -0,0 +1,82 @@ +#include +#include +#include +#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 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_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 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, 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); + } + diff --git a/src/Brofiler.h b/src/Brofiler.h new file mode 100644 index 0000000000..6ded906698 --- /dev/null +++ b/src/Brofiler.h @@ -0,0 +1,65 @@ +#ifndef BROFILER_H_ +#define BROFILER_H_ + +#include +#include +#include +#include + + +/** + * 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 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, 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_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 897acc9d37..f755895eae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -281,6 +281,7 @@ set(bro_SRCS BPF_Program.cc BroDoc.cc BroDocObj.cc + Brofiler.cc BroString.cc CCL.cc ChunkedIO.cc diff --git a/src/Stmt.cc b/src/Stmt.cc index 6a83940b3b..669dc5565e 100644 --- a/src/Stmt.cc +++ b/src/Stmt.cc @@ -258,6 +258,7 @@ static BroFile* print_stdout = 0; Val* PrintStmt::DoExec(val_list* vals, stmt_flow_type& /* flow */) const { + RegisterAccess(); if ( ! print_stdout ) print_stdout = new BroFile(stdout); diff --git a/src/Stmt.h b/src/Stmt.h index 8e3a4b4118..7c3b42609b 100644 --- a/src/Stmt.h +++ b/src/Stmt.h @@ -52,6 +52,7 @@ public: void RegisterAccess() const { last_access = network_time; access_count++; } void AccessStats(ODesc* d) const; + uint32 GetAccessCount() const { return access_count; } virtual void Describe(ODesc* d) const; diff --git a/src/main.cc b/src/main.cc index dfa46c3050..68ab8b14a5 100644 --- a/src/main.cc +++ b/src/main.cc @@ -47,9 +47,12 @@ extern "C" void OPENSSL_add_all_algorithms_conf(void); #include "ConnCompressor.h" #include "DPM.h" #include "BroDoc.h" +#include "Brofiler.h" #include "binpac_bro.h" +Brofiler brofiler; + #ifndef HAVE_STRSEP extern "C" { char* strsep(char**, const char*); @@ -260,6 +263,8 @@ void terminate_bro() terminating = true; + brofiler.WriteStats(); + EventHandlerPtr bro_done = internal_handler("bro_done"); if ( bro_done ) mgr.QueueEvent(bro_done, new val_list); @@ -335,6 +340,7 @@ static void bro_new_handler() int main(int argc, char** argv) { + brofiler.ReadStats(); bro_argc = argc; bro_argv = new char* [argc]; diff --git a/src/parse.y b/src/parse.y index 495931aae0..420e995ae1 100644 --- a/src/parse.y +++ b/src/parse.y @@ -80,10 +80,12 @@ #include "Reporter.h" #include "BroDoc.h" #include "BroDocObj.h" +#include "Brofiler.h" #include #include +extern Brofiler brofiler; extern BroDoc* current_reST_doc; extern int generate_documentation; extern std::list* reST_doc_comments; @@ -1330,93 +1332,111 @@ stmt: { set_location(@1, @3); $$ = new PrintStmt($2); + brofiler.stmts.push_back($$); } | TOK_EVENT event ';' { set_location(@1, @3); $$ = new EventStmt($2); + brofiler.stmts.push_back($$); } | TOK_IF '(' expr ')' stmt { set_location(@1, @4); $$ = new IfStmt($3, $5, new NullStmt()); + //brofiler.stmts.push_back($$); } | TOK_IF '(' expr ')' stmt TOK_ELSE stmt { set_location(@1, @4); $$ = new IfStmt($3, $5, $7); + //brofiler.stmts.push_back($$); } | TOK_SWITCH expr '{' case_list '}' { set_location(@1, @2); $$ = new SwitchStmt($2, $4); + //brofiler.stmts.push_back($$); } | for_head stmt - { $1->AsForStmt()->AddBody($2); } + { + $1->AsForStmt()->AddBody($2); + //brofiler.stmts.push_back($1); + } | TOK_NEXT ';' { set_location(@1, @2); $$ = new NextStmt; + brofiler.stmts.push_back($$); } | TOK_BREAK ';' { set_location(@1, @2); $$ = new BreakStmt; + brofiler.stmts.push_back($$); } | TOK_RETURN ';' { set_location(@1, @2); $$ = new ReturnStmt(0); + brofiler.stmts.push_back($$); } | TOK_RETURN expr ';' { set_location(@1, @2); $$ = new ReturnStmt($2); + brofiler.stmts.push_back($$); } | TOK_ADD expr ';' { set_location(@1, @3); $$ = new AddStmt($2); + brofiler.stmts.push_back($$); } | TOK_DELETE expr ';' { set_location(@1, @3); $$ = new DelStmt($2); + brofiler.stmts.push_back($$); } | TOK_LOCAL local_id opt_type init_class opt_init opt_attr ';' { set_location(@1, @7); $$ = 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 ';' { set_location(@1, @6); $$ = add_local($2, $3, $4, $5, $6, VAR_CONST); + brofiler.stmts.push_back($$); } | TOK_WHEN '(' expr ')' stmt { set_location(@3, @5); $$ = new WhenStmt($3, $5, 0, 0, false); + brofiler.stmts.push_back($$); } | TOK_WHEN '(' expr ')' stmt TOK_TIMEOUT expr '{' stmt_list '}' { set_location(@3, @8); $$ = new WhenStmt($3, $5, $9, $7, false); + brofiler.stmts.push_back($$); } @@ -1424,18 +1444,21 @@ stmt: { set_location(@4, @6); $$ = new WhenStmt($4, $6, 0, 0, true); + brofiler.stmts.push_back($$); } | TOK_RETURN TOK_WHEN '(' expr ')' stmt TOK_TIMEOUT expr '{' stmt_list '}' { set_location(@4, @9); $$ = new WhenStmt($4, $6, $10, $8, true); + brofiler.stmts.push_back($$); } | expr ';' { set_location(@1, @2); $$ = new ExprStmt($1); + brofiler.stmts.push_back($$); } | ';' diff --git a/src/util.cc b/src/util.cc index f81eff8f22..a48feb9828 100644 --- a/src/util.cc +++ b/src/util.cc @@ -345,6 +345,7 @@ template int atoi_n(int len, const char* s, const char** end, int base, // Instantiate the ones we need. template int atoi_n(int len, const char* s, const char** end, int base, int& result); template int atoi_n(int len, const char* s, const char** end, int base, int64_t& result); +template int atoi_n(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) {