diff --git a/CHANGES b/CHANGES index e497b035ed..eacace84e3 100644 --- a/CHANGES +++ b/CHANGES @@ -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) * 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 * Make configure complain if submodules are not checked out. (Johanna Amann, Corelight) diff --git a/VERSION b/VERSION index 0744123abb..c756c5ff09 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6-487 +2.6-500 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c605e6ba5..2a509c1202 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -302,7 +302,7 @@ set(bro_SRCS Scope.cc SerializationFormat.cc Sessions.cc - StateAccess.cc + Notifier.cc Stats.cc Stmt.cc Tag.cc diff --git a/src/DebugLogger.cc b/src/DebugLogger.cc index f7090f151b..6af7e26e38 100644 --- a/src/DebugLogger.cc +++ b/src/DebugLogger.cc @@ -12,7 +12,7 @@ DebugLogger debug_logger; // Same order here as in DebugStream. DebugLogger::Stream DebugLogger::streams[NUM_DBGS] = { { "serial", 0, false }, { "rules", 0, false }, - { "state", 0, false }, {"string", 0, false }, + { "string", 0, false }, { "notifiers", 0, false }, { "main-loop", 0, false }, { "dpd", 0, false }, { "tm", 0, false }, { "logging", 0, false }, {"input", 0, false }, diff --git a/src/DebugLogger.h b/src/DebugLogger.h index 2e24c7064f..0e2862dc23 100644 --- a/src/DebugLogger.h +++ b/src/DebugLogger.h @@ -16,9 +16,8 @@ enum DebugStream { DBG_SERIAL, // Serialization DBG_RULES, // Signature matching - DBG_STATE, // StateAccess logging DBG_STRING, // String code - DBG_NOTIFIERS, // Notifiers (see StateAccess.h) + DBG_NOTIFIERS, // Notifiers DBG_MAINLOOP, // Main IOSource loop DBG_ANALYZER, // Analyzer framework DBG_TM, // Time-machine packet input via Brocolli diff --git a/src/Expr.cc b/src/Expr.cc index e19536359a..fdbd584876 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -97,7 +97,7 @@ void Expr::EvalIntoAggregate(const BroType* /* t */, Val* /* aggr */, Internal("Expr::EvalIntoAggregate called"); } -void Expr::Assign(Frame* /* f */, Val* /* v */, Opcode /* op */) +void Expr::Assign(Frame* /* f */, Val* /* v */) { Internal("Expr::Assign called"); } @@ -261,10 +261,10 @@ Expr* NameExpr::MakeLvalue() return new RefExpr(this); } -void NameExpr::Assign(Frame* f, Val* v, Opcode op) +void NameExpr::Assign(Frame* f, Val* v) { if ( id->IsGlobal() ) - id->SetVal(v, op); + id->SetVal(v); else f->SetElement(id->Offset(), v); } @@ -1007,18 +1007,18 @@ Val* IncrExpr::Eval(Frame* f) const if ( elt ) { Val* new_elt = DoSingleEval(f, elt); - v_vec->Assign(i, new_elt, OP_INCR); + v_vec->Assign(i, new_elt); } 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 { 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); } @@ -2041,9 +2041,9 @@ Expr* RefExpr::MakeLvalue() 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, @@ -2743,7 +2743,7 @@ Val* IndexExpr::Fold(Val* v1, Val* v2) const return 0; } -void IndexExpr::Assign(Frame* f, Val* v, Opcode op) +void IndexExpr::Assign(Frame* f, Val* v) { if ( IsError() ) return; @@ -2783,7 +2783,7 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op) for ( auto idx = 0u; idx < v_vect->Size(); idx++, first++ ) 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 ) { @@ -2803,7 +2803,7 @@ void IndexExpr::Assign(Frame* f, Val* v, Opcode op) } case TYPE_TABLE: - if ( ! v1->AsTableVal()->Assign(v2, v, op) ) + if ( ! v1->AsTableVal()->Assign(v2, v) ) { if ( v ) { @@ -2906,7 +2906,7 @@ int FieldExpr::CanDel() const 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() ) return; @@ -2915,14 +2915,14 @@ void FieldExpr::Assign(Frame* f, Val* v, Opcode opcode) if ( op_v ) { RecordVal* r = op_v->AsRecordVal(); - r->Assign(field, v, opcode); + r->Assign(field, v); Unref(r); } } void FieldExpr::Delete(Frame* f) { - Assign(f, 0, OP_ASSIGN_IDX); + Assign(f, 0); } Val* FieldExpr::Fold(Val* v) const @@ -4753,7 +4753,7 @@ Expr* ListExpr::MakeLvalue() return new RefExpr(this); } -void ListExpr::Assign(Frame* f, Val* v, Opcode op) +void ListExpr::Assign(Frame* f, Val* v) { ListVal* lv = v->AsListVal(); @@ -4761,7 +4761,7 @@ void ListExpr::Assign(Frame* f, Val* v, Opcode op) RuntimeError("mismatch in list lengths"); loop_over_list(exprs, i) - exprs[i]->Assign(f, (*lv->Vals())[i]->Ref(), op); + exprs[i]->Assign(f, (*lv->Vals())[i]->Ref()); Unref(lv); } diff --git a/src/Expr.h b/src/Expr.h index 533e107e16..4e929bdf16 100644 --- a/src/Expr.h +++ b/src/Expr.h @@ -86,7 +86,7 @@ public: const; // 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 // as an initialization. The type should be Unref()'d when done @@ -239,7 +239,7 @@ public: ID* Id() const { return id; } 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; int IsPure() const override; @@ -586,7 +586,7 @@ class RefExpr : public UnaryExpr { public: 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; protected: @@ -639,7 +639,7 @@ public: void Add(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; // Need to override Eval since it can take a vector arg but does @@ -671,7 +671,7 @@ public: 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; Expr* MakeLvalue() override; @@ -991,7 +991,7 @@ public: BroType* InitType() const override; Val* InitVal(const BroType* t, Val* aggr) const 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; diff --git a/src/ID.cc b/src/ID.cc index 12677bec75..894de949a5 100644 --- a/src/ID.cc +++ b/src/ID.cc @@ -59,34 +59,14 @@ void ID::ClearVal() 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 ) Unref(val); val = v; weak_ref = arg_weak_ref; + Modified(); #ifdef DEBUG UpdateValID(); @@ -175,16 +155,6 @@ void ID::UpdateValAttrs() if ( ! attrs ) 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 ) val->AsTableVal()->SetAttrs(attrs); @@ -242,16 +212,6 @@ void ID::RemoveAttr(attr_tag a) { if ( attrs ) 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() diff --git a/src/ID.h b/src/ID.h index bb9e11ca06..ee60d4e61c 100644 --- a/src/ID.h +++ b/src/ID.h @@ -5,7 +5,7 @@ #include "Type.h" #include "Attr.h" -#include "StateAccess.h" +#include "Notifier.h" #include "TraverseTypes.h" #include @@ -15,7 +15,7 @@ class Func; typedef enum { INIT_NONE, INIT_FULL, INIT_EXTRA, INIT_REMOVE, } init_class; typedef enum { SCOPE_FUNCTION, SCOPE_MODULE, SCOPE_GLOBAL } IDScope; -class ID : public BroObj { +class ID : public BroObj, public notifier::Modifiable { public: ID(const char* name, IDScope arg_scope, bool arg_is_export); ~ID() override; @@ -46,7 +46,7 @@ public: // reference to the Val, the Val will be destroyed (naturally, // you have to take care that it will not be accessed via // 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(Expr* ev, init_class c); @@ -70,10 +70,6 @@ public: 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 AddAttrs(Attributes* attr); void RemoveAttr(attr_tag a); diff --git a/src/Notifier.cc b/src/Notifier.cc new file mode 100644 index 0000000000..511eb33beb --- /dev/null +++ b/src/Notifier.cc @@ -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); + } diff --git a/src/Notifier.h b/src/Notifier.h new file mode 100644 index 0000000000..59ed506599 --- /dev/null +++ b/src/Notifier.h @@ -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 +#include +#include + +#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 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 diff --git a/src/Op.h b/src/Op.h deleted file mode 100644 index a628a6bb68..0000000000 --- a/src/Op.h +++ /dev/null @@ -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 diff --git a/src/StateAccess.cc b/src/StateAccess.cc deleted file mode 100644 index 997eb5a48d..0000000000 --- a/src/StateAccess.cc +++ /dev/null @@ -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(arg_target); - target_type = TYPE_MVAL; - op1.val = const_cast(arg_op1); - op1_type = TYPE_VAL; - op2 = const_cast(arg_op2); - op3 = const_cast(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(arg_target); - target_type = TYPE_ID; - op1.val = const_cast(arg_op1); - op1_type = TYPE_VAL; - op2 = const_cast(arg_op2); - op3 = const_cast(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(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(arg_op2); - op3 = const_cast(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(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(arg_op2); - op3 = const_cast(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(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); - } - diff --git a/src/StateAccess.h b/src/StateAccess.h deleted file mode 100644 index 15e2ae4676..0000000000 --- a/src/StateAccess.h +++ /dev/null @@ -1,138 +0,0 @@ -// A class describing a state-modyfing access to a Value or an ID. - -#ifndef STATEACESSS_H -#define STATEACESSS_H - -#include -#include -#include - -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 NotifierSet; - typedef std::map NotifierMap; - NotifierMap ids; -}; - -extern NotifierRegistry notifiers; - -#endif diff --git a/src/Stats.cc b/src/Stats.cc index 1d2a2c8ad8..9489f12f93 100644 --- a/src/Stats.cc +++ b/src/Stats.cc @@ -255,7 +255,7 @@ void ProfileLogger::Log() while ( (id = globals->NextEntry(c)) ) // We don't show/count internal globals as they are always // contained in some other global user-visible container. - if ( id->HasVal() && ! id->IsInternalGlobal() ) + if ( id->HasVal() ) { Val* v = id->ID_Val(); diff --git a/src/Trigger.cc b/src/Trigger.cc index 213707b6b8..ae6483e3f5 100644 --- a/src/Trigger.cc +++ b/src/Trigger.cc @@ -33,7 +33,7 @@ TraversalCode TriggerTraversalCallback::PreExpr(const Expr* expr) trigger->Register(e->Id()); Val* v = e->Id()->ID_Val(); - if ( v && v->IsMutableVal() ) + if ( v && v->Modifiable() ) trigger->Register(v); break; }; @@ -382,38 +382,35 @@ void Trigger::Timeout() void Trigger::Register(ID* id) { assert(! disabled); - notifiers.Register(id, this); + notifier::registry.Register(id, this); Ref(id); - ids.insert(id); + objs.push_back({id, id}); } void Trigger::Register(Val* val) { + if ( ! val->Modifiable() ) + return; + assert(! disabled); - notifiers.Register(val, this); + notifier::registry.Register(val->Modifiable(), this); Ref(val); - vals.insert(val); + objs.emplace_back(val, val->Modifiable()); } 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); - Unref(ids[i]); + notifier::registry.Unregister(o.second, this); + Unref(o.first); } - ids.clear(); - - loop_over_list(vals, j) - { - notifiers.Unregister(vals[j], this); - Unref(vals[j]); - } - - vals.clear(); + objs.clear(); } void Trigger::Attach(Trigger *trigger) diff --git a/src/Trigger.h b/src/Trigger.h index 0f7889d19a..2e0c91865f 100644 --- a/src/Trigger.h +++ b/src/Trigger.h @@ -4,7 +4,7 @@ #include #include -#include "StateAccess.h" +#include "Notifier.h" #include "Traverse.h" // Triggers are the heart of "when" statements: expressions that when @@ -13,7 +13,7 @@ class TriggerTimer; class TriggerTraversalCallback; -class Trigger : public NotifierRegistry::Notifier, public BroObj { +class Trigger : public BroObj, public notifier::Receiver { public: // Don't access Trigger objects; they take care of themselves after // instantiation. Note that if the condition is already true, the @@ -61,12 +61,10 @@ public: { d->Add(""); } // Overidden from Notifier. We queue the trigger and evaluate it // later to avoid race conditions. - void Access(ID* id, const StateAccess& sa) override - { QueueTrigger(this); } - void Access(Val* val, const StateAccess& sa) override + void Modified(notifier::Modifiable* m) override { QueueTrigger(this); } - const char* Name() const override; + const char* Name() const; static void QueueTrigger(Trigger* trigger); @@ -104,8 +102,7 @@ private: bool delayed; // true if a function call is currently being delayed bool disabled; - val_list vals; - id_list ids; + std::vector> objs; typedef map ValCache; ValCache cache; diff --git a/src/Type.h b/src/Type.h index 043ec5c928..80548799a0 100644 --- a/src/Type.h +++ b/src/Type.h @@ -695,10 +695,6 @@ bool is_atomic_type(const BroType* t); // True if the given type tag corresponds to a function type. #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. #define IsVector(t) (t == TYPE_VECTOR) diff --git a/src/Val.cc b/src/Val.cc index 04e08feb61..017516acd8 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -380,128 +380,6 @@ bool Val::WouldOverflow(const BroType* from_type, const BroType* to_type, const return false; } -MutableVal::~MutableVal() - { - for ( list::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(this)->Type()->Ref()); - - global_scope()->Insert(name, id); - - id->SetVal(const_cast(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(this)->Type()->Ref()); - global_scope()->Insert(new_name, id); - id->SetVal(const_cast(this), OP_NONE, true); - - Unref(mv->id); - mv->id = 0; - } - IntervalVal::IntervalVal(double quantity, double units) : Val(quantity * units, TYPE_INTERVAL) { @@ -1056,7 +934,7 @@ static void table_entry_val_delete_func(void* val) delete tv; } -TableVal::TableVal(TableType* t, Attributes* a) : MutableVal(t) +TableVal::TableVal(TableType* t, Attributes* a) : Val(t) { Init(t); 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); if ( ! k ) @@ -1185,10 +1063,10 @@ int TableVal::Assign(Val* index, Val* new_val, Opcode op) 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(); @@ -1217,55 +1095,6 @@ int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op) 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. if ( old_entry_val && attrs && attrs->FindAttr(ATTR_EXPIRE_CREATE) ) 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; } + Modified(); return 1; } @@ -1318,15 +1148,13 @@ int TableVal::AddTo(Val* val, int is_first_init, bool propagate_ops) const if ( type->IsSet() ) { - if ( ! t->Assign(v->Value(), k, 0, - propagate_ops ? OP_ASSIGN : OP_NONE) ) + if ( ! t->Assign(v->Value(), k, 0) ) return 0; } else { v->Ref(); - if ( ! t->Assign(0, k, v->Value(), - propagate_ops ? OP_ASSIGN : OP_NONE) ) + if ( ! t->Assign(0, k, v->Value()) ) return 0; } } @@ -1595,11 +1423,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val) if ( v ) { if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) ) - { v->SetExpireAccess(network_time); - if ( LoggingAccess() && ExpirationEnabled() ) - ReadOperation(index, v); - } return v->Value() ? v->Value() : this; } @@ -1626,11 +1450,7 @@ Val* TableVal::Lookup(Val* index, bool use_default_val) if ( v ) { if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) ) - { v->SetExpireAccess(network_time); - if ( LoggingAccess() && ExpirationEnabled() ) - ReadOperation(index, v); - } return v->Value() ? v->Value() : this; } @@ -1684,11 +1504,7 @@ TableVal* TableVal::LookupSubnetValues(const SubNetVal* search) if ( entry ) { if ( attrs && attrs->FindAttr(ATTR_EXPIRE_READ) ) - { entry->SetExpireAccess(network_time); - if ( LoggingAccess() && ExpirationEnabled() ) - ReadOperation(s, entry); - } } Unref(s); // assign does not consume index @@ -1718,8 +1534,6 @@ bool TableVal::UpdateTimestamp(Val* index) return false; v->SetExpireAccess(network_time); - if ( LoggingAccess() && attrs->FindAttr(ATTR_EXPIRE_READ) ) - ReadOperation(index, v); return true; } @@ -1738,25 +1552,10 @@ Val* TableVal::Delete(const Val* index) if ( subnets && ! subnets->Remove(index) ) 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 v; + Modified(); return va; } @@ -1775,9 +1574,7 @@ Val* TableVal::Delete(const HashKey* k) delete v; - if ( LoggingAccess() ) - StateAccess::Log(new StateAccess(OP_DEL, this, k)); - + Modified(); return va; } @@ -1944,7 +1741,7 @@ int TableVal::ExpandCompoundAndInit(val_list* vl, int k, Val* new_val) return 1; } -int TableVal::CheckAndAssign(Val* index, Val* new_val, Opcode op) +int TableVal::CheckAndAssign(Val* index, Val* new_val) { Val* v = 0; if ( subnets ) @@ -1956,7 +1753,7 @@ int TableVal::CheckAndAssign(Val* index, Val* new_val, Opcode op) if ( v ) index->Warn("multiple initializations for index"); - return Assign(index, new_val, op); + return Assign(index, new_val); } void TableVal::InitTimer(double delay) @@ -1988,6 +1785,7 @@ void TableVal::DoExpire(double t) HashKey* k = 0; TableEntryVal* v = 0; TableEntryVal* v_saved = 0; + bool modified = false; for ( int i = 0; i < table_incremental_step && (v = tbl->NextEntry(k, expire_cookie)); ++i ) @@ -2040,18 +1838,18 @@ void TableVal::DoExpire(double t) Unref(index); } - if ( LoggingAccess() ) - StateAccess::Log( - new StateAccess(OP_EXPIRE, this, k)); - tbl->RemoveEntry(k); Unref(v->Value()); delete v; + modified = true; } delete k; } + if ( modified ) + Modified(); + if ( ! v ) { expire_cookie = 0; @@ -2149,26 +1947,6 @@ double TableVal::CallExpireFunc(Val* idx) 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) { auto tv = new TableVal(table_type); @@ -2219,48 +1997,6 @@ Val* TableVal::DoClone(CloneState* state) 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 size = 0; @@ -2282,7 +2018,7 @@ unsigned int TableVal::MemoryAllocation() const vector RecordVal::parse_time_records; -RecordVal::RecordVal(RecordType* t, bool init_fields) : MutableVal(t) +RecordVal::RecordVal(RecordType* t, bool init_fields) : Val(t) { origin = 0; int n = t->NumFields(); @@ -2343,24 +2079,11 @@ RecordVal::~RecordVal() 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); - - 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); + Modified(); } Val* RecordVal::Lookup(int field) const @@ -2570,41 +2293,6 @@ Val* RecordVal::DoClone(CloneState* state) 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 size = 0; @@ -2637,7 +2325,7 @@ Val* EnumVal::DoClone(CloneState* state) return Ref(); } -VectorVal::VectorVal(VectorType* t) : MutableVal(t) +VectorVal::VectorVal(VectorType* t) : Val(t) { vector_type = t->Ref()->AsVectorType(); val.vector_val = new vector(); @@ -2653,7 +2341,7 @@ VectorVal::~VectorVal() delete val.vector_val; } -bool VectorVal::Assign(unsigned int index, Val* element, Opcode op) +bool VectorVal::Assign(unsigned int index, Val* element) { if ( element && ! same_type(element->Type(), vector_type->YieldType(), 0) ) @@ -2669,19 +2357,6 @@ bool VectorVal::Assign(unsigned int index, Val* element, Opcode op) else 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); // 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. (*val.vector_val)[index] = element; + Modified(); return true; } @@ -2725,6 +2401,7 @@ bool VectorVal::Insert(unsigned int index, Val* element) // to do it similarly. val.vector_val->insert(it, element); + Modified(); return true; } @@ -2738,6 +2415,7 @@ bool VectorVal::Remove(unsigned int index) val.vector_val->erase(it); Unref(val_at_index); + Modified(); return true; } @@ -2790,37 +2468,6 @@ unsigned int VectorVal::ResizeAtLeast(unsigned int 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) { auto vv = new VectorVal(vector_type); diff --git a/src/Val.h b/src/Val.h index 5bc5df4da9..43523df26c 100644 --- a/src/Val.h +++ b/src/Val.h @@ -17,7 +17,7 @@ #include "Timer.h" #include "ID.h" #include "Scope.h" -#include "StateAccess.h" +#include "Notifier.h" #include "IPAddr.h" #include "DebugLogger.h" @@ -48,7 +48,6 @@ class RecordVal; class ListVal; class StringVal; class EnumVal; -class MutableVal; class OpaqueVal; class StateAccess; @@ -324,28 +323,13 @@ public: CONST_CONVERTER(TYPE_VECTOR, VectorVal*, AsVectorVal) 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; 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 // For debugging, we keep a reference to the global ID to which a // value has been bound *last*. @@ -514,69 +498,6 @@ private: 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 aliases; - Properties props; - uint64 last_modified; -}; - #define Microseconds 1e-6 #define Milliseconds 1e-3 #define Seconds 1.0 @@ -797,7 +718,7 @@ public: { val = v; last_access_time = network_time; - expire_access_time = last_read_update = + expire_access_time = int(network_time - bro_start_network_time); } @@ -806,7 +727,6 @@ public: auto rval = new TableEntryVal(val ? val->Clone(state) : nullptr); rval->last_access_time = last_access_time; rval->expire_access_time = expire_access_time; - rval->last_read_update = last_read_update; return rval; } @@ -822,24 +742,16 @@ public: void SetExpireAccess(double 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: friend class TableVal; Val* val; double last_access_time; - // The next two entries store seconds since Bro's start. We use - // ints here to save a few bytes, as we do not need a high resolution - // for these anyway. + // The next entry stores seconds since Bro's start. We use ints here + // to save a few bytes, as we do not need a high resolution for these + // anyway. int expire_access_time; - int last_read_update; }; class TableValTimer : public Timer { @@ -856,7 +768,7 @@ protected: }; class CompositeHash; -class TableVal : public MutableVal { +class TableVal : public Val, public notifier::Modifiable { public: explicit TableVal(TableType* t, Attributes* attrs = 0); ~TableVal() override; @@ -866,8 +778,8 @@ public: // 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 // in the second version. - int Assign(Val* index, Val* new_val, Opcode op = OP_ASSIGN); - int Assign(Val* index, HashKey* k, Val* new_val, Opcode op = OP_ASSIGN); + int Assign(Val* index, Val* new_val); + int Assign(Val* index, HashKey* k, Val* new_val); Val* SizeVal() const override { return val_mgr->GetCount(Size()); } @@ -969,19 +881,17 @@ public: HashKey* ComputeHash(const Val* index) const { return table_hash->ComputeHash(index, 1); } + notifier::Modifiable* Modifiable() override { return this; } + protected: friend class Val; - friend class StateAccess; TableVal() {} void Init(TableType* t); void CheckExpireAttr(attr_tag at); int ExpandCompoundAndInit(val_list* vl, int k, Val* new_val); - int CheckAndAssign(Val* index, Val* new_val, Opcode op = OP_ASSIGN); - - bool AddProperties(Properties arg_state) override; - bool RemoveProperties(Properties arg_state) override; + int CheckAndAssign(Val* index, Val* new_val); // Calculates default value for index. Returns 0 if none. Val* Default(Val* index); @@ -998,9 +908,6 @@ protected: // takes ownership of the reference. double CallExpireFunc(Val *idx); - // Propagates a read operation if necessary. - void ReadOperation(Val* index, TableEntryVal *v); - Val* DoClone(CloneState* state) override; TableType* table_type; @@ -1014,7 +921,7 @@ protected: Val* def_val; }; -class RecordVal : public MutableVal { +class RecordVal : public Val, public notifier::Modifiable { public: explicit RecordVal(RecordType* t, bool init_fields = true); ~RecordVal() override; @@ -1022,7 +929,7 @@ public: Val* SizeVal() const override { 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* LookupWithDefault(int field) const; // Does Ref() value. @@ -1061,6 +968,8 @@ public: unsigned int MemoryAllocation() const override; void DescribeReST(ODesc* d) const override; + notifier::Modifiable* Modifiable() override { return this; } + // Extend the underlying arrays of record instances created during // parsing to match the number of fields in the record type (they may // mismatch as a result of parse-time record type redefinitions. @@ -1070,9 +979,6 @@ protected: friend class Val; RecordVal() {} - bool AddProperties(Properties arg_state) override; - bool RemoveProperties(Properties arg_state) override; - Val* DoClone(CloneState* state) override; RecordType* record_type; @@ -1108,7 +1014,7 @@ protected: }; -class VectorVal : public MutableVal { +class VectorVal : public Val, public notifier::Modifiable { public: explicit VectorVal(VectorType* t); ~VectorVal() override; @@ -1122,11 +1028,11 @@ public: // Note: does NOT Ref() the element! Remember to do so unless // the element was just created and thus has refcount 1. // - bool Assign(unsigned int index, Val* element, Opcode op = OP_ASSIGN); - bool Assign(Val* index, Val* element, Opcode op = OP_ASSIGN) + bool Assign(unsigned int index, Val* element); + bool Assign(Val* index, Val* element) { return Assign(index->AsListVal()->Index(0)->CoerceToUnsigned(), - element, op); + element); } // Assigns the value to how_many locations starting at index. @@ -1156,6 +1062,8 @@ public: // Won't shrink size. 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. bool Insert(unsigned int index, Val* element); @@ -1166,8 +1074,6 @@ protected: friend class Val; VectorVal() { } - bool AddProperties(Properties arg_state) override; - bool RemoveProperties(Properties arg_state) override; void ValDescribe(ODesc* d) const override; Val* DoClone(CloneState* state) override; diff --git a/src/input/readers/config/Config.cc b/src/input/readers/config/Config.cc index 4f138c8828..8f0447cf66 100644 --- a/src/input/readers/config/Config.cc +++ b/src/input/readers/config/Config.cc @@ -33,7 +33,7 @@ Config::Config(ReaderFrontend *frontend) : ReaderBackend(frontend) while ( auto id = globals->NextEntry(c) ) { - if ( id->IsInternalGlobal() || ! id->IsOption() ) + if ( ! id->IsOption() ) continue; if ( id->Type()->Tag() == TYPE_RECORD || diff --git a/src/zeek.bif b/src/zeek.bif index 139c77f955..63c5c0c64f 100644 --- a/src/zeek.bif +++ b/src/zeek.bif @@ -1835,7 +1835,7 @@ function global_sizes%(%): var_sizes ID* id; while ( (id = globals->NextEntry(c)) ) - if ( id->HasVal() && ! id->IsInternalGlobal() ) + if ( id->HasVal() ) { Val* id_name = new StringVal(id->Name()); 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); PDict(ID)* globals = global_scope()->Vars(); 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; while ( (id = globals->NextEntry(c)) ) { - if ( id->IsInternalGlobal() ) - continue; - RecordVal* rec = new RecordVal(script_id); rec->Assign(0, new StringVal(type_name(id->Type()->Tag()))); rec->Assign(1, val_mgr->GetBool(id->IsExport())); diff --git a/testing/btest/Baseline/language.when-on-globals/out b/testing/btest/Baseline/language.when-on-globals/out new file mode 100644 index 0000000000..44dae2c89e --- /dev/null +++ b/testing/btest/Baseline/language.when-on-globals/out @@ -0,0 +1,4 @@ +"j" in x3[20]$x, expected timeout +15 in x2, T +x1 != 42, T +x2[10], T diff --git a/testing/btest/language/when-on-globals.zeek b/testing/btest/language/when-on-globals.zeek new file mode 100644 index 0000000000..087a88b4db --- /dev/null +++ b/testing/btest/language/when-on-globals.zeek @@ -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() }; +} +