Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
298 lines
11 KiB
C++
298 lines
11 KiB
C++
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef __avmplus_Sampler__
|
|
#define __avmplus_Sampler__
|
|
|
|
namespace avmplus
|
|
{
|
|
#ifdef DEBUGGER
|
|
|
|
//
|
|
// Note that "SamplerObjectType" is atomlike, but is NOT an Atom:
|
|
//
|
|
// -- the lower three bits are SamplerObjectTypeKind
|
|
// -- if kind is kSOT_Object, upper bits are a VTable*
|
|
// -- if kind is kSOT_String or kSOT_Namespace, upper bits are a Toplevel*
|
|
// -- if kind is kSOT_Empty, upper bits are zero
|
|
//
|
|
// note that for efficiency, the Toplevel* in the String/Namespace case is filled in lazily downstream
|
|
// (via sotSetToplevel), to avoid having String/Namespace ctors look up the proper Toplevel
|
|
// when not sampling.
|
|
//
|
|
// To reinforce the fact that this Isn't An Atom, it's now its own opaque type, with
|
|
// lovely little accessors. Please use them now, rather than casting to intptr and
|
|
// dancing on the bits directly.
|
|
//
|
|
|
|
enum SamplerObjectTypeKind
|
|
{
|
|
kSOT_Object = 0,
|
|
kSOT_String = 1,
|
|
kSOT_Namespace = 2,
|
|
kSOT_Empty = 3
|
|
};
|
|
|
|
REALLY_INLINE SamplerObjectType sotNamespace()
|
|
{
|
|
// start with Toplevel == NULL, filled in implicitly later
|
|
return SamplerObjectType(kSOT_Namespace);
|
|
}
|
|
|
|
REALLY_INLINE SamplerObjectType sotString()
|
|
{
|
|
// start with Toplevel == NULL, filled in implicitly later
|
|
return SamplerObjectType(kSOT_String);
|
|
}
|
|
|
|
REALLY_INLINE SamplerObjectType sotEmpty()
|
|
{
|
|
return SamplerObjectType(kSOT_Empty);
|
|
}
|
|
|
|
REALLY_INLINE SamplerObjectType sotObject(VTable* vt)
|
|
{
|
|
MMGC_STATIC_ASSERT(kSOT_Object == 0);
|
|
return SamplerObjectType(uintptr_t(vt) | uintptr_t(kSOT_Object));
|
|
}
|
|
|
|
REALLY_INLINE SamplerObjectTypeKind sotGetKind(SamplerObjectType sot)
|
|
{
|
|
return SamplerObjectTypeKind(uintptr_t(sot) & 7);
|
|
}
|
|
|
|
REALLY_INLINE VTable* sotGetVTable(SamplerObjectType sot)
|
|
{
|
|
MMGC_STATIC_ASSERT(kSOT_Object == 0);
|
|
AvmAssert(sotGetKind(sot) == kSOT_Object);
|
|
return (VTable*)(sot);
|
|
}
|
|
|
|
REALLY_INLINE Toplevel* sotGetToplevel(SamplerObjectType sot)
|
|
{
|
|
AvmAssert(sotGetKind(sot) == kSOT_String || sotGetKind(sot) == kSOT_Namespace);
|
|
return (Toplevel*)(uintptr_t(sot) & ~7);
|
|
}
|
|
|
|
REALLY_INLINE SamplerObjectType sotSetToplevel(SamplerObjectType sot, Toplevel* toplevel)
|
|
{
|
|
SamplerObjectTypeKind const sk = sotGetKind(sot);
|
|
if (sk == kSOT_String || sk == kSOT_Namespace)
|
|
{
|
|
sot = SamplerObjectType(uintptr_t(toplevel) | uintptr_t(sk));
|
|
}
|
|
return sot;
|
|
}
|
|
|
|
// return value will be a VTable* or a Toplevel* -- intended only for use in adding GC work items
|
|
REALLY_INLINE void* sotGetGCPointer(SamplerObjectType sot)
|
|
{
|
|
return (void*)(uintptr_t(sot) & ~7);
|
|
}
|
|
|
|
// This structure is used to read/write data to the sample stream.
|
|
// The fields are written out to the sample stream as they are defined here.
|
|
struct Sample
|
|
{
|
|
uint64_t micros;
|
|
uint32_t sampleType;
|
|
union {
|
|
// not filled in for sampleType==DELETED_OBJECT_SAMPLE
|
|
struct {
|
|
// Number of StackTraceElements in the trace
|
|
uint32_t depth;
|
|
// Beginning of an array of StackTraceElement. Basically, an MethodInfo*, Stringp, Stringp, uint32_t for each entry.
|
|
void *trace;
|
|
} stack;
|
|
// deleted object size record, instead of stack
|
|
uint64_t size;
|
|
};
|
|
// filled for DELETED_OBJECT_SAMPLE + NEW_OBJECT_SAMPLE
|
|
uint64_t id;
|
|
|
|
// Following three fields are only filled in for sampleType==NEW_OBJECT_SAMPLE or NEW_AUX_SAMPLE
|
|
// They are not present in the sample stream for other sample types
|
|
SamplerObjectType sot;
|
|
void *ptr;
|
|
uint64_t alloc_size; // size for new mem sample
|
|
};
|
|
|
|
// The abstract interface for a memory sampler. Use the AttachSampler() method to attach an
|
|
// IMemorySampler based class to MMgc for getting called back when allocations/deallocations occur.
|
|
class IMemorySampler
|
|
{
|
|
public:
|
|
// A unique ID for identifying the different memory samplers.
|
|
virtual int getSamplerType() = 0;
|
|
|
|
// Called when a new auxiliary memory allocation occurs.
|
|
virtual void recordAllocation(const void *item, size_t size) = 0;
|
|
|
|
// Called when a memory deallocation occurs.
|
|
virtual void recordDeallocation(const void *item, size_t size) = 0;
|
|
|
|
// Called when a new ScriptObject based object has been allocated.
|
|
virtual void recordNewObjectAllocation(AvmPlusScriptableObject *obj, avmplus::SamplerObjectType sot) = 0;
|
|
|
|
// Called when a ScriptObject based object has been reallocated (a back buffer it owns has grown).
|
|
// The object passed in is the dependent object for the ScriptObject. It can be of any type.
|
|
virtual void recordObjectReallocation(const void *obj) = 0;
|
|
|
|
// Call to add a dependent object. A dependent object is one that should be watched for
|
|
// reallocations in recordObjectReallocation(). A dependent object can be of any type.
|
|
virtual void addDependentObject(const avmplus::AvmPlusScriptableObject *obj, const void *depObj) = 0;
|
|
|
|
// Called when a new object of the given type has been allocated. For generic recording of any object type.
|
|
virtual void recordNewObjectAllocation(void *obj, const char *objectType, size_t size) = 0;
|
|
|
|
virtual ~IMemorySampler() {}
|
|
};
|
|
|
|
// Set the current memory sampler. A memory sampler is tied to a particular GC/core pair.
|
|
void AttachSampler(IMemorySampler* sampler);
|
|
|
|
// Get the current memory sampler. Global utility function, should only be used when we
|
|
// don't already have a reference to the current GC. If the GC is known, then GetAttachedSampler
|
|
// should be called on the GC object.
|
|
IMemorySampler* GetSampler();
|
|
|
|
class Sampler : public MMgc::GCRoot, public IMemorySampler
|
|
{
|
|
public:
|
|
enum { SAMPLER_TYPE_AVMPLUS = 1 };
|
|
|
|
Sampler(AvmCore*);
|
|
virtual ~Sampler();
|
|
|
|
enum SampleType
|
|
{
|
|
RAW_SAMPLE=0x55555555,
|
|
NEW_OBJECT_SAMPLE=0xaaaaaaaa,
|
|
DELETED_OBJECT_SAMPLE=0xdddddddd,
|
|
NEW_AUX_SAMPLE=0xeeeeeeee
|
|
};
|
|
|
|
enum { SAMPLE_FREQUENCY_MILLIS = 1 };
|
|
|
|
// should use opaque Cursor type instead of uint8_t*
|
|
uint8_t *getSamples(uint32_t &num);
|
|
void readSample(uint8_t *&p, Sample &s);
|
|
|
|
void init(bool sampling, bool autoStart);
|
|
void sampleCheck() { if(takeSample) sample(); }
|
|
|
|
int getSamplerType() { return SAMPLER_TYPE_AVMPLUS; }
|
|
void recordAllocation(const void *item, size_t size);
|
|
void recordDeallocation(const void *item, size_t size);
|
|
void recordNewObjectAllocation(AvmPlusScriptableObject *obj, avmplus::SamplerObjectType sot);
|
|
void recordObjectReallocation(const void *) {}
|
|
void recordNewObjectAllocation(void *, const char*, size_t) {}
|
|
void addDependentObject(const avmplus::AvmPlusScriptableObject *, const void *) {};
|
|
uint64_t recordAllocationInfo(AvmPlusScriptableObject *obj, SamplerObjectType sot);
|
|
uint64_t recordAllocationSample(const void* item, uint64_t size, bool callback_ok = true, bool forceWrite = false);
|
|
void recordDeallocationSample(const void* item, uint64_t size);
|
|
|
|
virtual void startSampling();
|
|
virtual void stopSampling();
|
|
virtual void clearSamples();
|
|
virtual void pauseSampling();
|
|
|
|
void sampleInternalAllocs(bool b);
|
|
|
|
void setCallback(ScriptObject* callback);
|
|
|
|
|
|
// called by VM after initBuiltin's
|
|
void initSampling();
|
|
|
|
void createFakeFunction(const char *name);
|
|
Stringp getFakeFunctionName(const char* name);
|
|
|
|
void presweep();
|
|
void postsweep();
|
|
|
|
inline uint32_t sampleCount() const { return numSamples; }
|
|
inline bool activelySampling() { return samplingNow; }
|
|
inline bool sampling() const { return _sampling; }
|
|
inline uint64_t getSampleBufferId() const { return sampleBufferId; }
|
|
|
|
private:
|
|
|
|
void sample();
|
|
|
|
void rewind(uint8_t*&b, uint32_t amount)
|
|
{
|
|
b -= amount;
|
|
}
|
|
|
|
int sampleSpaceCheck(bool callback_ok = true);
|
|
|
|
// sampleTimeMicros is the timestamp you want to have written to the sample,
|
|
// or 0 to use the current time.
|
|
void writeRawSample(SampleType sampleType, uint64_t sampleTimeMicros = 0);
|
|
|
|
// Returns the current time, as it should be written into samples.
|
|
static uint64_t nowMicros();
|
|
|
|
// ------------------------ DATA SECTION BEGIN
|
|
public:
|
|
VTable* sampleIteratorVTable;
|
|
VTable* slotIteratorVTable;
|
|
protected:
|
|
AvmCore* core;
|
|
private:
|
|
RCList<String> fakeMethodNames;
|
|
uint64_t allocId;
|
|
uint64_t sampleBufferId;
|
|
uint8_t* samples;
|
|
uint8_t* currentSample;
|
|
uint8_t* lastAllocSample;
|
|
uint64_t lastSampleCheckMicros; // the last time we considered writing a timer sample
|
|
GCMember<ScriptObject> callback;
|
|
uintptr_t timerHandle;
|
|
MMgc::GCHashtableBase<uint64_t,MMgc::GCHashtableKeyHandler,MMgc::GCHashtableAllocHandler_VMPI> uids; // important to use the VMPI variant for non-MMGC-based memory allocation.
|
|
MMgc::GCHashtable_VMPI ptrSamples; // important to use the VMPI variant for non-MMGC-based memory allocation.
|
|
volatile int32_t takeSample;
|
|
uint32_t numSamples;
|
|
uint32_t samples_size;
|
|
bool samplingNow;
|
|
bool samplingAllAllocs;
|
|
bool runningCallback;
|
|
bool autoStartSampling; // if true we call startSampling as early as possible during startup
|
|
bool _sampling; // are we sampling at all
|
|
// ------------------------ DATA SECTION END
|
|
};
|
|
|
|
#define SAMPLE_FRAME(_strp, _core) avmplus::CallStackNode __fcsn((avmplus::AvmCore*)_core, _strp)
|
|
#define SAMPLE_CHECK() __fcsn.sampleCheck();
|
|
|
|
#else
|
|
|
|
#define SAMPLE_FRAME(_x, _s)
|
|
#define SAMPLE_CHECK()
|
|
|
|
#endif // DEBUGGER
|
|
}
|
|
|
|
#ifdef VMCFG_TELEMETRY_SAMPLER
|
|
namespace telemetry
|
|
{
|
|
/* An interface that lets the VM call into the host when it's time to take a sample.
|
|
See the sampler description in AvmCore.h for more details. */
|
|
struct ISampler
|
|
{
|
|
virtual void takeSample() = 0;
|
|
};
|
|
|
|
/* An opaque handle that represents a function on a call stack. A given function should have the same handle for
|
|
the entire duration of a run. */
|
|
typedef avmplus::MethodInfo* FunctionHandle;
|
|
}
|
|
#endif // VMCFG_TELEMETRY_SAMPLER
|
|
|
|
#endif // __avmplus_Sampler__
|
|
|