Files
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

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__