using common base class functions to access OCDB objects
[u/mrichter/AliRoot.git] / HLT / trigger / AliHLTTriggerCounterComponent.cxx
CommitLineData
742ae1c4 1// $Id: AliHLTTriggerCounterComponent.cxx 43071 2010-08-25 08:41:44Z richterm $
2/**************************************************************************
3 * This file is property of and copyright by the ALICE HLT Project *
4 * ALICE Experiment at CERN, All rights reserved. *
5 * *
6 * Primary Authors: Artur Szostak <artursz@iafrica.com> *
7 * for The ALICE HLT Project. *
8 * *
9 * Permission to use, copy, modify and distribute this software and its *
10 * documentation strictly for non-commercial purposes is hereby granted *
11 * without fee, provided that the above copyright notice appears in all *
12 * copies and that both the copyright notice and this permission notice *
13 * appear in the supporting documentation. The authors make no claims *
14 * about the suitability of this software for any purpose. It is *
15 * provided "as is" without express or implied warranty. *
16 **************************************************************************/
17
18/// @file AliHLTTriggerCounterComponent.cxx
19/// @author Artur Szostak <artursz@iafrica.com>
20/// @date 3 Nov 2010
21/// @brief Implementation of the AliHLTTriggerCounterComponent class.
22///
23/// The AliHLTTriggerCounterComponent is used to count HLT input and output
24/// triggers based on the global trigger decisions coming from the global HLT
25/// trigger component.
26
27#include "AliHLTTriggerCounterComponent.h"
28#include "AliHLTTriggerDecision.h"
29#include "AliHLTGlobalTriggerDecision.h"
30#include "AliHLTTriggerCounters.h"
31#include "AliHLTCTPData.h"
32#include "AliCDBManager.h"
33#include "AliCDBStorage.h"
34#include "AliCDBEntry.h"
35#include "TObjString.h"
36#include "TROOT.h"
37#include <cstring>
38#include <cassert>
39
40ClassImp(AliHLTTriggerCounterComponent)
41
42const char* AliHLTTriggerCounterComponent::fgkConfigCDBPath = "HLT/ConfigHLT/HLTTriggerCounter";
43TMap AliHLTTriggerCounterComponent::fgInitialCounterConfig(TCollection::kInitHashTableCapacity, 2);
44
45
46AliHLTTriggerCounterComponent::AliHLTTriggerCounterComponent() :
47 AliHLTProcessor(),
48 fOutputMultiplier(0),
49 fInputCounters(),
50 fOutputCounters(),
51 fInputTimes(TCollection::kInitHashTableCapacity, 2),
52 fOutputTimes(TCollection::kInitHashTableCapacity, 2),
53 fLastPublishTime(-1),
247b2cad 54 fPublishPeriod(-1),
660469d3 55 fDefaultMaxIntegrationTime(1.),
247b2cad 56 fCountFalseInputs(false),
57 fCountFalseOutputs(false)
742ae1c4 58{
59 // Default constructor.
60
61 fInputTimes.SetOwner(kTRUE);
62 fOutputTimes.SetOwner(kTRUE);
63}
64
65
66AliHLTTriggerCounterComponent::~AliHLTTriggerCounterComponent()
67{
68 // Default destructor.
69
70}
71
72
73const char* AliHLTTriggerCounterComponent::GetComponentID()
74{
75 // Returns the component ID.
76 return "HLTTriggerCounter";
77}
78
79
80void AliHLTTriggerCounterComponent::GetInputDataTypes(AliHLTComponentDataTypeList& list)
81{
82 // Returns the list of input data types that are handled.
83 list.push_back(kAliHLTDataTypeGlobalTrigger);
84 list.push_back(kAliHLTDataTypeTriggerDecision);
85}
86
87
88AliHLTComponentDataType AliHLTTriggerCounterComponent::GetOutputDataType()
89{
90 // Returns kAliHLTMultipleDataType.
91 return kAliHLTMultipleDataType;
92}
93
94
95int AliHLTTriggerCounterComponent::GetOutputDataTypes(AliHLTComponentDataTypeList& list)
96{
97 // Returns the list of output data block types generated.
98 list.push_back(kAliHLTDataTypeInputTriggerCounters);
99 list.push_back(kAliHLTDataTypeOutputTriggerCounters);
100 return int(list.size());
101}
102
103
104void AliHLTTriggerCounterComponent::GetOutputDataSize(unsigned long& constBase, double& inputMultiplier)
105{
106 // Returns the buffer size requirements.
107 constBase = 1024*16;
108 inputMultiplier = fOutputMultiplier;
109}
110
111
112AliHLTComponent* AliHLTTriggerCounterComponent::Spawn()
113{
114 // Creates a new instance of the component.
115 return new AliHLTTriggerCounterComponent;
116}
117
118
119Int_t AliHLTTriggerCounterComponent::DoInit(int argc, const char** argv)
120{
121 // Initialises the data checker component from the command line.
122
123 HLTInfo("Starting HLT trigger counter component.");
124
125 const char* configFileName = NULL;
126 fInputCounters.Clear();
127 fOutputCounters.Clear();
128 fInputTimes.Clear();
129 fOutputTimes.Clear();
130 fLastPublishTime = -1;
131 fPublishPeriod = -1;
660469d3 132 fDefaultMaxIntegrationTime = -1;
247b2cad 133 fCountFalseInputs = false;
134 fCountFalseOutputs = false;
742ae1c4 135 bool loadCDBObject = true;
136
137 for (int i = 0; i < argc; ++i)
138 {
139 if (strcmp(argv[i], "-config") == 0)
140 {
141 if (configFileName != NULL)
142 {
143 HLTWarning("The configuration macro was already specified."
144 " Will replace previous value given by -config."
145 );
146 }
147 if (argc <= i+1)
148 {
149 HLTError("The configuration macro filename was not specified." );
150 return -EINVAL;
151 }
152 configFileName = argv[i+1];
153 i++;
154 continue;
155 }
156
157 if (strcmp(argv[i], "-publishperiod") == 0)
158 {
159 if (fPublishPeriod != -1)
160 {
161 HLTWarning("The publish period was already specified."
162 " Will replace previous value given by -publishperiod."
163 );
164 }
165 if (argc <= i+1)
166 {
167 HLTError("Publish period value was not specified for -publishperiod.");
168 return -EINVAL;
169 }
170 char* err = NULL;
171 errno = 0;
172 double tmpnum = strtod(argv[i+1], &err);
173 if (err == NULL or *err != '\0')
174 {
175 HLTError("Cannot convert '%s' to a floating point value.", argv[i+1]);
660469d3 176 return -EINVAL;
742ae1c4 177 }
178 if (errno == ERANGE)
179 {
180 HLTError("The specified value '%s' is out of range.", argv[i+1]);
660469d3 181 return -EINVAL;
742ae1c4 182 }
183 fPublishPeriod = (tmpnum < 0 ? -1 : tmpnum);
184 i++;
185 continue;
186 }
187
188 if (strcmp(argv[i], "-skipcdb") == 0)
189 {
190 loadCDBObject = false;
191 continue;
192 }
193
247b2cad 194 if (strcmp(argv[i], "-countfalseinputs") == 0)
195 {
196 fCountFalseInputs = true;
197 continue;
198 }
199
200 if (strcmp(argv[i], "-countfalseoutputs") == 0)
201 {
202 fCountFalseOutputs = true;
203 continue;
204 }
205
660469d3 206 if (strcmp(argv[i], "-integrationtime") == 0)
207 {
208 if (fDefaultMaxIntegrationTime != -1)
209 {
210 HLTWarning("The maximum integration time was already specified."
211 " Will replace previous value given by -integrationtime."
212 );
213 }
214 if (argc <= i+1)
215 {
216 HLTError("A value for the maximum integration time was not specified for -integrationtime.");
217 return -EINVAL;
218 }
219 char* err = NULL;
220 errno = 0;
221 double tmpnum = strtod(argv[i+1], &err);
222 if (err == NULL or *err != '\0')
223 {
224 HLTError("Cannot convert '%s' to a floating point value.", argv[i+1]);
225 return -EINVAL;
226 }
227 if (errno == ERANGE)
228 {
229 HLTError("The specified value '%s' is out of range.", argv[i+1]);
230 return -EINVAL;
231 }
232 if (tmpnum < 0)
233 {
234 HLTError("The specified value '%s' for the integration time must be positive.", argv[i+1]);
235 return -EINVAL;
236 }
237 fDefaultMaxIntegrationTime = tmpnum;
238 i++;
239 continue;
240 }
241
742ae1c4 242 HLTError("Unknown option '%s'.", argv[i]);
243 return -EINVAL;
244 } // for loop
245
660469d3 246 if (fDefaultMaxIntegrationTime == -1) fDefaultMaxIntegrationTime = 1.;
742ae1c4 247 if (configFileName != NULL)
248 {
249 int result = LoadConfigFromFile(configFileName);
250 if (result != 0) return result;
251 }
252 else if (loadCDBObject)
253 {
254 int result = LoadConfigFromCDB(fgkConfigCDBPath);
255 if (result != 0) return result;
256 }
257
660469d3 258 SetupCTPData(); // Setup the CTP accounting in AliHLTComponent.
259
742ae1c4 260 return 0;
261}
262
263
264Int_t AliHLTTriggerCounterComponent::DoDeinit()
265{
266 // Cleans up the component.
267
268 HLTInfo("Stopping HLT trigger counter component.");
269 fInputCounters.Clear();
270 fOutputCounters.Clear();
271 fInputTimes.Clear();
272 fOutputTimes.Clear();
273 return 0;
274}
275
276
277int AliHLTTriggerCounterComponent::DoEvent(const AliHLTComponentEventData& /*evtData*/, AliHLTComponentTriggerData& /*trigData*/)
278{
279 // Finds the global trigger objects and adds the triggers to the counters.
280
281 fInputCounters.UpdateTimeStamp();
282 fOutputCounters.UpdateTimeStamp();
283 Double_t inputTime = fInputCounters.TimeStamp().AsDouble();
284 Double_t outputTime = fOutputCounters.TimeStamp().AsDouble();
285
660469d3 286 // Add the CTP input triggers if available.
287 const AliHLTCTPData* ctp = CTPData();
288 if (ctp != NULL)
289 {
290 const TArrayL64& counters = ctp->Counters();
291 for (Int_t i = 0; i < counters.GetSize(); ++i)
292 {
293 const char* ctpName = ctp->Name(i);
294 // Check if CTP counter is initialised and skip if not.
295 if (strcmp(ctpName, "AliHLTReadoutList") == 0 and counters[i] == 0) continue;
296 TObject* cntobj = fInputCounters.FindObject(ctpName);
297 if (cntobj != NULL)
298 {
299 HLTDebug("Updating existing CTP counter \"%s\".", cntobj->GetName());
300 AliHLTTriggerCounters::AliCounter* counter = static_cast<AliHLTTriggerCounters::AliCounter*>(cntobj);
12d68ce1 301 if (counter->Counter() == ULong64_t(counters[i])) continue;
302 counter->Counter(ULong64_t(counters[i]));
660469d3 303 counter->SetBit(BIT(14), true); // mark counter as incremented
304 UpdateCounterRate(
305 counter,
306 static_cast<AliRingBuffer*>( fInputTimes.FindObject(ctpName) ),
307 inputTime
308 );
309 }
310 else
311 {
312 HLTDebug("Adding new CTP counter \"%s\".", cntobj->GetName());
313 fInputCounters.Add(
314 ctpName,
315 "New CTP trigger input counter found during the run.",
316 Double_t(counters[i]),
317 counters[i]
318 );
319 fInputCounters.GetCounterN(fInputCounters.NumberOfCounters()-1).SetBit(BIT(14), true); // mark counter as incremented
320 fInputTimes.Add(new AliRingBuffer(ctpName, inputTime));
321
322 }
323 }
324 }
325
742ae1c4 326 const TObject* obj = GetFirstInputObject(kAliHLTDataTypeGlobalTrigger, "AliHLTGlobalTriggerDecision");
327 while (obj != NULL)
328 {
247b2cad 329 HLTDebug("Received trigger decision object of type AliHLTGlobalTriggerDecision.");
742ae1c4 330 const AliHLTGlobalTriggerDecision* decision = dynamic_cast<const AliHLTGlobalTriggerDecision*>(obj);
331 if (decision != NULL)
332 {
247b2cad 333 if ((not fCountFalseOutputs and decision->Result()) or fCountFalseOutputs)
742ae1c4 334 {
247b2cad 335 // Tokenise the global trigger description string which contains a
336 // list of the triggers that were fired and increment the corresponding
337 // counters.
338 TString names = decision->Description();
339 Ssiz_t from = 0;
340 TString token;
341 while (names.Tokenize(token, from, ","))
742ae1c4 342 {
247b2cad 343 TObject* cntobj = fOutputCounters.FindObject(token.Data());
344 if (cntobj != NULL)
345 {
346 HLTDebug("Updating existing output counter \"%s\".", cntobj->GetName());
347 AliHLTTriggerCounters::AliCounter* counter = static_cast<AliHLTTriggerCounters::AliCounter*>(cntobj);
348 counter->Increment();
660469d3 349 counter->SetBit(BIT(14), true); // mark counter as incremented
247b2cad 350 UpdateCounterRate(
351 counter,
352 static_cast<AliRingBuffer*>( fOutputTimes.FindObject(token.Data()) ),
353 outputTime
354 );
355 }
356 else
357 {
358 HLTDebug("Adding new output counter \"%s\".", cntobj->GetName());
359 fOutputCounters.Add(token.Data(), "New trigger output counter found during the run.", 1, 1);
660469d3 360 fOutputCounters.GetCounterN(fOutputCounters.NumberOfCounters()-1).SetBit(BIT(14), true); // mark counter as incremented
247b2cad 361 fOutputTimes.Add(new AliRingBuffer(token.Data(), outputTime));
362 }
742ae1c4 363 }
364 }
365
742ae1c4 366 // Add the list of input triggers.
367 for (Int_t i = 0; i < decision->NumberOfTriggerInputs(); ++i)
368 {
369 const AliHLTTriggerDecision* input = decision->TriggerInput(i);
247b2cad 370 if (input == NULL) continue;
371 if (not fCountFalseInputs and not input->Result()) continue;
742ae1c4 372 TObject* cntobj = fInputCounters.FindObject(input->Name());
373 if (cntobj != NULL)
374 {
247b2cad 375 HLTDebug("Updating existing input counter \"%s\".", cntobj->GetName());
742ae1c4 376 AliHLTTriggerCounters::AliCounter* counter = static_cast<AliHLTTriggerCounters::AliCounter*>(cntobj);
377 counter->Increment();
660469d3 378 counter->SetBit(BIT(14), true); // mark counter as incremented
742ae1c4 379 UpdateCounterRate(
380 counter,
381 static_cast<AliRingBuffer*>( fInputTimes.FindObject(input->Name()) ),
382 inputTime
383 );
384 }
385 else
386 {
247b2cad 387 HLTDebug("Adding new input counter \"%s\".", cntobj->GetName());
742ae1c4 388 fInputCounters.Add(input->Name(), "New trigger input counter found during the run.", 1, 1);
660469d3 389 fInputCounters.GetCounterN(fInputCounters.NumberOfCounters()-1).SetBit(BIT(14), true); // mark counter as incremented
742ae1c4 390 fInputTimes.Add(new AliRingBuffer(input->Name(), inputTime));
391 }
392 }
393 }
394 obj = GetNextInputObject();
395 }
396
397 // Look for the individual trigger decision blocks to add if they are available.
398 obj = GetFirstInputObject(kAliHLTDataTypeTriggerDecision, "AliHLTTriggerDecision");
399 while (obj != NULL)
400 {
247b2cad 401 HLTDebug("Received trigger decision object of type AliHLTTriggerDecision.");
742ae1c4 402 const AliHLTTriggerDecision* decision = dynamic_cast<const AliHLTTriggerDecision*>(obj);
247b2cad 403 if (decision != NULL and ((not fCountFalseInputs and decision->Result()) or fCountFalseInputs))
742ae1c4 404 {
742ae1c4 405 TObject* cntobj = fInputCounters.FindObject(decision->Name());
406 if (cntobj != NULL)
407 {
247b2cad 408 HLTDebug("Updating existing input counter \"%s\".", cntobj->GetName());
742ae1c4 409 AliHLTTriggerCounters::AliCounter* counter = static_cast<AliHLTTriggerCounters::AliCounter*>(cntobj);
410 if (not counter->TestBit(BIT(14))) // Only update if marked as not updated.
411 {
412 counter->Increment();
660469d3 413 counter->SetBit(BIT(14), true); // mark counter as incremented
742ae1c4 414 UpdateCounterRate(
415 counter,
416 static_cast<AliRingBuffer*>( fInputTimes.FindObject(decision->Name()) ),
417 inputTime
418 );
419 }
420 }
421 else
422 {
247b2cad 423 HLTDebug("Adding new input counter \"%s\".", cntobj->GetName());
742ae1c4 424 fInputCounters.Add(decision->Name(), "New trigger input counter found during the run.", 1, 1);
660469d3 425 fInputCounters.GetCounterN(fInputCounters.NumberOfCounters()-1).SetBit(BIT(14), true); // mark counter as incremented
742ae1c4 426 fInputTimes.Add(new AliRingBuffer(decision->Name(), inputTime));
427 }
428 }
429 obj = GetNextInputObject();
430 }
660469d3 431
432 // Reset bit 14 which is used temporarily to mark incremented counters.
433 // Any counter which was not marked should have its rate updated.
742ae1c4 434 for (UInt_t i = 0; i < fInputCounters.NumberOfCounters(); ++i)
435 {
660469d3 436 AliHLTTriggerCounters::AliCounter* counter = &fInputCounters.GetCounterN(i);
437 if (not counter->TestBit(BIT(14)))
438 {
439 UpdateCounterRate2(
440 counter,
441 static_cast<AliRingBuffer*>( fInputTimes.FindObject(counter->Name()) ),
442 inputTime
443 );
444 }
445 counter->SetBit(BIT(14), false);
446 }
447 for (UInt_t i = 0; i < fOutputCounters.NumberOfCounters(); ++i)
448 {
449 AliHLTTriggerCounters::AliCounter* counter = &fOutputCounters.GetCounterN(i);
450 if (not counter->TestBit(BIT(14)))
451 {
452 UpdateCounterRate2(
453 counter,
454 static_cast<AliRingBuffer*>( fOutputTimes.FindObject(counter->Name()) ),
455 outputTime
456 );
457 }
458 counter->SetBit(BIT(14), false);
742ae1c4 459 }
460
461 Double_t now = TTimeStamp();
462 if (fLastPublishTime == -1) fLastPublishTime = now;
463 if (now - fLastPublishTime > fPublishPeriod)
464 {
247b2cad 465 HLTDebug("Pushing back counter objects.");
742ae1c4 466 fLastPublishTime = now;
467 bool inputCountersNotPushed = PushBack(&fInputCounters, kAliHLTDataTypeInputTriggerCounters) != 0;
468 bool outputCountersNotPushed = PushBack(&fOutputCounters, kAliHLTDataTypeOutputTriggerCounters) != 0;
469 if (inputCountersNotPushed or outputCountersNotPushed)
470 {
471 fOutputMultiplier = (fOutputMultiplier == 0 ? 1 : fOutputMultiplier*2);
472 return -ENOSPC;
473 }
474 }
475 return 0;
476}
477
478
479void AliHLTTriggerCounterComponent::UpdateCounterRate(
480 AliHLTTriggerCounters::AliCounter* counter, AliRingBuffer* timeBuf, Double_t newTime
481 )
482{
483 // Updates the counter's rate value.
484
485 assert(timeBuf != NULL);
486 Double_t dt = newTime - timeBuf->OldestTime();
487 Double_t rate = (dt != 0 ? (counter->Counter() - timeBuf->OldestCounter()) / dt : 0.);
488 counter->Rate(rate);
489 timeBuf->Increment(counter->Counter(), newTime);
490}
491
492
660469d3 493void AliHLTTriggerCounterComponent::UpdateCounterRate2(
494 AliHLTTriggerCounters::AliCounter* counter, AliRingBuffer* timeBuf, Double_t newTime
495 )
496{
497 // Updates the counter's rate value when counter is not incremented.
498
499 assert(timeBuf != NULL);
500 Double_t dt = newTime - timeBuf->OldestTime();
501 Double_t rate = (dt != 0 ? (counter->Counter() - timeBuf->OldestCounter()) / dt : 0.);
502 counter->Rate(rate);
503 timeBuf->Update(counter->Counter(), newTime);
504}
505
506
742ae1c4 507int AliHLTTriggerCounterComponent::LoadConfigFromCDB(const char* cdbPath)
508{
509 // Loads the initial configuration of counters from the given CDB path.
510
74cbb05a 511 HLTDebug("Trying to load component configuration from '%s'.", cdbPath);
512 TObject* obj = LoadAndExtractOCDBObject(cdbPath);
742ae1c4 513 if (obj == NULL)
514 {
515 HLTError("Configuration object for \"%s\" is missing.", cdbPath);
516 return -ENOENT;
517 }
518 if (obj->IsA() != TMap::Class())
519 {
520 HLTError("Wrong type for configuration object in \"%s\". Found a %s but we expect a TMap.",
521 cdbPath, obj->ClassName()
522 );
523 return -EPROTO;
524 }
525 TMap* counters = static_cast<TMap*>(obj);
526 SetInitialCounters(counters);
527 return 0;
528}
529
530
531int AliHLTTriggerCounterComponent::LoadConfigFromFile(const char* configFile)
532{
533 // Loads the initial configuration of counters from the given configuration file.
534
535 TString cmd = ".x ";
536 cmd += configFile;
537 gROOT->ProcessLine(cmd);
538 SetInitialCounters(&fgInitialCounterConfig);
539 return 0;
540}
541
542
543void AliHLTTriggerCounterComponent::SetInitialCounters(const TMap* counters)
544{
545 // Sets the initial counter values from TMap objects containing name description pairs.
546
547 Double_t now = TTimeStamp();
548 TMapIter next(counters);
549 TObject* key = NULL;
550 while ((key = next()) != NULL)
551 {
552 TObject* value = counters->GetValue(key);
553 if (value == NULL) continue;
660469d3 554 Double_t maxIntegTime = fDefaultMaxIntegrationTime;
555 if (key->GetUniqueID() > 0) maxIntegTime = key->GetUniqueID() * 1e-6;
742ae1c4 556 if (key->TestBit(BIT(14)))
557 {
558 fOutputCounters.Add(key->GetName(), value->GetName());
660469d3 559 fOutputTimes.Add(new AliRingBuffer(key->GetName(), now, maxIntegTime));
742ae1c4 560 }
561 else
562 {
563 fInputCounters.Add(key->GetName(), value->GetName());
660469d3 564 fInputTimes.Add(new AliRingBuffer(key->GetName(), now, maxIntegTime));
742ae1c4 565 }
566 }
567}
660469d3 568
569
570void* AliHLTTriggerCounterComponent::AliRingBuffer::operator new (std::size_t size) throw (std::bad_alloc)
571{
572 // New operator used to catch and log exceptions.
573
574 void* mem = malloc(size);
575 if (mem == NULL)
576 {
577 AliHLTLogging log;
578 log.LoggingVarargs(kHLTLogFatal, Class_Name(), FUNCTIONNAME(), __FILE__, __LINE__,
579 "Could not allocate more space of %d bytes for the ring buffer.", size);
580 throw std::bad_alloc();
581 }
582 return mem;
583}
584
585
586void AliHLTTriggerCounterComponent::AliRingBuffer::operator delete (void* mem) throw ()
587{
588 // Symmetric delete operator to release memory.
589
590 free(mem);
591}
592
593
594void AliHLTTriggerCounterComponent::AliRingBuffer::Increment(ULong64_t newCounter, Double_t newTime)
595{
596 // Inrements the buffer.
597
598 assert(fMaxIntegrationTime >= 0);
599
600 fCounterBuffer[fPos] = newCounter;
601 fTimeBuffer[fPos] = newTime;
602 fPos = (fPos+1) % kTimeStampEntries;
603
604 // We now need to replace all old values.
605 for (int i = 1; i < kTimeStampEntries; ++i)
606 {
607 if (newTime - fTimeBuffer[fPos] < fMaxIntegrationTime) break;
608 fCounterBuffer[fPos] = newCounter;
609 fTimeBuffer[fPos] = newTime;
610 fPos = (fPos+1) % kTimeStampEntries;
611 }
612}
613
614
615void AliHLTTriggerCounterComponent::AliRingBuffer::Update(ULong64_t currentCounter, Double_t newTime)
616{
617 // Removes all old counter measurements.
618
619 assert(fMaxIntegrationTime >= 0);
620 for (int i = 0; i < kTimeStampEntries; ++i)
621 {
622 if (newTime - fTimeBuffer[fPos] < fMaxIntegrationTime) break;
623 fCounterBuffer[fPos] = currentCounter;
624 fTimeBuffer[fPos] = newTime;
625 fPos = (fPos+1) % kTimeStampEntries;
626 }
627}
628