public Sampler(long timeBase, int divisor, int samplerRate, int maxCallstackDepth, int startDelay, int bufferLength = 8192) { mTimeBase = timeBase; mDivisor = divisor; mSamplerRate = samplerRate; mMaxCallstackDepth = maxCallstackDepth; mStartDelay = startDelay; // allocate buffers mReadData = new Address[bufferLength]; mData = new Address[bufferLength]; // allocate reusable sample class mSample = new Protocol.Sampler_sample(); mSample.ticktimes = new _root.Vector <double>(); mSample.callstack = new _root.Vector <uint>(); mSample.callstack.length = (uint)maxCallstackDepth; mSample.callstack.length = 0; // save target thread id mTargetThread = mach_thread_self(); // setup architecture specific settings var arch = flash.system.Capabilities.cpuArchitecture; switch (arch) { case "ARM": mThreadStateLength = 17; mThreadStateFlavor = 1; mRegisterPC = 15; mRegisterBP = 7; break; case "x86": mThreadStateLength = 32; mThreadStateFlavor = 1; mRegisterPC = 10; mRegisterBP = 6; break; default: Console.WriteLine("Telemetry: Architecture not supported for sampling"); return; } // start sampler thread mSamplerThread = new Thread(SamplerThreadFunc); mSamplerThread.Priority = ThreadPriority.Highest; mSamplerThread.Start(); Console.WriteLine("Telemetry: Sampler started rate: {0} ms", samplerRate); }
// write sampler data to AMF public void Write(MethodMap methodMap, bool combineSamples = true) { // get accumulated sampler data Address[] data = GetSamplerData(); // AMF serializable sample to write to Protocol.Sampler_sample sample = mSample; int lastCallStackIndex = 0; int lastCallStackCount = 0; // process all samples // this code is tricky because it tries to combine consecutive samples with the exact same callstack int sampleCount = 0; int index = 0; for (;;) { // get length of callstack int count = (int)data[index++]; if (count == 0) { break; } // get sample time int time = (int)data[index++]; // compare the last callstack with this one // if they are equal, the samples can be combined // else we have to start a new sample if (!combineSamples || (lastCallStackCount != count) || !ArrayEquals(data, index, lastCallStackIndex, count)) { // call stack is different... if (sample.numticks > 0) { // write last sample to log Session.WriteValueImmediate(sNameSamplerSample, sample); // reset sample for new callstack sample.numticks = 0; sample.ticktimes.length = 0; } // translate callstack to method ids var callstack = sample.callstack; callstack.length = 0; for (int i = 0; i < count; i++) { bool topOfStack; uint methodId = methodMap.GetMethodId(data[index + i], out topOfStack, true); // add method id callstack.push(methodId); // abort callstack if we are at a "top of stack" method if (topOfStack) { break; } } // save last callstack position lastCallStackIndex = index; lastCallStackCount = count; } // add tick to sample sample.numticks++; sample.time = time; sample.ticktimes.push((double)time); // advance to next sample index += count; sampleCount++; } if (sample.numticks > 0) { // write last sample to log Session.WriteValueImmediate(sNameSamplerSample, sample); // reset sample for new callstack sample.numticks = 0; sample.ticktimes.length = 0; } if (sampleCount > 0) { // TODO: Session.WriteValue(".sampler.medianInterval", 1000); Session.WriteValue(".sampler.averageInterval", 1000); Session.WriteValue(".sampler.maxInterval", 1000); } }