Remove StateAccess class.

This commit is contained in:
Robin Sommer 2019-06-06 03:11:00 +00:00
parent 02214dafc4
commit 31ddca863c
8 changed files with 14 additions and 744 deletions

View file

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

View file

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

View file

@ -79,7 +79,7 @@ void ID::SetVal(Val* v, Opcode op, bool arg_weak_ref)
#else
if ( debug_logger.IsVerbose() || props )
#endif
StateAccess::Log(new StateAccess(op, this, v, val));
notifiers.Modified(this);
}
if ( ! weak_ref )

View file

@ -4,538 +4,6 @@
#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)
{
if ( access->target_type == TYPE_ID )
{
if ( access->target.id->FindAttr(ATTR_TRACKED) )
notifiers.Modified(access->target.id);
}
else
{
if ( access->target.val->GetProperties() & MutableVal::TRACKED )
notifiers.Modified(access->target.val);
}
#ifdef DEBUG
ODesc desc;
access->Describe(&desc);
DBG_LOG(DBG_STATE, "operation: %s%s",
desc.Description(), replaying > 0 ? " (replay)" : "");
#endif
delete access;
}
NotifierRegistry notifiers;
NotifierRegistry::~NotifierRegistry()

View file

@ -27,69 +27,6 @@ enum Opcode { // Op1 Op2 Op3 (Vals)
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

View file

@ -438,8 +438,6 @@ ID* MutableVal::Bind() const
"%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());
@ -457,8 +455,6 @@ void MutableVal::TransferUniqueID(MutableVal* mv)
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);
@ -1178,55 +1174,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());
@ -1237,6 +1184,7 @@ int TableVal::Assign(Val* index, HashKey* k, Val* new_val, Opcode op)
delete old_entry_val;
}
Modified();
return 1;
}
@ -1556,11 +1504,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;
}
@ -1587,11 +1531,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;
}
@ -1645,11 +1585,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
@ -1679,8 +1615,6 @@ bool TableVal::UpdateTimestamp(Val* index)
return false;
v->SetExpireAccess(network_time);
if ( LoggingAccess() && attrs->FindAttr(ATTR_EXPIRE_READ) )
ReadOperation(index, v);
return true;
}
@ -1699,25 +1633,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;
}
@ -1736,9 +1655,7 @@ Val* TableVal::Delete(const HashKey* k)
delete v;
if ( LoggingAccess() )
StateAccess::Log(new StateAccess(OP_DEL, this, k));
Modified();
return va;
}
@ -1949,6 +1866,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 )
@ -2001,18 +1919,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;
@ -2124,11 +2042,8 @@ void TableVal::ReadOperation(Val* index, TableEntryVal* v)
// 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)
{
@ -2307,21 +2222,8 @@ RecordVal::~RecordVal()
void RecordVal::Assign(int field, Val* new_val, Opcode op)
{
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
@ -2627,19 +2529,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.
@ -2647,6 +2536,7 @@ bool VectorVal::Assign(unsigned int index, Val* element, Opcode op)
// to do it similarly.
(*val.vector_val)[index] = element;
Modified();
return true;
}

View file

@ -51,8 +51,6 @@ class StringVal;
class EnumVal;
class MutableVal;
class StateAccess;
class VectorVal;
class TableEntryVal;
@ -532,17 +530,6 @@ public:
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; }
@ -553,6 +540,7 @@ protected:
friend class Val;
void SetID(ID* arg_id) { Unref(id); id = arg_id; }
void Modified() { notifiers.Modified(this); }
private:
ID* Bind() const;
@ -957,7 +945,6 @@ public:
protected:
friend class Val;
friend class StateAccess;
TableVal() {}
void Init(TableType* t);

View file

@ -1843,17 +1843,6 @@ 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)) )