mirror of
https://github.com/zeek/zeek.git
synced 2025-10-06 08:38:20 +00:00
Merge remote-tracking branch 'remotes/origin/topic/robin/gh59-when'
Fixed a few small bugs - Modifiable had an uninitialized member and the Registry looped over a map while deleting elements from it. Fixes GH-319 * remotes/origin/topic/robin/gh59-when: Renaming src/StateAccess.{h,cc} to src/Notifier.{h,cc}. Clean up new code. Remove MutableVal class. Redo API for notifiers. Remove most of MutableVal (but not the class itelf yet) Remove enum Opcode. Remove StateAccess class. Redo NotfifierRegistry to no longer rely on StateAccess. Add new test for when-statement watching global variables. Couple of compile fixes.
This commit is contained in:
commit
b1be8abe4e
22 changed files with 365 additions and 1422 deletions
|
@ -302,7 +302,7 @@ set(bro_SRCS
|
||||||
Scope.cc
|
Scope.cc
|
||||||
SerializationFormat.cc
|
SerializationFormat.cc
|
||||||
Sessions.cc
|
Sessions.cc
|
||||||
StateAccess.cc
|
Notifier.cc
|
||||||
Stats.cc
|
Stats.cc
|
||||||
Stmt.cc
|
Stmt.cc
|
||||||
Tag.cc
|
Tag.cc
|
||||||
|
|
|
@ -12,7 +12,7 @@ DebugLogger debug_logger;
|
||||||
// Same order here as in DebugStream.
|
// Same order here as in DebugStream.
|
||||||
DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = {
|
DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = {
|
||||||
{ "serial", 0, false }, { "rules", 0, false },
|
{ "serial", 0, false }, { "rules", 0, false },
|
||||||
{ "state", 0, false }, {"string", 0, false },
|
{ "string", 0, false },
|
||||||
{ "notifiers", 0, false }, { "main-loop", 0, false },
|
{ "notifiers", 0, false }, { "main-loop", 0, false },
|
||||||
{ "dpd", 0, false }, { "tm", 0, false },
|
{ "dpd", 0, false }, { "tm", 0, false },
|
||||||
{ "logging", 0, false }, {"input", 0, false },
|
{ "logging", 0, false }, {"input", 0, false },
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
enum DebugStream {
|
enum DebugStream {
|
||||||
DBG_SERIAL, // Serialization
|
DBG_SERIAL, // Serialization
|
||||||
DBG_RULES, // Signature matching
|
DBG_RULES, // Signature matching
|
||||||
DBG_STATE, // StateAccess logging
|
|
||||||
DBG_STRING, // String code
|
DBG_STRING, // String code
|
||||||
DBG_NOTIFIERS, // Notifiers (see StateAccess.h)
|
DBG_NOTIFIERS, // Notifiers
|
||||||
DBG_MAINLOOP, // Main IOSource loop
|
DBG_MAINLOOP, // Main IOSource loop
|
||||||
DBG_ANALYZER, // Analyzer framework
|
DBG_ANALYZER, // Analyzer framework
|
||||||
DBG_TM, // Time-machine packet input via Brocolli
|
DBG_TM, // Time-machine packet input via Brocolli
|
||||||
|
|
34
src/Expr.cc
34
src/Expr.cc
|
@ -97,7 +97,7 @@ void Expr::EvalIntoAggregate(const BroType* /* t */, Val* /* aggr */,
|
||||||
Internal("Expr::EvalIntoAggregate called");
|
Internal("Expr::EvalIntoAggregate called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Expr::Assign(Frame* /* f */, Val* /* v */, Opcode /* op */)
|
void Expr::Assign(Frame* /* f */, Val* /* v */)
|
||||||
{
|
{
|
||||||
Internal("Expr::Assign called");
|
Internal("Expr::Assign called");
|
||||||
}
|
}
|
||||||
|
@ -261,10 +261,10 @@ Expr* NameExpr::MakeLvalue()
|
||||||
return new RefExpr(this);
|
return new RefExpr(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NameExpr::Assign(Frame* f, Val* v, Opcode op)
|
void NameExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
if ( id->IsGlobal() )
|
if ( id->IsGlobal() )
|
||||||
id->SetVal(v, op);
|
id->SetVal(v);
|
||||||
else
|
else
|
||||||
f->SetElement(id->Offset(), v);
|
f->SetElement(id->Offset(), v);
|
||||||
}
|
}
|
||||||
|
@ -1007,18 +1007,18 @@ Val* IncrExpr::Eval(Frame* f) const
|
||||||
if ( elt )
|
if ( elt )
|
||||||
{
|
{
|
||||||
Val* new_elt = DoSingleEval(f, elt);
|
Val* new_elt = DoSingleEval(f, elt);
|
||||||
v_vec->Assign(i, new_elt, OP_INCR);
|
v_vec->Assign(i, new_elt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
v_vec->Assign(i, 0, OP_INCR);
|
v_vec->Assign(i, 0);
|
||||||
}
|
}
|
||||||
op->Assign(f, v_vec, OP_INCR);
|
op->Assign(f, v_vec);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Val* old_v = v;
|
Val* old_v = v;
|
||||||
op->Assign(f, v = DoSingleEval(f, old_v), OP_INCR);
|
op->Assign(f, v = DoSingleEval(f, old_v));
|
||||||
Unref(old_v);
|
Unref(old_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2041,9 +2041,9 @@ Expr* RefExpr::MakeLvalue()
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RefExpr::Assign(Frame* f, Val* v, Opcode opcode)
|
void RefExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
op->Assign(f, v, opcode);
|
op->Assign(f, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssignExpr::AssignExpr(Expr* arg_op1, Expr* arg_op2, int arg_is_init,
|
AssignExpr::AssignExpr(Expr* arg_op1, Expr* arg_op2, int arg_is_init,
|
||||||
|
@ -2743,7 +2743,7 @@ Val* IndexExpr::Fold(Val* v1, Val* v2) const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
void IndexExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
if ( IsError() )
|
if ( IsError() )
|
||||||
return;
|
return;
|
||||||
|
@ -2783,7 +2783,7 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
||||||
for ( auto idx = 0u; idx < v_vect->Size(); idx++, first++ )
|
for ( auto idx = 0u; idx < v_vect->Size(); idx++, first++ )
|
||||||
v1_vect->Insert(first, v_vect->Lookup(idx)->Ref());
|
v1_vect->Insert(first, v_vect->Lookup(idx)->Ref());
|
||||||
}
|
}
|
||||||
else if ( ! v1_vect->Assign(v2, v, op) )
|
else if ( ! v1_vect->Assign(v2, v) )
|
||||||
{
|
{
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
|
@ -2803,7 +2803,7 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TYPE_TABLE:
|
case TYPE_TABLE:
|
||||||
if ( ! v1->AsTableVal()->Assign(v2, v, op) )
|
if ( ! v1->AsTableVal()->Assign(v2, v) )
|
||||||
{
|
{
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
|
@ -2906,7 +2906,7 @@ int FieldExpr::CanDel() const
|
||||||
return td->FindAttr(ATTR_DEFAULT) || td->FindAttr(ATTR_OPTIONAL);
|
return td->FindAttr(ATTR_DEFAULT) || td->FindAttr(ATTR_OPTIONAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldExpr::Assign(Frame* f, Val* v, Opcode opcode)
|
void FieldExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
if ( IsError() )
|
if ( IsError() )
|
||||||
return;
|
return;
|
||||||
|
@ -2915,14 +2915,14 @@ void FieldExpr::Assign(Frame* f, Val* v, Opcode opcode)
|
||||||
if ( op_v )
|
if ( op_v )
|
||||||
{
|
{
|
||||||
RecordVal* r = op_v->AsRecordVal();
|
RecordVal* r = op_v->AsRecordVal();
|
||||||
r->Assign(field, v, opcode);
|
r->Assign(field, v);
|
||||||
Unref(r);
|
Unref(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FieldExpr::Delete(Frame* f)
|
void FieldExpr::Delete(Frame* f)
|
||||||
{
|
{
|
||||||
Assign(f, 0, OP_ASSIGN_IDX);
|
Assign(f, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Val* FieldExpr::Fold(Val* v) const
|
Val* FieldExpr::Fold(Val* v) const
|
||||||
|
@ -4753,7 +4753,7 @@ Expr* ListExpr::MakeLvalue()
|
||||||
return new RefExpr(this);
|
return new RefExpr(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListExpr::Assign(Frame* f, Val* v, Opcode op)
|
void ListExpr::Assign(Frame* f, Val* v)
|
||||||
{
|
{
|
||||||
ListVal* lv = v->AsListVal();
|
ListVal* lv = v->AsListVal();
|
||||||
|
|
||||||
|
@ -4761,7 +4761,7 @@ void ListExpr::Assign(Frame* f, Val* v, Opcode op)
|
||||||
RuntimeError("mismatch in list lengths");
|
RuntimeError("mismatch in list lengths");
|
||||||
|
|
||||||
loop_over_list(exprs, i)
|
loop_over_list(exprs, i)
|
||||||
exprs[i]->Assign(f, (*lv->Vals())[i]->Ref(), op);
|
exprs[i]->Assign(f, (*lv->Vals())[i]->Ref());
|
||||||
|
|
||||||
Unref(lv);
|
Unref(lv);
|
||||||
}
|
}
|
||||||
|
|
12
src/Expr.h
12
src/Expr.h
|
@ -86,7 +86,7 @@ public:
|
||||||
const;
|
const;
|
||||||
|
|
||||||
// Assign to the given value, if appropriate.
|
// Assign to the given value, if appropriate.
|
||||||
virtual void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN);
|
virtual void Assign(Frame* f, Val* v);
|
||||||
|
|
||||||
// Returns the type corresponding to this expression interpreted
|
// Returns the type corresponding to this expression interpreted
|
||||||
// as an initialization. The type should be Unref()'d when done
|
// as an initialization. The type should be Unref()'d when done
|
||||||
|
@ -239,7 +239,7 @@ public:
|
||||||
ID* Id() const { return id; }
|
ID* Id() const { return id; }
|
||||||
|
|
||||||
Val* Eval(Frame* f) const override;
|
Val* Eval(Frame* f) const override;
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
int IsPure() const override;
|
int IsPure() const override;
|
||||||
|
|
||||||
|
@ -586,7 +586,7 @@ class RefExpr : public UnaryExpr {
|
||||||
public:
|
public:
|
||||||
explicit RefExpr(Expr* op);
|
explicit RefExpr(Expr* op);
|
||||||
|
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -639,7 +639,7 @@ public:
|
||||||
void Add(Frame* f) override;
|
void Add(Frame* f) override;
|
||||||
void Delete(Frame* f) override;
|
void Delete(Frame* f) override;
|
||||||
|
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
|
|
||||||
// Need to override Eval since it can take a vector arg but does
|
// Need to override Eval since it can take a vector arg but does
|
||||||
|
@ -671,7 +671,7 @@ public:
|
||||||
|
|
||||||
int CanDel() const override;
|
int CanDel() const override;
|
||||||
|
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
void Delete(Frame* f) override;
|
void Delete(Frame* f) override;
|
||||||
|
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
|
@ -991,7 +991,7 @@ public:
|
||||||
BroType* InitType() const override;
|
BroType* InitType() const override;
|
||||||
Val* InitVal(const BroType* t, Val* aggr) const override;
|
Val* InitVal(const BroType* t, Val* aggr) const override;
|
||||||
Expr* MakeLvalue() override;
|
Expr* MakeLvalue() override;
|
||||||
void Assign(Frame* f, Val* v, Opcode op = OP_ASSIGN) override;
|
void Assign(Frame* f, Val* v) override;
|
||||||
|
|
||||||
TraversalCode Traverse(TraversalCallback* cb) const override;
|
TraversalCode Traverse(TraversalCallback* cb) const override;
|
||||||
|
|
||||||
|
|
44
src/ID.cc
44
src/ID.cc
|
@ -59,34 +59,14 @@ void ID::ClearVal()
|
||||||
val = 0;
|
val = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ID::SetVal(Val* v, Opcode op, bool arg_weak_ref)
|
void ID::SetVal(Val* v, bool arg_weak_ref)
|
||||||
{
|
{
|
||||||
if ( op != OP_NONE )
|
|
||||||
{
|
|
||||||
MutableVal::Properties props = 0;
|
|
||||||
|
|
||||||
if ( attrs && attrs->FindAttr(ATTR_TRACKED) )
|
|
||||||
props |= MutableVal::TRACKED;
|
|
||||||
|
|
||||||
if ( props )
|
|
||||||
{
|
|
||||||
if ( v->IsMutableVal() )
|
|
||||||
v->AsMutableVal()->AddProperties(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef DEBUG
|
|
||||||
if ( props )
|
|
||||||
#else
|
|
||||||
if ( debug_logger.IsVerbose() || props )
|
|
||||||
#endif
|
|
||||||
StateAccess::Log(new StateAccess(op, this, v, val));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! weak_ref )
|
if ( ! weak_ref )
|
||||||
Unref(val);
|
Unref(val);
|
||||||
|
|
||||||
val = v;
|
val = v;
|
||||||
weak_ref = arg_weak_ref;
|
weak_ref = arg_weak_ref;
|
||||||
|
Modified();
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
UpdateValID();
|
UpdateValID();
|
||||||
|
@ -175,16 +155,6 @@ void ID::UpdateValAttrs()
|
||||||
if ( ! attrs )
|
if ( ! attrs )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MutableVal::Properties props = 0;
|
|
||||||
|
|
||||||
if ( val && val->IsMutableVal() )
|
|
||||||
{
|
|
||||||
if ( attrs->FindAttr(ATTR_TRACKED) )
|
|
||||||
props |= MutableVal::TRACKED;
|
|
||||||
|
|
||||||
val->AsMutableVal()->AddProperties(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( val && val->Type()->Tag() == TYPE_TABLE )
|
if ( val && val->Type()->Tag() == TYPE_TABLE )
|
||||||
val->AsTableVal()->SetAttrs(attrs);
|
val->AsTableVal()->SetAttrs(attrs);
|
||||||
|
|
||||||
|
@ -242,16 +212,6 @@ void ID::RemoveAttr(attr_tag a)
|
||||||
{
|
{
|
||||||
if ( attrs )
|
if ( attrs )
|
||||||
attrs->RemoveAttr(a);
|
attrs->RemoveAttr(a);
|
||||||
|
|
||||||
if ( val && val->IsMutableVal() )
|
|
||||||
{
|
|
||||||
MutableVal::Properties props = 0;
|
|
||||||
|
|
||||||
if ( a == ATTR_TRACKED )
|
|
||||||
props |= MutableVal::TRACKED;
|
|
||||||
|
|
||||||
val->AsMutableVal()->RemoveProperties(props);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ID::SetOption()
|
void ID::SetOption()
|
||||||
|
|
10
src/ID.h
10
src/ID.h
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "Type.h"
|
#include "Type.h"
|
||||||
#include "Attr.h"
|
#include "Attr.h"
|
||||||
#include "StateAccess.h"
|
#include "Notifier.h"
|
||||||
#include "TraverseTypes.h"
|
#include "TraverseTypes.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class Func;
|
||||||
typedef enum { INIT_NONE, INIT_FULL, INIT_EXTRA, INIT_REMOVE, } init_class;
|
typedef enum { INIT_NONE, INIT_FULL, INIT_EXTRA, INIT_REMOVE, } init_class;
|
||||||
typedef enum { SCOPE_FUNCTION, SCOPE_MODULE, SCOPE_GLOBAL } IDScope;
|
typedef enum { SCOPE_FUNCTION, SCOPE_MODULE, SCOPE_GLOBAL } IDScope;
|
||||||
|
|
||||||
class ID : public BroObj {
|
class ID : public BroObj, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
ID(const char* name, IDScope arg_scope, bool arg_is_export);
|
ID(const char* name, IDScope arg_scope, bool arg_is_export);
|
||||||
~ID() override;
|
~ID() override;
|
||||||
|
@ -46,7 +46,7 @@ public:
|
||||||
// reference to the Val, the Val will be destroyed (naturally,
|
// reference to the Val, the Val will be destroyed (naturally,
|
||||||
// you have to take care that it will not be accessed via
|
// you have to take care that it will not be accessed via
|
||||||
// the ID afterwards).
|
// the ID afterwards).
|
||||||
void SetVal(Val* v, Opcode op = OP_ASSIGN, bool weak_ref = false);
|
void SetVal(Val* v, bool weak_ref = false);
|
||||||
|
|
||||||
void SetVal(Val* v, init_class c);
|
void SetVal(Val* v, init_class c);
|
||||||
void SetVal(Expr* ev, init_class c);
|
void SetVal(Expr* ev, init_class c);
|
||||||
|
@ -70,10 +70,6 @@ public:
|
||||||
|
|
||||||
bool IsRedefinable() const { return FindAttr(ATTR_REDEF) != 0; }
|
bool IsRedefinable() const { return FindAttr(ATTR_REDEF) != 0; }
|
||||||
|
|
||||||
// Returns true if ID is one of those internal globally unique IDs
|
|
||||||
// to which MutableVals are bound (there name start with a '#').
|
|
||||||
bool IsInternalGlobal() const { return name && name[0] == '#'; }
|
|
||||||
|
|
||||||
void SetAttrs(Attributes* attr);
|
void SetAttrs(Attributes* attr);
|
||||||
void AddAttrs(Attributes* attr);
|
void AddAttrs(Attributes* attr);
|
||||||
void RemoveAttr(attr_tag a);
|
void RemoveAttr(attr_tag a);
|
||||||
|
|
72
src/Notifier.cc
Normal file
72
src/Notifier.cc
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
#include "DebugLogger.h"
|
||||||
|
#include "Notifier.h"
|
||||||
|
|
||||||
|
notifier::Registry notifier::registry;
|
||||||
|
|
||||||
|
notifier::Receiver::Receiver()
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "creating receiver %p", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier::Receiver::~Receiver()
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "deleting receiver %p", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier::Registry::~Registry()
|
||||||
|
{
|
||||||
|
while ( registrations.begin() != registrations.end() )
|
||||||
|
Unregister(registrations.begin()->first);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Register(Modifiable* m, notifier::Receiver* r)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "registering object %p for receiver %p", m, r);
|
||||||
|
|
||||||
|
registrations.insert({m, r});
|
||||||
|
++m->num_receivers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Unregister(Modifiable* m, notifier::Receiver* r)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "unregistering object %p from receiver %p", m, r);
|
||||||
|
|
||||||
|
auto x = registrations.equal_range(m);
|
||||||
|
for ( auto i = x.first; i != x.second; i++ )
|
||||||
|
{
|
||||||
|
if ( i->second == r )
|
||||||
|
{
|
||||||
|
--i->first->num_receivers;
|
||||||
|
registrations.erase(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Unregister(Modifiable* m)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "unregistering object %p from all notifiers", m);
|
||||||
|
|
||||||
|
auto x = registrations.equal_range(m);
|
||||||
|
for ( auto i = x.first; i != x.second; i++ )
|
||||||
|
--i->first->num_receivers;
|
||||||
|
|
||||||
|
registrations.erase(x.first, x.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifier::Registry::Modified(Modifiable* m)
|
||||||
|
{
|
||||||
|
DBG_LOG(DBG_NOTIFIERS, "object %p has been modified", m);
|
||||||
|
|
||||||
|
auto x = registrations.equal_range(m);
|
||||||
|
for ( auto i = x.first; i != x.second; i++ )
|
||||||
|
i->second->Modified(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifier::Modifiable::~Modifiable()
|
||||||
|
{
|
||||||
|
if ( num_receivers )
|
||||||
|
registry.Unregister(this);
|
||||||
|
}
|
116
src/Notifier.h
Normal file
116
src/Notifier.h
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
//
|
||||||
|
// A notification framework to inform interested parties of modifications to
|
||||||
|
// selected global objects. To get notified about a change, derive a class
|
||||||
|
// from notifier::Receiver and register the interesting objects with the
|
||||||
|
// notification::Registry.
|
||||||
|
|
||||||
|
#ifndef NOTIFIER_H
|
||||||
|
#define NOTIFIER_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
#include "DebugLogger.h"
|
||||||
|
|
||||||
|
namespace notifier {
|
||||||
|
|
||||||
|
class Modifiable;
|
||||||
|
|
||||||
|
/** Interface class for receivers of notifications. */
|
||||||
|
class Receiver {
|
||||||
|
public:
|
||||||
|
Receiver();
|
||||||
|
virtual ~Receiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback executed when a register object has been modified.
|
||||||
|
*
|
||||||
|
* @param m object that was modified
|
||||||
|
*/
|
||||||
|
virtual void Modified(Modifiable* m) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Singleton class tracking all notification requests globally. */
|
||||||
|
class Registry {
|
||||||
|
public:
|
||||||
|
~Registry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a receiver to be informed when a modifiable object has
|
||||||
|
* changed.
|
||||||
|
*
|
||||||
|
* @param m object to track. Does not take ownership, but the object
|
||||||
|
* will automatically unregister itself on destruction.
|
||||||
|
*
|
||||||
|
* @param r receiver to notify on changes. Does not take ownershop,
|
||||||
|
* the receiver must remain valid as long as the registration stays
|
||||||
|
* in place.
|
||||||
|
*/
|
||||||
|
void Register(Modifiable* m, Receiver* r);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels a receiver's request to be informed about an object's
|
||||||
|
* modification. The arguments to the method must match what was
|
||||||
|
* originally registered.
|
||||||
|
*
|
||||||
|
* @param m object to no loger track.
|
||||||
|
*
|
||||||
|
* @param r receiver to no longer notify.
|
||||||
|
*/
|
||||||
|
void Unregister(Modifiable* m, Receiver* Receiver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels any active receiver requests to be informed about a
|
||||||
|
* partilar object's modifications.
|
||||||
|
*
|
||||||
|
* @param m object to no loger track.
|
||||||
|
*/
|
||||||
|
void Unregister(Modifiable* m);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Modifiable;
|
||||||
|
|
||||||
|
// Inform all registered receivers of a modification to an object.
|
||||||
|
// Will be called from the object itself.
|
||||||
|
void Modified(Modifiable* m);
|
||||||
|
|
||||||
|
typedef std::unordered_multimap<Modifiable*, Receiver*> ModifiableMap;
|
||||||
|
ModifiableMap registrations;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton object tracking all global notification requests.
|
||||||
|
*/
|
||||||
|
extern Registry registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for objects that can trigger notifications to receivers when
|
||||||
|
* modified.
|
||||||
|
*/
|
||||||
|
class Modifiable {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Calling this method signals to all registered receivers that the
|
||||||
|
* object has been modified.
|
||||||
|
*/
|
||||||
|
void Modified()
|
||||||
|
{
|
||||||
|
if ( num_receivers )
|
||||||
|
registry.Modified(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class Registry;
|
||||||
|
|
||||||
|
virtual ~Modifiable();
|
||||||
|
|
||||||
|
// Number of currently registered receivers.
|
||||||
|
uint64 num_receivers = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
23
src/Op.h
23
src/Op.h
|
@ -1,23 +0,0 @@
|
||||||
// See the file "COPYING" in the main distribution directory for copyright.
|
|
||||||
|
|
||||||
#ifndef op_h
|
|
||||||
#define op_h
|
|
||||||
|
|
||||||
// BRO operations.
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
OP_INCR, OP_DECR, OP_NOT, OP_NEGATE,
|
|
||||||
OP_PLUS, OP_MINUS, OP_TIMES, OP_DIVIDE, OP_MOD,
|
|
||||||
OP_AND, OP_OR,
|
|
||||||
OP_LT, OP_LE, OP_EQ, OP_NE, OP_GE, OP_GT,
|
|
||||||
OP_MATCH,
|
|
||||||
OP_ASSIGN,
|
|
||||||
OP_INDEX, OP_FIELD,
|
|
||||||
OP_IN,
|
|
||||||
OP_LIST,
|
|
||||||
OP_CALL,
|
|
||||||
OP_SCHED,
|
|
||||||
OP_NAME, OP_CONST, OP_THIS
|
|
||||||
} BroOP;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,643 +0,0 @@
|
||||||
#include "Val.h"
|
|
||||||
#include "StateAccess.h"
|
|
||||||
#include "Event.h"
|
|
||||||
#include "NetVar.h"
|
|
||||||
#include "DebugLogger.h"
|
|
||||||
|
|
||||||
int StateAccess::replaying = 0;
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const MutableVal* arg_target, const Val* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.val = const_cast<MutableVal*>(arg_target);
|
|
||||||
target_type = TYPE_MVAL;
|
|
||||||
op1.val = const_cast<Val*>(arg_op1);
|
|
||||||
op1_type = TYPE_VAL;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = false;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const ID* arg_target, const Val* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.id = const_cast<ID*>(arg_target);
|
|
||||||
target_type = TYPE_ID;
|
|
||||||
op1.val = const_cast<Val*>(arg_op1);
|
|
||||||
op1_type = TYPE_VAL;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = false;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const ID* arg_target, const HashKey* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.id = const_cast<ID*>(arg_target);
|
|
||||||
target_type = TYPE_ID;
|
|
||||||
op1.key = new HashKey(arg_op1->Key(), arg_op1->Size(), arg_op1->Hash());
|
|
||||||
op1_type = TYPE_KEY;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = true;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(Opcode arg_opcode,
|
|
||||||
const MutableVal* arg_target, const HashKey* arg_op1,
|
|
||||||
const Val* arg_op2, const Val* arg_op3)
|
|
||||||
{
|
|
||||||
opcode = arg_opcode;
|
|
||||||
target.val = const_cast<MutableVal*>(arg_target);
|
|
||||||
target_type = TYPE_MVAL;
|
|
||||||
op1.key = new HashKey(arg_op1->Key(), arg_op1->Size(), arg_op1->Hash());
|
|
||||||
op1_type = TYPE_KEY;
|
|
||||||
op2 = const_cast<Val*>(arg_op2);
|
|
||||||
op3 = const_cast<Val*>(arg_op3);
|
|
||||||
delete_op1_key = true;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::StateAccess(const StateAccess& sa)
|
|
||||||
{
|
|
||||||
opcode = sa.opcode;
|
|
||||||
target_type = sa.target_type;
|
|
||||||
op1_type = sa.op1_type;
|
|
||||||
delete_op1_key = false;
|
|
||||||
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
target.id = sa.target.id;
|
|
||||||
else
|
|
||||||
target.val = sa.target.val;
|
|
||||||
|
|
||||||
if ( op1_type == TYPE_VAL )
|
|
||||||
op1.val = sa.op1.val;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We need to copy the key as the pointer may not be
|
|
||||||
// valid anymore later.
|
|
||||||
op1.key = new HashKey(sa.op1.key->Key(), sa.op1.key->Size(),
|
|
||||||
sa.op1.key->Hash());
|
|
||||||
delete_op1_key = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
op2 = sa.op2;
|
|
||||||
op3 = sa.op3;
|
|
||||||
|
|
||||||
RefThem();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateAccess::~StateAccess()
|
|
||||||
{
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
Unref(target.id);
|
|
||||||
else
|
|
||||||
Unref(target.val);
|
|
||||||
|
|
||||||
if ( op1_type == TYPE_VAL )
|
|
||||||
Unref(op1.val);
|
|
||||||
else if ( delete_op1_key )
|
|
||||||
delete op1.key;
|
|
||||||
|
|
||||||
Unref(op2);
|
|
||||||
Unref(op3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::RefThem()
|
|
||||||
{
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
Ref(target.id);
|
|
||||||
else
|
|
||||||
Ref(target.val);
|
|
||||||
|
|
||||||
if ( op1_type == TYPE_VAL && op1.val )
|
|
||||||
Ref(op1.val);
|
|
||||||
|
|
||||||
if ( op2 )
|
|
||||||
Ref(op2);
|
|
||||||
if ( op3 )
|
|
||||||
Ref(op3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Val* GetInteger(bro_int_t n, TypeTag t)
|
|
||||||
{
|
|
||||||
if ( t == TYPE_INT )
|
|
||||||
return val_mgr->GetInt(n);
|
|
||||||
|
|
||||||
return val_mgr->GetCount(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::Replay()
|
|
||||||
{
|
|
||||||
// For simplicity we assume that we only replay unserialized accesses.
|
|
||||||
assert(target_type == TYPE_ID && op1_type == TYPE_VAL);
|
|
||||||
|
|
||||||
if ( ! target.id )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Val* v = target.id->ID_Val();
|
|
||||||
TypeTag t = v ? v->Type()->Tag() : TYPE_VOID;
|
|
||||||
|
|
||||||
if ( opcode != OP_ASSIGN && ! v )
|
|
||||||
{
|
|
||||||
// FIXME: I think this warrants an internal error,
|
|
||||||
// but let's check that first ...
|
|
||||||
// reporter->InternalError("replay id lacking a value");
|
|
||||||
reporter->Error("replay id lacks a value");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
++replaying;
|
|
||||||
|
|
||||||
switch ( opcode ) {
|
|
||||||
case OP_ASSIGN:
|
|
||||||
assert(op1.val);
|
|
||||||
// There mustn't be a direct assignment to a unique ID.
|
|
||||||
assert(target.id->Name()[0] != '#');
|
|
||||||
|
|
||||||
target.id->SetVal(op1.val->Ref());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR:
|
|
||||||
if ( IsIntegral(t) )
|
|
||||||
{
|
|
||||||
assert(op1.val && op2);
|
|
||||||
// We derive the amount as difference between old
|
|
||||||
// and new value.
|
|
||||||
bro_int_t amount =
|
|
||||||
op1.val->CoerceToInt() - op2->CoerceToInt();
|
|
||||||
|
|
||||||
target.id->SetVal(GetInteger(v->CoerceToInt() + amount, t),
|
|
||||||
OP_INCR);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_ASSIGN_IDX:
|
|
||||||
assert(op1.val);
|
|
||||||
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
assert(op2);
|
|
||||||
v->AsTableVal()->Assign(op1.val, op2 ? op2->Ref() : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_RECORD )
|
|
||||||
{
|
|
||||||
const char* field = op1.val->AsString()->CheckString();
|
|
||||||
int idx = v->Type()->AsRecordType()->FieldOffset(field);
|
|
||||||
|
|
||||||
if ( idx >= 0 )
|
|
||||||
v->AsRecordVal()->Assign(idx, op2 ? op2->Ref() : 0);
|
|
||||||
else
|
|
||||||
reporter->Error("access replay: unknown record field %s for assign", field);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_VECTOR )
|
|
||||||
{
|
|
||||||
assert(op2);
|
|
||||||
bro_uint_t index = op1.val->AsCount();
|
|
||||||
v->AsVectorVal()->Assign(index, op2 ? op2->Ref() : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
reporter->InternalError("unknown type in replaying index assign");
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR_IDX:
|
|
||||||
{
|
|
||||||
assert(op1.val && op2 && op3);
|
|
||||||
|
|
||||||
// We derive the amount as the difference between old
|
|
||||||
// and new value.
|
|
||||||
bro_int_t amount = op2->CoerceToInt() - op3->CoerceToInt();
|
|
||||||
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
t = v->Type()->AsTableType()->YieldType()->Tag();
|
|
||||||
Val* lookup_op1 = v->AsTableVal()->Lookup(op1.val);
|
|
||||||
int delta = lookup_op1->CoerceToInt() + amount;
|
|
||||||
Val* new_val = GetInteger(delta, t);
|
|
||||||
v->AsTableVal()->Assign(op1.val, new_val, OP_INCR );
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_RECORD )
|
|
||||||
{
|
|
||||||
const char* field = op1.val->AsString()->CheckString();
|
|
||||||
int idx = v->Type()->AsRecordType()->FieldOffset(field);
|
|
||||||
if ( idx >= 0 )
|
|
||||||
{
|
|
||||||
t = v->Type()->AsRecordType()->FieldType(idx)->Tag();
|
|
||||||
Val* lookup_field =
|
|
||||||
v->AsRecordVal()->Lookup(idx);
|
|
||||||
bro_int_t delta =
|
|
||||||
lookup_field->CoerceToInt() + amount;
|
|
||||||
Val* new_val = GetInteger(delta, t);
|
|
||||||
v->AsRecordVal()->Assign(idx, new_val, OP_INCR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
reporter->Error("access replay: unknown record field %s for assign", field);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ( t == TYPE_VECTOR )
|
|
||||||
{
|
|
||||||
bro_uint_t index = op1.val->AsCount();
|
|
||||||
t = v->Type()->AsVectorType()->YieldType()->Tag();
|
|
||||||
Val* lookup_op1 = v->AsVectorVal()->Lookup(index);
|
|
||||||
int delta = lookup_op1->CoerceToInt() + amount;
|
|
||||||
Val* new_val = GetInteger(delta, t);
|
|
||||||
v->AsVectorVal()->Assign(index, new_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
reporter->InternalError("unknown type in replaying index increment");
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case OP_ADD:
|
|
||||||
assert(op1.val);
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
v->AsTableVal()->Assign(op1.val, 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_DEL:
|
|
||||||
assert(op1.val);
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
Unref(v->AsTableVal()->Delete(op1.val));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_EXPIRE:
|
|
||||||
assert(op1.val);
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
// No old check for expire. It may have already
|
|
||||||
// been deleted by ourselves. Furthermore, we
|
|
||||||
// ignore the expire_func's return value.
|
|
||||||
TableVal* tv = v->AsTableVal();
|
|
||||||
if ( tv->Lookup(op1.val, false) )
|
|
||||||
{
|
|
||||||
// We want to propagate state updates which
|
|
||||||
// are performed in the expire_func.
|
|
||||||
StateAccess::ResumeReplay();
|
|
||||||
|
|
||||||
tv->CallExpireFunc(op1.val->Ref());
|
|
||||||
|
|
||||||
StateAccess::SuspendReplay();
|
|
||||||
|
|
||||||
Unref(tv->AsTableVal()->Delete(op1.val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_PRINT:
|
|
||||||
assert(op1.val);
|
|
||||||
reporter->InternalError("access replay for print not implemented");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_READ_IDX:
|
|
||||||
if ( t == TYPE_TABLE )
|
|
||||||
{
|
|
||||||
assert(op1.val);
|
|
||||||
TableVal* tv = v->AsTableVal();
|
|
||||||
|
|
||||||
// Update the timestamp if we have a read_expire.
|
|
||||||
if ( tv->FindAttr(ATTR_EXPIRE_READ) )
|
|
||||||
{
|
|
||||||
tv->UpdateTimestamp(op1.val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
reporter->Error("read for non-table");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
reporter->InternalError("access replay: unknown opcode for StateAccess");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
--replaying;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID* StateAccess::Target() const
|
|
||||||
{
|
|
||||||
return target_type == TYPE_ID ? target.id : target.val->UniqueID();
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::Describe(ODesc* d) const
|
|
||||||
{
|
|
||||||
const ID* id;
|
|
||||||
const char* id_str = "";
|
|
||||||
const char* unique_str = "";
|
|
||||||
|
|
||||||
d->SetShort();
|
|
||||||
|
|
||||||
if ( target_type == TYPE_ID )
|
|
||||||
{
|
|
||||||
id = target.id;
|
|
||||||
|
|
||||||
if ( ! id )
|
|
||||||
{
|
|
||||||
d->Add("(unknown id)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
id_str = id->Name();
|
|
||||||
|
|
||||||
if ( id->ID_Val() && id->ID_Val()->IsMutableVal() &&
|
|
||||||
id->Name()[0] != '#' )
|
|
||||||
unique_str = fmt(" [id] (%s)", id->ID_Val()->AsMutableVal()->UniqueID()->Name());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
id = target.val->UniqueID();
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
if ( target.val->GetID() )
|
|
||||||
{
|
|
||||||
id_str = target.val->GetID()->Name();
|
|
||||||
unique_str = fmt(" [val] (%s)", id->Name());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
id_str = id->Name();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Val* op1 = op1_type == TYPE_VAL ?
|
|
||||||
this->op1.val :
|
|
||||||
id->ID_Val()->AsTableVal()->RecoverIndex(this->op1.key);
|
|
||||||
|
|
||||||
switch ( opcode ) {
|
|
||||||
case OP_ASSIGN:
|
|
||||||
assert(op1);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add(" = ");
|
|
||||||
op1->Describe(d);
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR:
|
|
||||||
assert(op1 && op2);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add(" += ");
|
|
||||||
d->Add(op1->CoerceToInt() - op2->CoerceToInt());
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_ASSIGN_IDX:
|
|
||||||
assert(op1);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
d->Add(" = ");
|
|
||||||
if ( op2 )
|
|
||||||
op2->Describe(d);
|
|
||||||
else
|
|
||||||
d->Add("(null)");
|
|
||||||
if ( op3 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op3->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_INCR_IDX:
|
|
||||||
assert(op1 && op2 && op3);
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
d->Add(" += ");
|
|
||||||
d->Add(op2->CoerceToInt() - op3->CoerceToInt());
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_ADD:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("add ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_DEL:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("del ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_EXPIRE:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("expire ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
if ( op2 )
|
|
||||||
{
|
|
||||||
d->Add(" (");
|
|
||||||
op2->Describe(d);
|
|
||||||
d->Add(")");
|
|
||||||
}
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_PRINT:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("print ");
|
|
||||||
d->Add(id_str);
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add(unique_str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_READ_IDX:
|
|
||||||
assert(op1);
|
|
||||||
d->Add("read ");
|
|
||||||
d->Add(id_str);
|
|
||||||
d->Add("[");
|
|
||||||
op1->Describe(d);
|
|
||||||
d->Add("]");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
reporter->InternalError("unknown opcode for StateAccess");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( op1_type != TYPE_VAL )
|
|
||||||
Unref(const_cast<Val*>(op1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void StateAccess::Log(StateAccess* access)
|
|
||||||
{
|
|
||||||
bool tracked = false;
|
|
||||||
|
|
||||||
if ( access->target_type == TYPE_ID )
|
|
||||||
{
|
|
||||||
if ( access->target.id->FindAttr(ATTR_TRACKED) )
|
|
||||||
tracked = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( access->target.val->GetProperties() & MutableVal::TRACKED )
|
|
||||||
tracked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( tracked )
|
|
||||||
notifiers.AccessPerformed(*access);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
ODesc desc;
|
|
||||||
access->Describe(&desc);
|
|
||||||
DBG_LOG(DBG_STATE, "operation: %s%s",
|
|
||||||
desc.Description(), replaying > 0 ? " (replay)" : "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
delete access;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
NotifierRegistry notifiers;
|
|
||||||
|
|
||||||
void NotifierRegistry::Register(ID* id, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
DBG_LOG(DBG_NOTIFIERS, "registering ID %s for notifier %s",
|
|
||||||
id->Name(), notifier->Name());
|
|
||||||
|
|
||||||
Attr* attr = new Attr(ATTR_TRACKED);
|
|
||||||
|
|
||||||
if ( id->Attrs() )
|
|
||||||
{
|
|
||||||
if ( ! id->Attrs()->FindAttr(ATTR_TRACKED) )
|
|
||||||
id->Attrs()->AddAttr(attr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
attr_list* a = new attr_list{attr};
|
|
||||||
id->SetAttrs(new Attributes(a, id->Type(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(attr);
|
|
||||||
|
|
||||||
NotifierMap::iterator i = ids.find(id->Name());
|
|
||||||
|
|
||||||
if ( i != ids.end() )
|
|
||||||
i->second->insert(notifier);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
NotifierSet* s = new NotifierSet;
|
|
||||||
s->insert(notifier);
|
|
||||||
ids.insert(NotifierMap::value_type(id->Name(), s));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::Register(Val* val, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
if ( val->IsMutableVal() )
|
|
||||||
Register(val->AsMutableVal()->UniqueID(), notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::Unregister(ID* id, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
DBG_LOG(DBG_NOTIFIERS, "unregistering ID %s for notifier %s",
|
|
||||||
id->Name(), notifier->Name());
|
|
||||||
|
|
||||||
NotifierMap::iterator i = ids.find(id->Name());
|
|
||||||
|
|
||||||
if ( i == ids.end() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
Attr* attr = id->Attrs()->FindAttr(ATTR_TRACKED);
|
|
||||||
id->Attrs()->RemoveAttr(ATTR_TRACKED);
|
|
||||||
Unref(attr);
|
|
||||||
|
|
||||||
NotifierSet* s = i->second;
|
|
||||||
s->erase(notifier);
|
|
||||||
|
|
||||||
if ( s->size() == 0 )
|
|
||||||
{
|
|
||||||
delete s;
|
|
||||||
ids.erase(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::Unregister(Val* val, NotifierRegistry::Notifier* notifier)
|
|
||||||
{
|
|
||||||
if ( val->IsMutableVal() )
|
|
||||||
Unregister(val->AsMutableVal()->UniqueID(), notifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotifierRegistry::AccessPerformed(const StateAccess& sa)
|
|
||||||
{
|
|
||||||
ID* id = sa.Target();
|
|
||||||
|
|
||||||
NotifierMap::iterator i = ids.find(id->Name());
|
|
||||||
|
|
||||||
if ( i == ids.end() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
DBG_LOG(DBG_NOTIFIERS, "modification to tracked ID %s", id->Name());
|
|
||||||
|
|
||||||
NotifierSet* s = i->second;
|
|
||||||
|
|
||||||
if ( id->IsInternalGlobal() )
|
|
||||||
for ( NotifierSet::iterator j = s->begin(); j != s->end(); j++ )
|
|
||||||
(*j)->Access(id->ID_Val(), sa);
|
|
||||||
else
|
|
||||||
for ( NotifierSet::iterator j = s->begin(); j != s->end(); j++ )
|
|
||||||
(*j)->Access(id, sa);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* NotifierRegistry::Notifier::Name() const
|
|
||||||
{
|
|
||||||
return fmt("%p", this);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
// A class describing a state-modyfing access to a Value or an ID.
|
|
||||||
|
|
||||||
#ifndef STATEACESSS_H
|
|
||||||
#define STATEACESSS_H
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class Val;
|
|
||||||
class ID;
|
|
||||||
class MutableVal;
|
|
||||||
class HashKey;
|
|
||||||
class ODesc;
|
|
||||||
class TableVal;
|
|
||||||
|
|
||||||
enum Opcode { // Op1 Op2 Op3 (Vals)
|
|
||||||
OP_NONE,
|
|
||||||
OP_ASSIGN, // new old
|
|
||||||
OP_ASSIGN_IDX, // idx new old
|
|
||||||
OP_ADD, // idx old
|
|
||||||
OP_INCR, // idx new old
|
|
||||||
OP_INCR_IDX, // idx new old
|
|
||||||
OP_DEL, // idx old
|
|
||||||
OP_PRINT, // args
|
|
||||||
OP_EXPIRE, // idx
|
|
||||||
OP_READ_IDX, // idx
|
|
||||||
};
|
|
||||||
|
|
||||||
class StateAccess {
|
|
||||||
public:
|
|
||||||
StateAccess(Opcode opcode, const ID* target, const Val* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
StateAccess(Opcode opcode, const MutableVal* target, const Val* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
|
|
||||||
// For tables, the idx operand may be given as an index HashKey.
|
|
||||||
// This is for efficiency. While we need to reconstruct the index
|
|
||||||
// if we are actually going to serialize the access, we can at
|
|
||||||
// least skip it if we don't.
|
|
||||||
StateAccess(Opcode opcode, const ID* target, const HashKey* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
StateAccess(Opcode opcode, const MutableVal* target, const HashKey* op1,
|
|
||||||
const Val* op2 = 0, const Val* op3 = 0);
|
|
||||||
|
|
||||||
StateAccess(const StateAccess& sa);
|
|
||||||
|
|
||||||
virtual ~StateAccess();
|
|
||||||
|
|
||||||
// Replays this access in the our environment.
|
|
||||||
void Replay();
|
|
||||||
|
|
||||||
// Returns target ID which may be an internal one for unbound vals.
|
|
||||||
ID* Target() const;
|
|
||||||
|
|
||||||
void Describe(ODesc* d) const;
|
|
||||||
|
|
||||||
// Main entry point when StateAcesses are performed.
|
|
||||||
// For every state-changing operation, this has to be called.
|
|
||||||
static void Log(StateAccess* access);
|
|
||||||
|
|
||||||
// If we're going to make additional non-replaying accesses during a
|
|
||||||
// Replay(), we have to call these.
|
|
||||||
static void SuspendReplay() { --replaying; }
|
|
||||||
static void ResumeReplay() { ++replaying; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
StateAccess() { target.id = 0; op1.val = op2 = op3 = 0; }
|
|
||||||
void RefThem();
|
|
||||||
|
|
||||||
Opcode opcode;
|
|
||||||
union {
|
|
||||||
ID* id;
|
|
||||||
MutableVal* val;
|
|
||||||
} target;
|
|
||||||
|
|
||||||
union {
|
|
||||||
Val* val;
|
|
||||||
const HashKey* key;
|
|
||||||
} op1;
|
|
||||||
|
|
||||||
Val* op2;
|
|
||||||
Val* op3;
|
|
||||||
|
|
||||||
enum Type { TYPE_ID, TYPE_VAL, TYPE_MVAL, TYPE_KEY };
|
|
||||||
Type target_type;
|
|
||||||
Type op1_type;
|
|
||||||
bool delete_op1_key;
|
|
||||||
|
|
||||||
static int replaying;
|
|
||||||
};
|
|
||||||
|
|
||||||
// We provide a notifier framework to inform interested parties of
|
|
||||||
// modifications to selected global IDs/Vals. To get notified about a change,
|
|
||||||
// derive a class from Notifier and register the interesting IDs/Vals with
|
|
||||||
// the NotifierRegistry.
|
|
||||||
//
|
|
||||||
// Note: For containers (e.g., tables), notifications are only issued if the
|
|
||||||
// container itself is modified, *not* for changes to the values contained
|
|
||||||
// therein.
|
|
||||||
|
|
||||||
class NotifierRegistry {
|
|
||||||
public:
|
|
||||||
class Notifier {
|
|
||||||
public:
|
|
||||||
virtual ~Notifier() { }
|
|
||||||
|
|
||||||
// Called when a change is being performed. Note that when these
|
|
||||||
// methods are called, it is undefined whether the change has
|
|
||||||
// already been done or is just going to be performed soon.
|
|
||||||
virtual void Access(ID* id, const StateAccess& sa) = 0;
|
|
||||||
virtual void Access(Val* val, const StateAccess& sa) = 0;
|
|
||||||
virtual const char* Name() const; // for debugging
|
|
||||||
};
|
|
||||||
|
|
||||||
NotifierRegistry() { }
|
|
||||||
~NotifierRegistry() { }
|
|
||||||
|
|
||||||
// Inform the given notifier if ID/Val changes.
|
|
||||||
void Register(ID* id, Notifier* notifier);
|
|
||||||
void Register(Val* val, Notifier* notifier);
|
|
||||||
|
|
||||||
// Cancel notification for this ID/Val.
|
|
||||||
void Unregister(ID* id, Notifier* notifier);
|
|
||||||
void Unregister(Val* val, Notifier* notifier);
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class StateAccess;
|
|
||||||
void AccessPerformed(const StateAccess& sa);
|
|
||||||
|
|
||||||
typedef std::set<Notifier*> NotifierSet;
|
|
||||||
typedef std::map<std::string, NotifierSet*> NotifierMap;
|
|
||||||
NotifierMap ids;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern NotifierRegistry notifiers;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -255,7 +255,7 @@ void ProfileLogger::Log()
|
||||||
while ( (id = globals->NextEntry(c)) )
|
while ( (id = globals->NextEntry(c)) )
|
||||||
// We don't show/count internal globals as they are always
|
// We don't show/count internal globals as they are always
|
||||||
// contained in some other global user-visible container.
|
// contained in some other global user-visible container.
|
||||||
if ( id->HasVal() && ! id->IsInternalGlobal() )
|
if ( id->HasVal() )
|
||||||
{
|
{
|
||||||
Val* v = id->ID_Val();
|
Val* v = id->ID_Val();
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ TraversalCode TriggerTraversalCallback::PreExpr(const Expr* expr)
|
||||||
trigger->Register(e->Id());
|
trigger->Register(e->Id());
|
||||||
|
|
||||||
Val* v = e->Id()->ID_Val();
|
Val* v = e->Id()->ID_Val();
|
||||||
if ( v && v->IsMutableVal() )
|
if ( v && v->Modifiable() )
|
||||||
trigger->Register(v);
|
trigger->Register(v);
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
@ -382,38 +382,35 @@ void Trigger::Timeout()
|
||||||
void Trigger::Register(ID* id)
|
void Trigger::Register(ID* id)
|
||||||
{
|
{
|
||||||
assert(! disabled);
|
assert(! disabled);
|
||||||
notifiers.Register(id, this);
|
notifier::registry.Register(id, this);
|
||||||
|
|
||||||
Ref(id);
|
Ref(id);
|
||||||
ids.insert(id);
|
objs.push_back({id, id});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trigger::Register(Val* val)
|
void Trigger::Register(Val* val)
|
||||||
{
|
{
|
||||||
|
if ( ! val->Modifiable() )
|
||||||
|
return;
|
||||||
|
|
||||||
assert(! disabled);
|
assert(! disabled);
|
||||||
notifiers.Register(val, this);
|
notifier::registry.Register(val->Modifiable(), this);
|
||||||
|
|
||||||
Ref(val);
|
Ref(val);
|
||||||
vals.insert(val);
|
objs.emplace_back(val, val->Modifiable());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trigger::UnregisterAll()
|
void Trigger::UnregisterAll()
|
||||||
{
|
{
|
||||||
loop_over_list(ids, i)
|
DBG_LOG(DBG_NOTIFIERS, "%s: unregistering all", Name());
|
||||||
|
|
||||||
|
for ( const auto& o : objs )
|
||||||
{
|
{
|
||||||
notifiers.Unregister(ids[i], this);
|
notifier::registry.Unregister(o.second, this);
|
||||||
Unref(ids[i]);
|
Unref(o.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
ids.clear();
|
objs.clear();
|
||||||
|
|
||||||
loop_over_list(vals, j)
|
|
||||||
{
|
|
||||||
notifiers.Unregister(vals[j], this);
|
|
||||||
Unref(vals[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
vals.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Trigger::Attach(Trigger *trigger)
|
void Trigger::Attach(Trigger *trigger)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "StateAccess.h"
|
#include "Notifier.h"
|
||||||
#include "Traverse.h"
|
#include "Traverse.h"
|
||||||
|
|
||||||
// Triggers are the heart of "when" statements: expressions that when
|
// Triggers are the heart of "when" statements: expressions that when
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
class TriggerTimer;
|
class TriggerTimer;
|
||||||
class TriggerTraversalCallback;
|
class TriggerTraversalCallback;
|
||||||
|
|
||||||
class Trigger : public NotifierRegistry::Notifier, public BroObj {
|
class Trigger : public BroObj, public notifier::Receiver {
|
||||||
public:
|
public:
|
||||||
// Don't access Trigger objects; they take care of themselves after
|
// Don't access Trigger objects; they take care of themselves after
|
||||||
// instantiation. Note that if the condition is already true, the
|
// instantiation. Note that if the condition is already true, the
|
||||||
|
@ -61,12 +61,10 @@ public:
|
||||||
{ d->Add("<trigger>"); }
|
{ d->Add("<trigger>"); }
|
||||||
// Overidden from Notifier. We queue the trigger and evaluate it
|
// Overidden from Notifier. We queue the trigger and evaluate it
|
||||||
// later to avoid race conditions.
|
// later to avoid race conditions.
|
||||||
void Access(ID* id, const StateAccess& sa) override
|
void Modified(notifier::Modifiable* m) override
|
||||||
{ QueueTrigger(this); }
|
|
||||||
void Access(Val* val, const StateAccess& sa) override
|
|
||||||
{ QueueTrigger(this); }
|
{ QueueTrigger(this); }
|
||||||
|
|
||||||
const char* Name() const override;
|
const char* Name() const;
|
||||||
|
|
||||||
static void QueueTrigger(Trigger* trigger);
|
static void QueueTrigger(Trigger* trigger);
|
||||||
|
|
||||||
|
@ -104,8 +102,7 @@ private:
|
||||||
bool delayed; // true if a function call is currently being delayed
|
bool delayed; // true if a function call is currently being delayed
|
||||||
bool disabled;
|
bool disabled;
|
||||||
|
|
||||||
val_list vals;
|
std::vector<std::pair<BroObj *, notifier::Modifiable*>> objs;
|
||||||
id_list ids;
|
|
||||||
|
|
||||||
typedef map<const CallExpr*, Val*> ValCache;
|
typedef map<const CallExpr*, Val*> ValCache;
|
||||||
ValCache cache;
|
ValCache cache;
|
||||||
|
|
|
@ -695,10 +695,6 @@ bool is_atomic_type(const BroType* t);
|
||||||
// True if the given type tag corresponds to a function type.
|
// True if the given type tag corresponds to a function type.
|
||||||
#define IsFunc(t) (t == TYPE_FUNC)
|
#define IsFunc(t) (t == TYPE_FUNC)
|
||||||
|
|
||||||
// True if the given type tag corresponds to mutable type.
|
|
||||||
#define IsMutable(t) \
|
|
||||||
(t == TYPE_RECORD || t == TYPE_TABLE || t == TYPE_VECTOR)
|
|
||||||
|
|
||||||
// True if the given type type is a vector.
|
// True if the given type type is a vector.
|
||||||
#define IsVector(t) (t == TYPE_VECTOR)
|
#define IsVector(t) (t == TYPE_VECTOR)
|
||||||
|
|
||||||
|
|
401
src/Val.cc
401
src/Val.cc
|
@ -380,128 +380,6 @@ bool Val::WouldOverflow(const BroType* from_type, const BroType* to_type, const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
MutableVal::~MutableVal()
|
|
||||||
{
|
|
||||||
for ( list<ID*>::iterator i = aliases.begin(); i != aliases.end(); ++i )
|
|
||||||
{
|
|
||||||
if ( global_scope() )
|
|
||||||
global_scope()->Remove((*i)->Name());
|
|
||||||
(*i)->ClearVal(); // just to make sure.
|
|
||||||
Unref((*i));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( id )
|
|
||||||
{
|
|
||||||
if ( global_scope() )
|
|
||||||
global_scope()->Remove(id->Name());
|
|
||||||
id->ClearVal(); // just to make sure.
|
|
||||||
Unref(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MutableVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( (props | arg_props) == props )
|
|
||||||
// No change.
|
|
||||||
return false;
|
|
||||||
|
|
||||||
props |= arg_props;
|
|
||||||
|
|
||||||
if ( ! id )
|
|
||||||
Bind();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool MutableVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( (props & ~arg_props) == props )
|
|
||||||
// No change.
|
|
||||||
return false;
|
|
||||||
|
|
||||||
props &= ~arg_props;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ID* MutableVal::Bind() const
|
|
||||||
{
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
assert(!id);
|
|
||||||
|
|
||||||
static unsigned int id_counter = 0;
|
|
||||||
static const int MAX_NAME_SIZE = 128;
|
|
||||||
static char name[MAX_NAME_SIZE];
|
|
||||||
static char* end_of_static_str = 0;
|
|
||||||
|
|
||||||
if ( ! initialized )
|
|
||||||
{
|
|
||||||
// Get local IP.
|
|
||||||
char host[MAXHOSTNAMELEN];
|
|
||||||
strcpy(host, "localhost");
|
|
||||||
gethostname(host, MAXHOSTNAMELEN);
|
|
||||||
host[MAXHOSTNAMELEN-1] = '\0';
|
|
||||||
#if 0
|
|
||||||
// We ignore errors.
|
|
||||||
struct hostent* ent = gethostbyname(host);
|
|
||||||
|
|
||||||
uint32 ip;
|
|
||||||
if ( ent && ent->h_addr_list[0] )
|
|
||||||
ip = *(uint32*) ent->h_addr_list[0];
|
|
||||||
else
|
|
||||||
ip = htonl(0x7f000001); // 127.0.0.1
|
|
||||||
|
|
||||||
safe_snprintf(name, MAX_NAME_SIZE, "#%s#%d#",
|
|
||||||
IPAddr(IPv4, &ip, IPAddr::Network)->AsString().c_str(),
|
|
||||||
getpid());
|
|
||||||
#else
|
|
||||||
safe_snprintf(name, MAX_NAME_SIZE, "#%s#%d#", host, getpid());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
end_of_static_str = name + strlen(name);
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
safe_snprintf(end_of_static_str, MAX_NAME_SIZE - (end_of_static_str - name),
|
|
||||||
"%u", ++id_counter);
|
|
||||||
name[MAX_NAME_SIZE-1] = '\0';
|
|
||||||
|
|
||||||
// DBG_LOG(DBG_STATE, "new unique ID %s", name);
|
|
||||||
|
|
||||||
id = new ID(name, SCOPE_GLOBAL, true);
|
|
||||||
id->SetType(const_cast<MutableVal*>(this)->Type()->Ref());
|
|
||||||
|
|
||||||
global_scope()->Insert(name, id);
|
|
||||||
|
|
||||||
id->SetVal(const_cast<MutableVal*>(this), OP_NONE, true);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MutableVal::TransferUniqueID(MutableVal* mv)
|
|
||||||
{
|
|
||||||
const char* new_name = mv->UniqueID()->Name();
|
|
||||||
|
|
||||||
if ( ! id )
|
|
||||||
Bind();
|
|
||||||
|
|
||||||
DBG_LOG(DBG_STATE, "transfering ID (new %s, old/alias %s)", new_name, id->Name());
|
|
||||||
|
|
||||||
// Keep old name as alias.
|
|
||||||
aliases.push_back(id);
|
|
||||||
|
|
||||||
id = new ID(new_name, SCOPE_GLOBAL, true);
|
|
||||||
id->SetType(const_cast<MutableVal*>(this)->Type()->Ref());
|
|
||||||
global_scope()->Insert(new_name, id);
|
|
||||||
id->SetVal(const_cast<MutableVal*>(this), OP_NONE, true);
|
|
||||||
|
|
||||||
Unref(mv->id);
|
|
||||||
mv->id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntervalVal::IntervalVal(double quantity, double units) :
|
IntervalVal::IntervalVal(double quantity, double units) :
|
||||||
Val(quantity * units, TYPE_INTERVAL)
|
Val(quantity * units, TYPE_INTERVAL)
|
||||||
{
|
{
|
||||||
|
@ -1056,7 +934,7 @@ static void table_entry_val_delete_func(void* val)
|
||||||
delete tv;
|
delete tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
TableVal::TableVal(TableType* t, Attributes* a) : MutableVal(t)
|
TableVal::TableVal(TableType* t, Attributes* a) : Val(t)
|
||||||
{
|
{
|
||||||
Init(t);
|
Init(t);
|
||||||
SetAttrs(a);
|
SetAttrs(a);
|
||||||
|
@ -1175,7 +1053,7 @@ void TableVal::CheckExpireAttr(attr_tag at)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TableVal::Assign(Val* index, Val* new_val, Opcode op)
|
int TableVal::Assign(Val* index, Val* new_val)
|
||||||
{
|
{
|
||||||
HashKey* k = ComputeHash(index);
|
HashKey* k = ComputeHash(index);
|
||||||
if ( ! k )
|
if ( ! k )
|
||||||
|
@ -1185,10 +1063,10 @@ int TableVal::Assign(Val* index, Val* new_val, Opcode op)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Assign(index, k, new_val, op);
|
return Assign(index, k, new_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
|
int TableVal::Assign(Val* index, HashKey* k, Val* new_val)
|
||||||
{
|
{
|
||||||
int is_set = table_type->IsSet();
|
int is_set = table_type->IsSet();
|
||||||
|
|
||||||
|
@ -1217,55 +1095,6 @@ int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
|
||||||
subnets->Insert(index, new_entry_val);
|
subnets->Insert(index, new_entry_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( LoggingAccess() && op != OP_NONE )
|
|
||||||
{
|
|
||||||
Val* rec_index = 0;
|
|
||||||
if ( ! index )
|
|
||||||
index = rec_index = RecoverIndex(&k_copy);
|
|
||||||
|
|
||||||
if ( new_val )
|
|
||||||
{
|
|
||||||
// A table.
|
|
||||||
if ( new_val->IsMutableVal() )
|
|
||||||
new_val->AsMutableVal()->AddProperties(GetProperties());
|
|
||||||
|
|
||||||
bool unref_old_val = false;
|
|
||||||
Val* old_val = old_entry_val ?
|
|
||||||
old_entry_val->Value() : 0;
|
|
||||||
if ( op == OP_INCR && ! old_val )
|
|
||||||
// If it's an increment, somebody has already
|
|
||||||
// checked that the index is there. If it's
|
|
||||||
// not, that can only be due to using the
|
|
||||||
// default.
|
|
||||||
{
|
|
||||||
old_val = Default(index);
|
|
||||||
unref_old_val = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(op != OP_INCR || old_val);
|
|
||||||
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(
|
|
||||||
op == OP_INCR ?
|
|
||||||
OP_INCR_IDX : OP_ASSIGN_IDX,
|
|
||||||
this, index, new_val, old_val));
|
|
||||||
|
|
||||||
if ( unref_old_val )
|
|
||||||
Unref(old_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// A set.
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_ADD, this,
|
|
||||||
index, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( rec_index )
|
|
||||||
Unref(rec_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep old expiration time if necessary.
|
// Keep old expiration time if necessary.
|
||||||
if ( old_entry_val && attrs && attrs->FindAttr(ATTR_EXPIRE_CREATE) )
|
if ( old_entry_val && attrs && attrs->FindAttr(ATTR_EXPIRE_CREATE) )
|
||||||
new_entry_val->SetExpireAccess(old_entry_val->ExpireAccessTime());
|
new_entry_val->SetExpireAccess(old_entry_val->ExpireAccessTime());
|
||||||
|
@ -1276,6 +1105,7 @@ int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
|
||||||
delete old_entry_val;
|
delete old_entry_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Modified();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1318,15 +1148,13 @@ int TableVal::AddTo(Val* val, int is_first_init, bool propagate_ops) const
|
||||||
|
|
||||||
if ( type->IsSet() )
|
if ( type->IsSet() )
|
||||||
{
|
{
|
||||||
if ( ! t->Assign(v->Value(), k, 0,
|
if ( ! t->Assign(v->Value(), k, 0) )
|
||||||
propagate_ops ? OP_ASSIGN : OP_NONE) )
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
v->Ref();
|
v->Ref();
|
||||||
if ( ! t->Assign(0, k, v->Value(),
|
if ( ! t->Assign(0, k, v->Value()) )
|
||||||
propagate_ops ? OP_ASSIGN : OP_NONE) )
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1595,11 +1423,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val)
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
||||||
{
|
|
||||||
v->SetExpireAccess(network_time);
|
v->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && ExpirationEnabled() )
|
|
||||||
ReadOperation(index, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return v->Value() ? v->Value() : this;
|
return v->Value() ? v->Value() : this;
|
||||||
}
|
}
|
||||||
|
@ -1626,11 +1450,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val)
|
||||||
if ( v )
|
if ( v )
|
||||||
{
|
{
|
||||||
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
||||||
{
|
|
||||||
v->SetExpireAccess(network_time);
|
v->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && ExpirationEnabled() )
|
|
||||||
ReadOperation(index, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return v->Value() ? v->Value() : this;
|
return v->Value() ? v->Value() : this;
|
||||||
}
|
}
|
||||||
|
@ -1684,11 +1504,7 @@ TableVal* TableVal::LookupSubnetValues(const SubNetVal* search)
|
||||||
if ( entry )
|
if ( entry )
|
||||||
{
|
{
|
||||||
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
||||||
{
|
|
||||||
entry->SetExpireAccess(network_time);
|
entry->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && ExpirationEnabled() )
|
|
||||||
ReadOperation(s, entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Unref(s); // assign does not consume index
|
Unref(s); // assign does not consume index
|
||||||
|
@ -1718,8 +1534,6 @@ bool TableVal::UpdateTimestamp(Val* index)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
v->SetExpireAccess(network_time);
|
v->SetExpireAccess(network_time);
|
||||||
if ( LoggingAccess() && attrs->FindAttr(ATTR_EXPIRE_READ) )
|
|
||||||
ReadOperation(index, v);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1738,25 +1552,10 @@ Val* TableVal::Delete(const Val* index)
|
||||||
if ( subnets && ! subnets->Remove(index) )
|
if ( subnets && ! subnets->Remove(index) )
|
||||||
reporter->InternalWarning("index not in prefix table");
|
reporter->InternalWarning("index not in prefix table");
|
||||||
|
|
||||||
if ( LoggingAccess() )
|
|
||||||
{
|
|
||||||
if ( v )
|
|
||||||
{
|
|
||||||
// A set.
|
|
||||||
Val* has_old_val = val_mgr->GetInt(1);
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_DEL, this, index,
|
|
||||||
has_old_val));
|
|
||||||
Unref(has_old_val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_DEL, this, index, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
delete k;
|
delete k;
|
||||||
delete v;
|
delete v;
|
||||||
|
|
||||||
|
Modified();
|
||||||
return va;
|
return va;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1775,9 +1574,7 @@ Val* TableVal::Delete(const HashKey* k)
|
||||||
|
|
||||||
delete v;
|
delete v;
|
||||||
|
|
||||||
if ( LoggingAccess() )
|
Modified();
|
||||||
StateAccess::Log(new StateAccess(OP_DEL, this, k));
|
|
||||||
|
|
||||||
return va;
|
return va;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1944,7 +1741,7 @@ int TableVal::ExpandCompoundAndInit(val_list* vl, int k, Val* new_val)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int TableVal::CheckAndAssign(Val* index, Val* new_val, Opcode op)
|
int TableVal::CheckAndAssign(Val* index, Val* new_val)
|
||||||
{
|
{
|
||||||
Val* v = 0;
|
Val* v = 0;
|
||||||
if ( subnets )
|
if ( subnets )
|
||||||
|
@ -1956,7 +1753,7 @@ int TableVal::CheckAndAssign(Val* index, Val* new_val, Opcode op)
|
||||||
if ( v )
|
if ( v )
|
||||||
index->Warn("multiple initializations for index");
|
index->Warn("multiple initializations for index");
|
||||||
|
|
||||||
return Assign(index, new_val, op);
|
return Assign(index, new_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TableVal::InitTimer(double delay)
|
void TableVal::InitTimer(double delay)
|
||||||
|
@ -1988,6 +1785,7 @@ void TableVal::DoExpire(double t)
|
||||||
HashKey* k = 0;
|
HashKey* k = 0;
|
||||||
TableEntryVal* v = 0;
|
TableEntryVal* v = 0;
|
||||||
TableEntryVal* v_saved = 0;
|
TableEntryVal* v_saved = 0;
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
for ( int i = 0; i < table_incremental_step &&
|
for ( int i = 0; i < table_incremental_step &&
|
||||||
(v = tbl->NextEntry(k, expire_cookie)); ++i )
|
(v = tbl->NextEntry(k, expire_cookie)); ++i )
|
||||||
|
@ -2040,18 +1838,18 @@ void TableVal::DoExpire(double t)
|
||||||
Unref(index);
|
Unref(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( LoggingAccess() )
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(OP_EXPIRE, this, k));
|
|
||||||
|
|
||||||
tbl->RemoveEntry(k);
|
tbl->RemoveEntry(k);
|
||||||
Unref(v->Value());
|
Unref(v->Value());
|
||||||
delete v;
|
delete v;
|
||||||
|
modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
delete k;
|
delete k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( modified )
|
||||||
|
Modified();
|
||||||
|
|
||||||
if ( ! v )
|
if ( ! v )
|
||||||
{
|
{
|
||||||
expire_cookie = 0;
|
expire_cookie = 0;
|
||||||
|
@ -2149,26 +1947,6 @@ double TableVal::CallExpireFunc(Val* idx)
|
||||||
return secs;
|
return secs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TableVal::ReadOperation(Val* index, TableEntryVal* v)
|
|
||||||
{
|
|
||||||
double timeout = GetExpireTime();
|
|
||||||
|
|
||||||
if ( timeout < 0 )
|
|
||||||
// Skip in case of unset/invalid expiration value. If it's an
|
|
||||||
// error, it has been reported already.
|
|
||||||
return;
|
|
||||||
|
|
||||||
// In theory we need to only propagate one update per &read_expire
|
|
||||||
// interval to prevent peers from expiring intervals. To account for
|
|
||||||
// practical issues such as latency, we send one update every half
|
|
||||||
// &read_expire.
|
|
||||||
if ( network_time - v->LastReadUpdate() > timeout / 2 )
|
|
||||||
{
|
|
||||||
StateAccess::Log(new StateAccess(OP_READ_IDX, this, index));
|
|
||||||
v->SetLastReadUpdate(network_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Val* TableVal::DoClone(CloneState* state)
|
Val* TableVal::DoClone(CloneState* state)
|
||||||
{
|
{
|
||||||
auto tv = new TableVal(table_type);
|
auto tv = new TableVal(table_type);
|
||||||
|
@ -2219,48 +1997,6 @@ Val* TableVal::DoClone(CloneState* state)
|
||||||
return tv;
|
return tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TableVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::AddProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( Type()->IsSet() || ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// For a large table, this could get expensive. So, let's hope
|
|
||||||
// that nobody creates such a table *before* making it persistent
|
|
||||||
// (for example by inserting it into another table).
|
|
||||||
TableEntryVal* v;
|
|
||||||
PDict(TableEntryVal)* tbl = val.table_val;
|
|
||||||
IterCookie* c = tbl->InitForIteration();
|
|
||||||
while ( (v = tbl->NextEntry(c)) )
|
|
||||||
if ( v->Value()->IsMutableVal() )
|
|
||||||
v->Value()->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TableVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::RemoveProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( Type()->IsSet() || ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// For a large table, this could get expensive. So, let's hope
|
|
||||||
// that nobody creates such a table *before* making it persistent
|
|
||||||
// (for example by inserting it into another table).
|
|
||||||
TableEntryVal* v;
|
|
||||||
PDict(TableEntryVal)* tbl = val.table_val;
|
|
||||||
IterCookie* c = tbl->InitForIteration();
|
|
||||||
while ( (v = tbl->NextEntry(c)) )
|
|
||||||
if ( v->Value()->IsMutableVal() )
|
|
||||||
v->Value()->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int TableVal::MemoryAllocation() const
|
unsigned int TableVal::MemoryAllocation() const
|
||||||
{
|
{
|
||||||
unsigned int size = 0;
|
unsigned int size = 0;
|
||||||
|
@ -2282,7 +2018,7 @@ unsigned int TableVal::MemoryAllocation() const
|
||||||
|
|
||||||
vector<RecordVal*> RecordVal::parse_time_records;
|
vector<RecordVal*> RecordVal::parse_time_records;
|
||||||
|
|
||||||
RecordVal::RecordVal(RecordType* t, bool init_fields) : MutableVal(t)
|
RecordVal::RecordVal(RecordType* t, bool init_fields) : Val(t)
|
||||||
{
|
{
|
||||||
origin = 0;
|
origin = 0;
|
||||||
int n = t->NumFields();
|
int n = t->NumFields();
|
||||||
|
@ -2343,24 +2079,11 @@ RecordVal::~RecordVal()
|
||||||
delete_vals(AsNonConstRecord());
|
delete_vals(AsNonConstRecord());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecordVal::Assign(int field, Val* new_val, Opcode op)
|
void RecordVal::Assign(int field, Val* new_val)
|
||||||
{
|
{
|
||||||
Val* old_val = AsNonConstRecord()->replace(field, new_val);
|
Val* old_val = AsNonConstRecord()->replace(field, new_val);
|
||||||
|
|
||||||
if ( LoggingAccess() && op != OP_NONE )
|
|
||||||
{
|
|
||||||
if ( new_val && new_val->IsMutableVal() )
|
|
||||||
new_val->AsMutableVal()->AddProperties(GetProperties());
|
|
||||||
|
|
||||||
StringVal* index = new StringVal(Type()->AsRecordType()->FieldName(field));
|
|
||||||
StateAccess::Log(
|
|
||||||
new StateAccess(
|
|
||||||
op == OP_INCR ? OP_INCR_IDX : OP_ASSIGN_IDX,
|
|
||||||
this, index, new_val, old_val));
|
|
||||||
Unref(index); // The logging may keep a cached copy.
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(old_val);
|
Unref(old_val);
|
||||||
|
Modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
Val* RecordVal::Lookup(int field) const
|
Val* RecordVal::Lookup(int field) const
|
||||||
|
@ -2570,41 +2293,6 @@ Val* RecordVal::DoClone(CloneState* state)
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RecordVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::AddProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
loop_over_list(*val.val_list_val, i)
|
|
||||||
{
|
|
||||||
Val* v = (*val.val_list_val)[i];
|
|
||||||
if ( v && v->IsMutableVal() )
|
|
||||||
v->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool RecordVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::RemoveProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
loop_over_list(*val.val_list_val, i)
|
|
||||||
{
|
|
||||||
Val* v = (*val.val_list_val)[i];
|
|
||||||
if ( v && v->IsMutableVal() )
|
|
||||||
v->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int RecordVal::MemoryAllocation() const
|
unsigned int RecordVal::MemoryAllocation() const
|
||||||
{
|
{
|
||||||
unsigned int size = 0;
|
unsigned int size = 0;
|
||||||
|
@ -2637,7 +2325,7 @@ Val* EnumVal::DoClone(CloneState* state)
|
||||||
return Ref();
|
return Ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorVal::VectorVal(VectorType* t) : MutableVal(t)
|
VectorVal::VectorVal(VectorType* t) : Val(t)
|
||||||
{
|
{
|
||||||
vector_type = t->Ref()->AsVectorType();
|
vector_type = t->Ref()->AsVectorType();
|
||||||
val.vector_val = new vector<Val*>();
|
val.vector_val = new vector<Val*>();
|
||||||
|
@ -2653,7 +2341,7 @@ VectorVal::~VectorVal()
|
||||||
delete val.vector_val;
|
delete val.vector_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VectorVal::Assign(unsigned int index, Val* element, Opcode op)
|
bool VectorVal::Assign(unsigned int index, Val* element)
|
||||||
{
|
{
|
||||||
if ( element &&
|
if ( element &&
|
||||||
! same_type(element->Type(), vector_type->YieldType(), 0) )
|
! same_type(element->Type(), vector_type->YieldType(), 0) )
|
||||||
|
@ -2669,19 +2357,6 @@ bool VectorVal::Assign(unsigned int index, Val* element, Opcode op)
|
||||||
else
|
else
|
||||||
val.vector_val->resize(index + 1);
|
val.vector_val->resize(index + 1);
|
||||||
|
|
||||||
if ( LoggingAccess() && op != OP_NONE )
|
|
||||||
{
|
|
||||||
if ( element->IsMutableVal() )
|
|
||||||
element->AsMutableVal()->AddProperties(GetProperties());
|
|
||||||
|
|
||||||
Val* ival = val_mgr->GetCount(index);
|
|
||||||
|
|
||||||
StateAccess::Log(new StateAccess(op == OP_INCR ?
|
|
||||||
OP_INCR_IDX : OP_ASSIGN_IDX,
|
|
||||||
this, ival, element, val_at_index));
|
|
||||||
Unref(ival);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unref(val_at_index);
|
Unref(val_at_index);
|
||||||
|
|
||||||
// Note: we do *not* Ref() the element, if any, at this point.
|
// Note: we do *not* Ref() the element, if any, at this point.
|
||||||
|
@ -2689,6 +2364,7 @@ bool VectorVal::Assign(unsigned int index, Val* element, Opcode op)
|
||||||
// to do it similarly.
|
// to do it similarly.
|
||||||
(*val.vector_val)[index] = element;
|
(*val.vector_val)[index] = element;
|
||||||
|
|
||||||
|
Modified();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2725,6 +2401,7 @@ bool VectorVal::Insert(unsigned int index, Val* element)
|
||||||
// to do it similarly.
|
// to do it similarly.
|
||||||
val.vector_val->insert(it, element);
|
val.vector_val->insert(it, element);
|
||||||
|
|
||||||
|
Modified();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2738,6 +2415,7 @@ bool VectorVal::Remove(unsigned int index)
|
||||||
val.vector_val->erase(it);
|
val.vector_val->erase(it);
|
||||||
Unref(val_at_index);
|
Unref(val_at_index);
|
||||||
|
|
||||||
|
Modified();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2790,37 +2468,6 @@ unsigned int VectorVal::ResizeAtLeast(unsigned int new_num_elements)
|
||||||
return Resize(new_num_elements);
|
return Resize(new_num_elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VectorVal::AddProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::AddProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
|
|
||||||
if ( (*val.vector_val)[i]->IsMutableVal() )
|
|
||||||
(*val.vector_val)[i]->AsMutableVal()->AddProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VectorVal::RemoveProperties(Properties arg_props)
|
|
||||||
{
|
|
||||||
if ( ! MutableVal::RemoveProperties(arg_props) )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( ! RecursiveProps(arg_props) )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for ( unsigned int i = 0; i < val.vector_val->size(); ++i )
|
|
||||||
if ( (*val.vector_val)[i]->IsMutableVal() )
|
|
||||||
(*val.vector_val)[i]->AsMutableVal()->RemoveProperties(RecursiveProps(arg_props));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Val* VectorVal::DoClone(CloneState* state)
|
Val* VectorVal::DoClone(CloneState* state)
|
||||||
{
|
{
|
||||||
auto vv = new VectorVal(vector_type);
|
auto vv = new VectorVal(vector_type);
|
||||||
|
|
144
src/Val.h
144
src/Val.h
|
@ -21,7 +21,7 @@
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
#include "ID.h"
|
#include "ID.h"
|
||||||
#include "Scope.h"
|
#include "Scope.h"
|
||||||
#include "StateAccess.h"
|
#include "Notifier.h"
|
||||||
#include "IPAddr.h"
|
#include "IPAddr.h"
|
||||||
#include "DebugLogger.h"
|
#include "DebugLogger.h"
|
||||||
|
|
||||||
|
@ -52,7 +52,6 @@ class RecordVal;
|
||||||
class ListVal;
|
class ListVal;
|
||||||
class StringVal;
|
class StringVal;
|
||||||
class EnumVal;
|
class EnumVal;
|
||||||
class MutableVal;
|
|
||||||
class OpaqueVal;
|
class OpaqueVal;
|
||||||
|
|
||||||
class StateAccess;
|
class StateAccess;
|
||||||
|
@ -328,28 +327,13 @@ public:
|
||||||
CONST_CONVERTER(TYPE_VECTOR, VectorVal*, AsVectorVal)
|
CONST_CONVERTER(TYPE_VECTOR, VectorVal*, AsVectorVal)
|
||||||
CONST_CONVERTER(TYPE_OPAQUE, OpaqueVal*, AsOpaqueVal)
|
CONST_CONVERTER(TYPE_OPAQUE, OpaqueVal*, AsOpaqueVal)
|
||||||
|
|
||||||
bool IsMutableVal() const
|
|
||||||
{
|
|
||||||
return IsMutable(type->Tag());
|
|
||||||
}
|
|
||||||
|
|
||||||
const MutableVal* AsMutableVal() const
|
|
||||||
{
|
|
||||||
if ( ! IsMutableVal() )
|
|
||||||
BadTag("Val::AsMutableVal", type_name(type->Tag()));
|
|
||||||
return (MutableVal*) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
MutableVal* AsMutableVal()
|
|
||||||
{
|
|
||||||
if ( ! IsMutableVal() )
|
|
||||||
BadTag("Val::AsMutableVal", type_name(type->Tag()));
|
|
||||||
return (MutableVal*) this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Describe(ODesc* d) const override;
|
void Describe(ODesc* d) const override;
|
||||||
virtual void DescribeReST(ODesc* d) const;
|
virtual void DescribeReST(ODesc* d) const;
|
||||||
|
|
||||||
|
// To be overridden by mutable derived class to enable change
|
||||||
|
// notification.
|
||||||
|
virtual notifier::Modifiable* Modifiable() { return 0; }
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// For debugging, we keep a reference to the global ID to which a
|
// For debugging, we keep a reference to the global ID to which a
|
||||||
// value has been bound *last*.
|
// value has been bound *last*.
|
||||||
|
@ -517,69 +501,6 @@ private:
|
||||||
|
|
||||||
extern ValManager* val_mgr;
|
extern ValManager* val_mgr;
|
||||||
|
|
||||||
class MutableVal : public Val {
|
|
||||||
public:
|
|
||||||
// Each MutableVal gets a globally unique ID that can be used to
|
|
||||||
// reference it no matter if it's directly bound to any user-visible
|
|
||||||
// ID. This ID is inserted into the global namespace.
|
|
||||||
ID* UniqueID() const { return id ? id : Bind(); }
|
|
||||||
|
|
||||||
// Returns true if we've already generated a unique ID.
|
|
||||||
bool HasUniqueID() const { return id; }
|
|
||||||
|
|
||||||
// Transfers the unique ID of the given value to this value. We keep our
|
|
||||||
// old ID as an alias.
|
|
||||||
void TransferUniqueID(MutableVal* mv);
|
|
||||||
|
|
||||||
// MutableVals can have properties (let's refrain from calling them
|
|
||||||
// attributes!). Most properties are recursive. If a derived object
|
|
||||||
// can contain MutableVals itself, the object has to override
|
|
||||||
// {Add,Remove}Properties(). RecursiveProp(state) masks out all non-
|
|
||||||
// recursive properties. If this is non-null, an overriden method must
|
|
||||||
// call itself with RecursiveProp(state) as argument for all contained
|
|
||||||
// values. (In any case, don't forget to call the parent's method.)
|
|
||||||
typedef char Properties;
|
|
||||||
|
|
||||||
// Tracked by NotifierRegistry, not recursive.
|
|
||||||
static const int TRACKED = 0x04;
|
|
||||||
|
|
||||||
int RecursiveProps(int prop) const { return prop & ~TRACKED; }
|
|
||||||
|
|
||||||
Properties GetProperties() const { return props; }
|
|
||||||
virtual bool AddProperties(Properties state);
|
|
||||||
virtual bool RemoveProperties(Properties state);
|
|
||||||
|
|
||||||
// Whether StateAccess:LogAccess needs to be called.
|
|
||||||
bool LoggingAccess() const
|
|
||||||
{
|
|
||||||
#ifndef DEBUG
|
|
||||||
return props & TRACKED;
|
|
||||||
#else
|
|
||||||
return debug_logger.IsVerbose() ||
|
|
||||||
(props & TRACKED);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit MutableVal(BroType* t) : Val(t)
|
|
||||||
{ props = 0; id = 0; }
|
|
||||||
MutableVal() { props = 0; id = 0; }
|
|
||||||
~MutableVal() override;
|
|
||||||
|
|
||||||
friend class ID;
|
|
||||||
friend class Val;
|
|
||||||
|
|
||||||
void SetID(ID* arg_id) { Unref(id); id = arg_id; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ID* Bind() const;
|
|
||||||
|
|
||||||
mutable ID* id;
|
|
||||||
list<ID*> aliases;
|
|
||||||
Properties props;
|
|
||||||
uint64 last_modified;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define Microseconds 1e-6
|
#define Microseconds 1e-6
|
||||||
#define Milliseconds 1e-3
|
#define Milliseconds 1e-3
|
||||||
#define Seconds 1.0
|
#define Seconds 1.0
|
||||||
|
@ -800,7 +721,7 @@ public:
|
||||||
{
|
{
|
||||||
val = v;
|
val = v;
|
||||||
last_access_time = network_time;
|
last_access_time = network_time;
|
||||||
expire_access_time = last_read_update =
|
expire_access_time =
|
||||||
int(network_time - bro_start_network_time);
|
int(network_time - bro_start_network_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -809,7 +730,6 @@ public:
|
||||||
auto rval = new TableEntryVal(val ? val->Clone() : nullptr);
|
auto rval = new TableEntryVal(val ? val->Clone() : nullptr);
|
||||||
rval->last_access_time = last_access_time;
|
rval->last_access_time = last_access_time;
|
||||||
rval->expire_access_time = expire_access_time;
|
rval->expire_access_time = expire_access_time;
|
||||||
rval->last_read_update = last_read_update;
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -825,24 +745,16 @@ public:
|
||||||
void SetExpireAccess(double time)
|
void SetExpireAccess(double time)
|
||||||
{ expire_access_time = int(time - bro_start_network_time); }
|
{ expire_access_time = int(time - bro_start_network_time); }
|
||||||
|
|
||||||
// Returns/sets time of when we propagated the last OP_READ_IDX
|
|
||||||
// for this item.
|
|
||||||
double LastReadUpdate() const
|
|
||||||
{ return bro_start_network_time + last_read_update; }
|
|
||||||
void SetLastReadUpdate(double time)
|
|
||||||
{ last_read_update = int(time - bro_start_network_time); }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class TableVal;
|
friend class TableVal;
|
||||||
|
|
||||||
Val* val;
|
Val* val;
|
||||||
double last_access_time;
|
double last_access_time;
|
||||||
|
|
||||||
// The next two entries store seconds since Bro's start. We use
|
// The next entry stores seconds since Bro's start. We use ints here
|
||||||
// ints here to save a few bytes, as we do not need a high resolution
|
// to save a few bytes, as we do not need a high resolution for these
|
||||||
// for these anyway.
|
// anyway.
|
||||||
int expire_access_time;
|
int expire_access_time;
|
||||||
int last_read_update;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TableValTimer : public Timer {
|
class TableValTimer : public Timer {
|
||||||
|
@ -859,7 +771,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
class CompositeHash;
|
class CompositeHash;
|
||||||
class TableVal : public MutableVal {
|
class TableVal : public Val, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
explicit TableVal(TableType* t, Attributes* attrs = 0);
|
explicit TableVal(TableType* t, Attributes* attrs = 0);
|
||||||
~TableVal() override;
|
~TableVal() override;
|
||||||
|
@ -869,8 +781,8 @@ public:
|
||||||
// version takes a HashKey and Unref()'s it when done. If we're a
|
// version takes a HashKey and Unref()'s it when done. If we're a
|
||||||
// set, new_val has to be nil. If we aren't a set, index may be nil
|
// set, new_val has to be nil. If we aren't a set, index may be nil
|
||||||
// in the second version.
|
// in the second version.
|
||||||
int Assign(Val* index, Val* new_val, Opcode op = OP_ASSIGN);
|
int Assign(Val* index, Val* new_val);
|
||||||
int Assign(Val* index, HashKey* k, Val* new_val, Opcode op = OP_ASSIGN);
|
int Assign(Val* index, HashKey* k, Val* new_val);
|
||||||
|
|
||||||
Val* SizeVal() const override { return val_mgr->GetCount(Size()); }
|
Val* SizeVal() const override { return val_mgr->GetCount(Size()); }
|
||||||
|
|
||||||
|
@ -972,19 +884,17 @@ public:
|
||||||
HashKey* ComputeHash(const Val* index) const
|
HashKey* ComputeHash(const Val* index) const
|
||||||
{ return table_hash->ComputeHash(index, 1); }
|
{ return table_hash->ComputeHash(index, 1); }
|
||||||
|
|
||||||
|
notifier::Modifiable* Modifiable() override { return this; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Val;
|
friend class Val;
|
||||||
friend class StateAccess;
|
|
||||||
TableVal() {}
|
TableVal() {}
|
||||||
|
|
||||||
void Init(TableType* t);
|
void Init(TableType* t);
|
||||||
|
|
||||||
void CheckExpireAttr(attr_tag at);
|
void CheckExpireAttr(attr_tag at);
|
||||||
int ExpandCompoundAndInit(val_list* vl, int k, Val* new_val);
|
int ExpandCompoundAndInit(val_list* vl, int k, Val* new_val);
|
||||||
int CheckAndAssign(Val* index, Val* new_val, Opcode op = OP_ASSIGN);
|
int CheckAndAssign(Val* index, Val* new_val);
|
||||||
|
|
||||||
bool AddProperties(Properties arg_state) override;
|
|
||||||
bool RemoveProperties(Properties arg_state) override;
|
|
||||||
|
|
||||||
// Calculates default value for index. Returns 0 if none.
|
// Calculates default value for index. Returns 0 if none.
|
||||||
Val* Default(Val* index);
|
Val* Default(Val* index);
|
||||||
|
@ -1001,9 +911,6 @@ protected:
|
||||||
// takes ownership of the reference.
|
// takes ownership of the reference.
|
||||||
double CallExpireFunc(Val *idx);
|
double CallExpireFunc(Val *idx);
|
||||||
|
|
||||||
// Propagates a read operation if necessary.
|
|
||||||
void ReadOperation(Val* index, TableEntryVal *v);
|
|
||||||
|
|
||||||
Val* DoClone(CloneState* state) override;
|
Val* DoClone(CloneState* state) override;
|
||||||
|
|
||||||
TableType* table_type;
|
TableType* table_type;
|
||||||
|
@ -1017,7 +924,7 @@ protected:
|
||||||
Val* def_val;
|
Val* def_val;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RecordVal : public MutableVal {
|
class RecordVal : public Val, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
explicit RecordVal(RecordType* t, bool init_fields = true);
|
explicit RecordVal(RecordType* t, bool init_fields = true);
|
||||||
~RecordVal() override;
|
~RecordVal() override;
|
||||||
|
@ -1025,7 +932,7 @@ public:
|
||||||
Val* SizeVal() const override
|
Val* SizeVal() const override
|
||||||
{ return val_mgr->GetCount(Type()->AsRecordType()->NumFields()); }
|
{ return val_mgr->GetCount(Type()->AsRecordType()->NumFields()); }
|
||||||
|
|
||||||
void Assign(int field, Val* new_val, Opcode op = OP_ASSIGN);
|
void Assign(int field, Val* new_val);
|
||||||
Val* Lookup(int field) const; // Does not Ref() value.
|
Val* Lookup(int field) const; // Does not Ref() value.
|
||||||
Val* LookupWithDefault(int field) const; // Does Ref() value.
|
Val* LookupWithDefault(int field) const; // Does Ref() value.
|
||||||
|
|
||||||
|
@ -1064,6 +971,8 @@ public:
|
||||||
unsigned int MemoryAllocation() const override;
|
unsigned int MemoryAllocation() const override;
|
||||||
void DescribeReST(ODesc* d) const override;
|
void DescribeReST(ODesc* d) const override;
|
||||||
|
|
||||||
|
notifier::Modifiable* Modifiable() override { return this; }
|
||||||
|
|
||||||
// Extend the underlying arrays of record instances created during
|
// Extend the underlying arrays of record instances created during
|
||||||
// parsing to match the number of fields in the record type (they may
|
// parsing to match the number of fields in the record type (they may
|
||||||
// mismatch as a result of parse-time record type redefinitions.
|
// mismatch as a result of parse-time record type redefinitions.
|
||||||
|
@ -1073,9 +982,6 @@ protected:
|
||||||
friend class Val;
|
friend class Val;
|
||||||
RecordVal() {}
|
RecordVal() {}
|
||||||
|
|
||||||
bool AddProperties(Properties arg_state) override;
|
|
||||||
bool RemoveProperties(Properties arg_state) override;
|
|
||||||
|
|
||||||
Val* DoClone(CloneState* state) override;
|
Val* DoClone(CloneState* state) override;
|
||||||
|
|
||||||
RecordType* record_type;
|
RecordType* record_type;
|
||||||
|
@ -1111,7 +1017,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class VectorVal : public MutableVal {
|
class VectorVal : public Val, public notifier::Modifiable {
|
||||||
public:
|
public:
|
||||||
explicit VectorVal(VectorType* t);
|
explicit VectorVal(VectorType* t);
|
||||||
~VectorVal() override;
|
~VectorVal() override;
|
||||||
|
@ -1125,11 +1031,11 @@ public:
|
||||||
// Note: does NOT Ref() the element! Remember to do so unless
|
// Note: does NOT Ref() the element! Remember to do so unless
|
||||||
// the element was just created and thus has refcount 1.
|
// the element was just created and thus has refcount 1.
|
||||||
//
|
//
|
||||||
bool Assign(unsigned int index, Val* element, Opcode op = OP_ASSIGN);
|
bool Assign(unsigned int index, Val* element);
|
||||||
bool Assign(Val* index, Val* element, Opcode op = OP_ASSIGN)
|
bool Assign(Val* index, Val* element)
|
||||||
{
|
{
|
||||||
return Assign(index->AsListVal()->Index(0)->CoerceToUnsigned(),
|
return Assign(index->AsListVal()->Index(0)->CoerceToUnsigned(),
|
||||||
element, op);
|
element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assigns the value to how_many locations starting at index.
|
// Assigns the value to how_many locations starting at index.
|
||||||
|
@ -1159,6 +1065,8 @@ public:
|
||||||
// Won't shrink size.
|
// Won't shrink size.
|
||||||
unsigned int ResizeAtLeast(unsigned int new_num_elements);
|
unsigned int ResizeAtLeast(unsigned int new_num_elements);
|
||||||
|
|
||||||
|
notifier::Modifiable* Modifiable() override { return this; }
|
||||||
|
|
||||||
// Insert an element at a specific position into the underlying vector.
|
// Insert an element at a specific position into the underlying vector.
|
||||||
bool Insert(unsigned int index, Val* element);
|
bool Insert(unsigned int index, Val* element);
|
||||||
|
|
||||||
|
@ -1169,8 +1077,6 @@ protected:
|
||||||
friend class Val;
|
friend class Val;
|
||||||
VectorVal() { }
|
VectorVal() { }
|
||||||
|
|
||||||
bool AddProperties(Properties arg_state) override;
|
|
||||||
bool RemoveProperties(Properties arg_state) override;
|
|
||||||
void ValDescribe(ODesc* d) const override;
|
void ValDescribe(ODesc* d) const override;
|
||||||
Val* DoClone(CloneState* state) override;
|
Val* DoClone(CloneState* state) override;
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ Config::Config(ReaderFrontend *frontend) : ReaderBackend(frontend)
|
||||||
|
|
||||||
while ( auto id = globals->NextEntry(c) )
|
while ( auto id = globals->NextEntry(c) )
|
||||||
{
|
{
|
||||||
if ( id->IsInternalGlobal() || ! id->IsOption() )
|
if ( ! id->IsOption() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ( id->Type()->Tag() == TYPE_RECORD ||
|
if ( id->Type()->Tag() == TYPE_RECORD ||
|
||||||
|
|
16
src/zeek.bif
16
src/zeek.bif
|
@ -1835,7 +1835,7 @@ function global_sizes%(%): var_sizes
|
||||||
|
|
||||||
ID* id;
|
ID* id;
|
||||||
while ( (id = globals->NextEntry(c)) )
|
while ( (id = globals->NextEntry(c)) )
|
||||||
if ( id->HasVal() && ! id->IsInternalGlobal() )
|
if ( id->HasVal() )
|
||||||
{
|
{
|
||||||
Val* id_name = new StringVal(id->Name());
|
Val* id_name = new StringVal(id->Name());
|
||||||
Val* id_size = val_mgr->GetCount(id->ID_Val()->MemoryAllocation());
|
Val* id_size = val_mgr->GetCount(id->ID_Val()->MemoryAllocation());
|
||||||
|
@ -1859,24 +1859,10 @@ function global_ids%(%): id_table
|
||||||
TableVal* ids = new TableVal(id_table);
|
TableVal* ids = new TableVal(id_table);
|
||||||
PDict(ID)* globals = global_scope()->Vars();
|
PDict(ID)* globals = global_scope()->Vars();
|
||||||
IterCookie* c = globals->InitForIteration();
|
IterCookie* c = globals->InitForIteration();
|
||||||
#ifdef DEBUG
|
|
||||||
/**
|
|
||||||
* Explanation time: c needs to be a robust cookie when one is in debug mode,
|
|
||||||
* otherwise the Zeek process will crash in ~80% of cases when -B all is specified.
|
|
||||||
* The reason for this are the RecordVals that we create. RecordVal::Assign triggers
|
|
||||||
* a StateAccess::Log, which in turn (only in debug mode) triggers StateAccess::Describe,
|
|
||||||
* which creates a UniqueID for the variable, which triggers an insert into global_scope.
|
|
||||||
* Which invalidates the iteration cookie if it is not robust.
|
|
||||||
**/
|
|
||||||
globals->MakeRobustCookie(c);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ID* id;
|
ID* id;
|
||||||
while ( (id = globals->NextEntry(c)) )
|
while ( (id = globals->NextEntry(c)) )
|
||||||
{
|
{
|
||||||
if ( id->IsInternalGlobal() )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
RecordVal* rec = new RecordVal(script_id);
|
RecordVal* rec = new RecordVal(script_id);
|
||||||
rec->Assign(0, new StringVal(type_name(id->Type()->Tag())));
|
rec->Assign(0, new StringVal(type_name(id->Type()->Tag())));
|
||||||
rec->Assign(1, val_mgr->GetBool(id->IsExport()));
|
rec->Assign(1, val_mgr->GetBool(id->IsExport()));
|
||||||
|
|
4
testing/btest/Baseline/language.when-on-globals/out
Normal file
4
testing/btest/Baseline/language.when-on-globals/out
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
"j" in x3[20]$x, expected timeout
|
||||||
|
15 in x2, T
|
||||||
|
x1 != 42, T
|
||||||
|
x2[10], T
|
71
testing/btest/language/when-on-globals.zeek
Normal file
71
testing/btest/language/when-on-globals.zeek
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# @TEST-EXEC: zeek -b -r $TRACES/wikipedia.trace %INPUT | sort >out
|
||||||
|
# @TEST-EXEC: btest-diff out
|
||||||
|
|
||||||
|
redef exit_only_after_terminate = T;
|
||||||
|
|
||||||
|
type X: record {
|
||||||
|
s: string;
|
||||||
|
x: set[string] &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
global x1 = 42;
|
||||||
|
global x2: table[count] of X;
|
||||||
|
global x3: table[count] of X;
|
||||||
|
|
||||||
|
event quit()
|
||||||
|
{
|
||||||
|
terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
event zeek_init()
|
||||||
|
{
|
||||||
|
x2[10] = [$s="foo"];
|
||||||
|
x3[20] = [$s="bar", $x=set("i")];
|
||||||
|
|
||||||
|
when ( x1 != 42 )
|
||||||
|
{
|
||||||
|
print "x1 != 42", x1 != 42;
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "unexpected timeout (1)";
|
||||||
|
}
|
||||||
|
|
||||||
|
when ( 15 in x2 )
|
||||||
|
{
|
||||||
|
print "15 in x2", 10 in x2;
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "unexpected timeout (2)";
|
||||||
|
}
|
||||||
|
|
||||||
|
when ( x2[10]$s == "bar" )
|
||||||
|
{
|
||||||
|
print "x2[10]", x2[10]$s == "bar";
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "unexpected timeout (3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
when ( "j" in x3[20]$x )
|
||||||
|
{
|
||||||
|
print "unexpected trigger";
|
||||||
|
}
|
||||||
|
timeout 1sec
|
||||||
|
{
|
||||||
|
print "\"j\" in x3[20]$x, expected timeout";
|
||||||
|
}
|
||||||
|
|
||||||
|
x1 = 100;
|
||||||
|
x2[15] = [$s="xyz"];
|
||||||
|
x2[10]$s = "bar";
|
||||||
|
|
||||||
|
# This will *NOT* trigger then when-condition because we're modifying
|
||||||
|
# an inner value that's not directly tracked.
|
||||||
|
add x3[20]$x["j"];
|
||||||
|
|
||||||
|
schedule 2secs { quit() };
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue