internal void ReadBufferEvents(LogHeader logHeader, BufferDescriptor buffer, CachedBinaryReader reader)
        {
            CurrentBuffer = buffer;
            HandleBufferStartRead ();

            while (!reader.IsBufferEmpty) {
                byte info = reader.ReadByte ();
                var type = (byte)(info & 0xF);
                byte extendedInfo = (byte)(info & 0xF0);
                ulong timeDiff;
                switch (type) {
                case EventType.Method: {
                    timeDiff = reader.ReadULeb128 ();
                    Time += timeDiff;
                    buffer.RecordTime (Time);
                    switch (extendedInfo) {
                    case TypeMethod.Leave:
                        var method = reader.ReadSLeb128 ();
                        MethodBase += method;
                        HandleMethodLeave ();
                        break;
                    case TypeMethod.Enter:
                        method = reader.ReadSLeb128 ();
                        MethodBase += method;
                        HandleMethodEnter ();
                        break;
                    case TypeMethod.ExcLeave:
                        method = reader.ReadSLeb128 ();
                        MethodBase += method;
                        HandleMethodExcLeave ();
                        break;
                    case TypeMethod.Jit:
                        method = reader.ReadSLeb128 ();
                        MethodBase += method;
                        var codeAddress = FixPointer (logHeader, buffer.Header.PtrBase + reader.ReadSLeb128 ());
                        var codeSize = reader.ReadULeb128 ();
                        var name = reader.ReadNullTerminatedString ();
                        HandleMethodJit (codeAddress, codeSize, name);
                        break;
                    default:
                        throw new InvalidOperationException ("Unknown method event type:" + extendedInfo);
                    }
                    break;
                }
                case EventType.Alloc: {
                    timeDiff = reader.ReadULeb128 ();
                    Time += timeDiff;
                    buffer.RecordTime (Time);

                    var ptr = reader.ReadSLeb128 ();
                    var obj = reader.ReadSLeb128 ();
                    var size = reader.ReadULeb128 ();
                    Backtrace backtrace;
                    if ((extendedInfo & TypeAlloc.BacktraceBit) == TypeAlloc.BacktraceBit) {
                        backtrace = Backtrace.Read (reader);
                    } else {
                        backtrace = null;
                    }
                    HandleAlloc (ptr, obj, size, backtrace);
                    break;
                }
                case EventType.Gc: {
                    timeDiff = reader.ReadULeb128 ();
                    Time += timeDiff;
                    buffer.RecordTime (Time);

                    switch (extendedInfo) {
                    case TypeGc.Event:

                        var gcEventType = (MonoGCEvent)reader.ReadULeb128 ();
                        var generation = reader.ReadULeb128 ();
                        HandleGc (gcEventType, generation);
                        break;
                    case TypeGc.Resize:
                        var heapSize = reader.ReadULeb128 ();
                        HandleResizeGc (heapSize);
                        break;
                    case TypeGc.Move:
                        ulong num = reader.ReadULeb128 ();
                        var objAddr = new long[num];
                        for (ulong i = 0; i < num; i++) {
                            objAddr [i] = reader.ReadSLeb128 ();
                        }
                        HandleMoveGc (objAddr);
                        break;
                    case TypeGc.HandleCreated:
                        var handleType = (System.Runtime.InteropServices.GCHandleType)reader.ReadULeb128 ();
                        var handle = reader.ReadULeb128 ();
                        var obja = reader.ReadSLeb128 ();
                        HandleHandleCreatedGc (handleType, handle, obja);
                        break;
                    case TypeGc.HandleDestroyed:
                        handleType = (System.Runtime.InteropServices.GCHandleType)reader.ReadULeb128 ();
                        handle = reader.ReadULeb128 ();
                        HandleHandleDestroyedGc (handleType, handle);
                        break;
                    default:
                        throw new InvalidOperationException ("unknown gc type:" + extendedInfo);
                    }
                    break;
                }
                case EventType.Metadata: {
                    timeDiff = reader.ReadULeb128 ();
                    Time += timeDiff;
                    buffer.RecordTime (Time);

                    byte mtype = reader.ReadByte ();
                    long pointer = reader.ReadSLeb128 ();

                    switch (mtype) {
                    case TypeMetadata.Class:
                        var image = reader.ReadSLeb128 ();
                        var flags = reader.ReadULeb128 ();
                        if (flags != 0)
                            throw new Exception ("Flags should be 0");
                        var name = reader.ReadNullTerminatedString ();
                        HandleMetaDataClass (pointer, image, name);
                        break;
                    case TypeMetadata.Image:
                        flags = reader.ReadULeb128 ();
                        if (flags != 0)
                            throw new Exception ("Flags should be 0");
                        name = reader.ReadNullTerminatedString ();
                        HandleMetaDataImage (pointer, name);
                        break;
                    case TypeMetadata.Thread:
                        flags = reader.ReadULeb128 ();
                        if (flags != 0)
                            throw new Exception ("Flags should be 0");
                        name = reader.ReadNullTerminatedString ();
                        HandleMetaDataThread (pointer, name);
                        break;
                    default:
                        throw new InvalidOperationException ("Unknown metadata event type:" + type);
                    }
                    break;
                }
                case EventType.Exception: {
                    timeDiff = reader.ReadULeb128 ();
                    Time += timeDiff;
                    buffer.RecordTime (Time);

                    switch (extendedInfo & (TypeException.BacktraceBit - 1)) {
                    case TypeException.Clause:
                        var clauseType = reader.ReadULeb128 ();
                        var clauseNum = reader.ReadULeb128 ();
                        var method = reader.ReadSLeb128 ();
                        MethodBase += method;
                        HandleExceptionClause (clauseType, clauseNum);
                        break;

                    case TypeException.Throw:
                        var obj = reader.ReadSLeb128 ();
                        Backtrace backtrace;
                        if ((extendedInfo & TypeException.BacktraceBit) == TypeException.BacktraceBit) {
                            backtrace = Backtrace.Read (reader);
                        } else {
                            backtrace = null;
                        }
                        HandleExceptionThrow (obj, backtrace);
                        break;

                    default:
                        throw new InvalidOperationException ("Unknown exception event type:" + (extendedInfo & (TypeException.BacktraceBit - 1)));
                    }
                    break;
                }
                case EventType.Monitor: {
                    timeDiff = reader.ReadULeb128 ();
                    Time += timeDiff;
                    buffer.RecordTime (Time);

                    var obj = reader.ReadSLeb128 ();
                    byte ev = (byte)((extendedInfo >> 4) & 0x3);
                    Backtrace backtrace;
                    if (ev == TypeMonitor.ProfilerMonitorContention && (extendedInfo & TypeMonitor.BacktraceBit) == TypeMonitor.BacktraceBit) {
                        backtrace = Backtrace.Read (reader);
                    } else {
                        backtrace = null;
                    }
                    HandleMonitor (obj, backtrace);
                    break;
                }
                case EventType.Sample:
                    switch (extendedInfo) {
                    case TypeSample.Hit:
                        var sampleType = (SampleType)reader.ReadULeb128 ();
                        var timeStamp = reader.ReadULeb128 ();
                        var count = reader.ReadULeb128 ();
                        var instructionPointers = new long[count];
                        for (ulong i = 0; i < count; i++) {
                            instructionPointers[i] = FixPointer (logHeader, buffer.Header.PtrBase + reader.ReadSLeb128 ());
                        }
                        buffer.RecordTime (timeStamp);
                        HandleSampleHit (sampleType, timeStamp, instructionPointers);
                        break;
                    case TypeSample.USym:
                        var address = FixPointer (logHeader, buffer.Header.PtrBase + reader.ReadSLeb128 ());
                        var size = reader.ReadULeb128 ();
                        var name = reader.ReadNullTerminatedString ();
                        HandleSampleUSym (address, size, name);
                        break;
                    case TypeSample.UBin:
                        timeDiff = reader.ReadULeb128 ();
                        Time += timeDiff;
                        buffer.RecordTime (Time);

                        address = reader.ReadSLeb128 ();
                        var offset = reader.ReadULeb128 ();
                        size = reader.ReadULeb128 ();
                        name = reader.ReadNullTerminatedString ();

                        HandleSampleUBin (address, offset, size, name);
                        break;
                    case TypeSample.CountersDesc:
                        var counters = new List<Tuple<ulong, string, ulong, ulong, ulong, ulong>> ();
                        var len = reader.ReadULeb128 ();

                        for (ulong i = 0; i < len; i++) {
                            var csection = reader.ReadULeb128 ();
                            var cname = reader.ReadNullTerminatedString ();
                            var ctype = reader.ReadULeb128 ();
                            var cunit = reader.ReadULeb128 ();
                            var cvariance = reader.ReadULeb128 ();
                            var cindex = reader.ReadULeb128 ();

                            counters.Add (Tuple.Create<ulong, string, ulong, ulong, ulong, ulong> (
                                csection, cname, ctype, cunit, cvariance, cindex
                            ));
                        }

                        HandleSampleCountersDesc (counters);
                        break;
                    case TypeSample.Counters:
                        var samples = new List<Tuple<ulong, ulong, object>> ();
                        var timestamp = reader.ReadULeb128 ();

                        while (true) {
                            var sindex = reader.ReadULeb128 ();
                            if (sindex == 0)
                                break;

                            object sval;
                            var stype = reader.ReadULeb128 ();
                            switch (stype) {
                            case TypeCountersSample.Int:
                            case TypeCountersSample.Long:
                            case TypeCountersSample.Word:
                            case TypeCountersSample.TimeInterval:
                                CountersSampleValues [sindex] = sval = reader.ReadSLeb128 ()
                                    + (CountersSampleValues.ContainsKey (sindex) ? (long)(CountersSampleValues [sindex]) : 0);
                                break;
                            case TypeCountersSample.UInt:
                            case TypeCountersSample.ULong:
                                CountersSampleValues [sindex] = sval = reader.ReadULeb128 ()
                                    + (CountersSampleValues.ContainsKey (sindex) ? (ulong)(CountersSampleValues [sindex]) : 0);
                                break;
                            case TypeCountersSample.Double:
                                sval = reader.ReadDouble ();
                                break;
                            case TypeCountersSample.String:
                                sval = reader.ReadULeb128 () == 1 ? reader.ReadNullTerminatedString () : null;
                                break;
                            default:
                                throw new InvalidOperationException ("Unknown counter sample type:" + stype);
                            }

                            samples.Add (Tuple.Create<ulong, ulong, object> (sindex, stype, sval));
                        }

                        HandleSampleCounters (timestamp, samples);
                        break;
                    default:
                        throw new InvalidOperationException ("Unknown sample event:" + extendedInfo);
                    }
                    break;
                case EventType.Heap: {
                    switch (extendedInfo) {
                    case TypeHeap.Start:
                        timeDiff = reader.ReadULeb128 ();
                        Time += timeDiff;
                        buffer.RecordTime (Time);

                        HandleHeapStart ();
                        break;
                    case TypeHeap.End:
                        timeDiff = reader.ReadULeb128 ();
                        Time += timeDiff;
                        buffer.RecordTime (Time);

                        HandleHeapEnd ();
                        break;
                    case TypeHeap.Object:
                        var obj = reader.ReadSLeb128 ();
                        var cl = reader.ReadSLeb128 ();
                        var size = reader.ReadULeb128 ();
                        ulong num = reader.ReadULeb128 ();
                        var objectRefs = new long[num];
                        var relOffsets = new ulong[num];
                        for (ulong i = 0; i < num; i++) {
                            relOffsets [i] = reader.ReadULeb128 ();
                            objectRefs [i] = reader.ReadSLeb128 ();
                        }
                        HandleHeapObject (obj, cl, size, relOffsets, objectRefs);
                        break;
                    case TypeHeap.Root:
                        ulong numRoots = reader.ReadULeb128 ();
                        var numGc = reader.ReadULeb128 ();
                        var objects = new long [numRoots];
                        var rootTypes = new MonoProfileGCRootType [numRoots];
                        var extraInfos = new ulong [numRoots];
                        for (ulong i = 0; i < numRoots; i++) {
                            objects[i] = reader.ReadSLeb128 ();
                            rootTypes[i] = (MonoProfileGCRootType)reader.ReadULeb128 ();
                            extraInfos[i] = reader.ReadULeb128 ();
                        }
                            HandleHeapRoot (numRoots, numGc, objects, rootTypes, extraInfos);
                        break;
                    default:
                        throw new InvalidOperationException ("Unknown heap event type:" + extendedInfo);
                    }
                    break;
                }
                default:
                    throw new InvalidOperationException ("invalid event type " + type);
                }
            }

            CurrentBuffer = null;
            HandleBufferEndRead ();
        }
 /// <summary>
 /// if exinfo == TypeHeap.Root
 ///     [num_roots: uleb128] number of root references
 ///     [num_gc: uleb128] number of major gcs
 ///     [object: sleb128] the object as a difference from obj_base
 ///     [root_type: uleb128] the root_type: MonoProfileGCRootType (profiler.h)
 ///     [size: uleb128] size of the object on the heap
 ///     [extra_info: uleb128] the extra_info value
 ///     object, root_type and extra_info are repeated num_roots times
 /// </summary>
 /// <param name="numRoots">The number of root references</param>
 /// <param name="numGc">The number of major gcs.</param>
 /// <param name="obj">The object as a difference from obj_base.</param>
 /// <param name="rootType">The root_type: MonoProfileGCRootType (profiler.h).</param>
 /// <param name="extraInfo">The extra info value.</param>
 public virtual void HandleHeapRoot(ulong numRoots, ulong numGc, long[] obj, MonoProfileGCRootType[] rootType, ulong[] extraInfo)
 {
 }