This commit is contained in:
Jon Siwek 2019-06-20 20:54:15 -07:00
commit f95390aabe
24 changed files with 388 additions and 1424 deletions

23
CHANGES
View file

@ -1,10 +1,31 @@
2.6-487 | 2019-06-20 20:38:26 -0700 2.6-500 | 2019-06-20 20:54:15 -0700
* Add new RDP event: rdp_client_cluster_data (Jeff Atkinson) * Add new RDP event: rdp_client_cluster_data (Jeff Atkinson)
* Added "options" field to RDP::ClientChannelDef (Jeff Atkinson) * Added "options" field to RDP::ClientChannelDef (Jeff Atkinson)
2.6-494 | 2019-06-20 20:24:38 -0700
* Renaming src/StateAccess.{h,cc} to src/Notifier.{h,cc}.
The old names did not reflect the content of the files anymore. (Robin Sommer, Corelight)
* Remove MutableVal, StateAccess classes, enum Opcode. (Robin Sommer, Corelight)
* Redo API for notifiers.
There's now an notifier::Modifiable interface class that class
supposed to signal modifications are to be derived from. This takes
the place of the former MutableValue class and also unifies how Val
and IDs signal modifications. (Robin Sommer, Corelight)
* Redo NotfifierRegistry to no longer rely on StateAccess.
We simplify the API to a simple Modified() operation. (Robin Sommer, Corelight)
* Add new test for when-statement watching global variables. (Robin Sommer, Corelight)
2.6-482 | 2019-06-20 19:57:20 -0700 2.6-482 | 2019-06-20 19:57:20 -0700
* Make configure complain if submodules are not checked out. (Johanna Amann, Corelight) * Make configure complain if submodules are not checked out. (Johanna Amann, Corelight)

View file

@ -1 +1 @@
2.6-487 2.6-500

View file

@ -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

View file

@ -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 },

View file

@ -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

View file

@ -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);
} }

View file

@ -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;

View file

@ -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()

View file

@ -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
View 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
View 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

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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();

View file

@ -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)

View file

@ -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;

View file

@ -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)

View file

@ -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
View file

@ -17,7 +17,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"
@ -48,7 +48,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;
@ -324,28 +323,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*.
@ -514,69 +498,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
@ -797,7 +718,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);
} }
@ -806,7 +727,6 @@ public:
auto rval = new TableEntryVal(val ? val->Clone(state) : nullptr); auto rval = new TableEntryVal(val ? val->Clone(state) : 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;
} }
@ -822,24 +742,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 {
@ -856,7 +768,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;
@ -866,8 +778,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()); }
@ -969,19 +881,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);
@ -998,9 +908,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;
@ -1014,7 +921,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;
@ -1022,7 +929,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.
@ -1061,6 +968,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.
@ -1070,9 +979,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;
@ -1108,7 +1014,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;
@ -1122,11 +1028,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.
@ -1156,6 +1062,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);
@ -1166,8 +1074,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;

View file

@ -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 ||

View file

@ -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()));

View file

@ -0,0 +1,4 @@
"j" in x3[20]$x, expected timeout
15 in x2, T
x1 != 42, T
x2[10], T

View 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() };
}