VirtualBox

Changeset 10868 in vbox


Ignore:
Timestamp:
Jul 24, 2008 6:34:35 PM (16 years ago)
Author:
vboxsync
Message:

PerfAPI: Windows collector re-worked to extract raw counters via WMI. Filtering for queries added. Security initialization is added to svcmain to access perf enumerators.

Location:
trunk/src/VBox/Main
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/MachineImpl.cpp

    r10797 r10868  
    73617361
    73627362#ifdef VBOX_WITH_RESOURCE_USAGE_API
    7363 void Machine::registerMetrics (PerformanceCollector *aCollector)
     7363void Machine::registerMetrics (PerformanceCollector *aCollector, RTPROCESS pid)
    73647364{
    73657365    pm::MetricFactory *metricFactory = aCollector->getMetricFactory();
     
    73677367    pm::SubMetric *cpuLoadUser = new pm::SubMetric ("CPU/Load/User");
    73687368    pm::SubMetric *cpuLoadKernel = new pm::SubMetric ("CPU/Load/Kernel");
     7369    pm::SubMetric *ramUsageUsed  = new pm::SubMetric ("RAM/Usage/Used");
    73697370    /* Create and register base metrics */
    73707371    IUnknown *objptr;
     
    73737374    tmp.queryInterfaceTo (&objptr);
    73747375    pm::BaseMetric *cpuLoad =
    7375         metricFactory->createMachineCpuLoad (objptr, mData->mSession.mPid,
     7376        metricFactory->createMachineCpuLoad (objptr, pid,
    73767377                                             cpuLoadUser, cpuLoadKernel);
    73777378    aCollector->registerBaseMetric (cpuLoad);
     7379    pm::BaseMetric *ramUsage =
     7380        metricFactory->createMachineRamUsage (objptr, pid, ramUsageUsed);
     7381    aCollector->registerBaseMetric (ramUsage);
    73787382
    73797383    aCollector->registerMetric (new pm::Metric(cpuLoad, cpuLoadUser, 0));
     
    73917395    aCollector->registerMetric (new pm::Metric(cpuLoad, cpuLoadKernel,
    73927396                                                new pm::AggregateMax()));
     7397
     7398    aCollector->registerMetric (new pm::Metric(ramUsage, ramUsageUsed, 0));
     7399    aCollector->registerMetric (new pm::Metric(ramUsage, ramUsageUsed,
     7400                                               new pm::AggregateAvg()));
     7401    aCollector->registerMetric (new pm::Metric(ramUsage, ramUsageUsed,
     7402                                               new pm::AggregateMin()));
     7403    aCollector->registerMetric (new pm::Metric(ramUsage, ramUsageUsed,
     7404                                               new pm::AggregateMax()));
    73937405};
    73947406
     
    76147626    }
    76157627
     7628#ifdef VBOX_WITH_RESOURCE_USAGE_API
     7629    registerMetrics (mParent->performanceCollector(), mData->mSession.mPid);
     7630#endif /* VBOX_WITH_RESOURCE_USAGE_API */
     7631
    76167632    /* Confirm a successful initialization when it's the case */
    76177633    autoInitSpan.setSucceeded();
     
    76877703    AutoMultiWriteLock2 alock (mParent, this);
    76887704
     7705#ifdef VBOX_WITH_RESOURCE_USAGE_API
     7706    unregisterMetrics (mParent->performanceCollector());
     7707#endif /* VBOX_WITH_RESOURCE_USAGE_API */
     7708
    76897709    MachineState_T lastState = mData->mMachineState;
    76907710
  • trunk/src/VBox/Main/Makefile.kmk

    r10797 r10868  
    273273VBoxSVC_SOURCES.solaris += solaris/PerformanceSolaris.cpp
    274274VBoxSVC_SOURCES.win     +=     win/PerformanceWin.cpp
    275 VBoxSVC_LDFLAGS.win     += PDH.LIB
     275VBoxSVC_LDFLAGS.win     += wbemuuid.lib
    276276endif
    277277
  • trunk/src/VBox/Main/Performance.cpp

    r10753 r10868  
    2222 */
    2323
     24/*
     25 * @todo list:
     26 *
     27 * 1) Solaris backend
     28 * 2) Linux backend
     29 * 3) Detection of erroneous metric names
     30 * 4) Min/max ranges for metrics
     31 * 5) Darwin backend
     32 * 6) [OS/2 backend]
     33 */
     34
    2435#include <VBox/com/array.h>
    2536#include <VBox/com/ptr.h>
     
    2940#include <iprt/mem.h>
    3041
     42#include "Logging.h"
    3143#include "Performance.h"
    3244
     
    3850{
    3951    Assert(mHAL);
    40     return new HostCpuLoad(mHAL, object, user, kernel, idle);
     52    return new HostCpuLoadRaw(mHAL, object, user, kernel, idle);
    4153}
    4254BaseMetric *MetricFactory::createHostCpuMHz(ComPtr<IUnknown> object, SubMetric *mhz)
     
    5365{
    5466    Assert(mHAL);
    55     return new MachineCpuLoad(mHAL, object, process, user, kernel);
     67    return new MachineCpuLoadRaw(mHAL, object, process, user, kernel);
    5668}
    5769BaseMetric *MetricFactory::createMachineRamUsage(ComPtr<IUnknown> object, RTPROCESS process, SubMetric *used)
     
    7385}
    7486
    75 int CollectorHAL::getRawHostCpuLoad(unsigned long *user, unsigned long *kernel, unsigned long *idle)
     87int CollectorHAL::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
    7688{
    7789    return E_NOTIMPL;
    7890}
    7991
    80 int CollectorHAL::getRawProcessCpuLoad(RTPROCESS process, unsigned long *user, unsigned long *kernel)
     92int CollectorHAL::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
    8193{
    8294    return E_NOTIMPL;
     
    90102        {
    91103            mLastSampleTaken = nowAt;
     104            LogFlowThisFunc (("Collecting data for obj(%p)...\n", mObject));
    92105            collect();
    93106        }
    94107    }
    95108}
     109
     110/*bool BaseMetric::associatedWith(ComPtr<IUnknown> object)
     111{
     112    LogFlowThisFunc (("mObject(%p) == object(%p) is %s.\n", mObject, object, mObject == object ? "true" : "false"));
     113    return mObject == object;
     114}*/
    96115
    97116void HostCpuLoad::init(unsigned long period, unsigned long length)
     
    118137void HostCpuLoadRaw::collect()
    119138{
    120     unsigned long user, kernel, idle;
    121     unsigned long userDiff, kernelDiff, idleDiff, totalDiff;
     139    uint64_t user, kernel, idle;
     140    uint64_t userDiff, kernelDiff, idleDiff, totalDiff;
    122141
    123142    int rc = mHAL->getRawHostCpuLoad(&user, &kernel, &idle);
     
    128147        idleDiff   = idle   - mIdlePrev;
    129148        totalDiff  = userDiff + kernelDiff + idleDiff;
    130    
    131         mUser->put(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff);
    132         mKernel->put(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff);
    133         mIdle->put(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff);
     149
     150        if (totalDiff == 0)
     151        {
     152            /* This is only possible if none of counters has changed! */
     153            LogFlowThisFunc (("Impossible! User, kernel and idle raw "
     154                "counters has not changed since last sample.\n" ));
     155            mUser->put(0);
     156            mKernel->put(0);
     157            mIdle->put(0);
     158        }
     159        else
     160        {
     161            mUser->put((unsigned long)(PM_CPU_LOAD_MULTIPLIER * userDiff / totalDiff));
     162            mKernel->put((unsigned long)(PM_CPU_LOAD_MULTIPLIER * kernelDiff / totalDiff));
     163            mIdle->put((unsigned long)(PM_CPU_LOAD_MULTIPLIER * idleDiff / totalDiff));
     164        }
    134165   
    135166        mUserPrev   = user;
     
    198229void MachineCpuLoadRaw::collect()
    199230{
    200     unsigned long hostUser, hostKernel, hostIdle, hostTotal;
    201     unsigned long processUser, processKernel;
    202 
    203     int rc = mHAL->getRawHostCpuLoad(&hostUser, &hostKernel, &hostIdle);
    204     if (RT_SUCCESS(rc))
    205     {
    206         hostTotal = hostUser + hostKernel + hostIdle;
     231    uint64_t processUser, processKernel, hostTotal;
     232
     233    int rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel, &hostTotal);
     234    if (RT_SUCCESS(rc))
     235    {
     236        if (hostTotal == mHostTotalPrev)
     237        {
     238            /* Nearly impossible, but... */
     239            mUser->put(0);
     240            mKernel->put(0);
     241        }
     242        else
     243        {
     244            mUser->put((unsigned long)(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev)));
     245            mKernel->put((unsigned long)(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev)));
     246        }
    207247   
    208         rc = mHAL->getRawProcessCpuLoad(mProcess, &processUser, &processKernel);
    209         AssertRC(rc);
    210         if (RT_SUCCESS(rc))
    211         {
    212             mUser->put(PM_CPU_LOAD_MULTIPLIER * (processUser - mProcessUserPrev) / (hostTotal - mHostTotalPrev));
    213             mUser->put(PM_CPU_LOAD_MULTIPLIER * (processKernel - mProcessKernelPrev ) / (hostTotal - mHostTotalPrev));
    214        
    215             mHostTotalPrev     = hostTotal;
    216             mProcessUserPrev   = processUser;
    217             mProcessKernelPrev = processKernel;
    218         }
     248        mHostTotalPrev     = hostTotal;
     249        mProcessUserPrev   = processUser;
     250        mProcessKernelPrev = processKernel;
    219251    }
    220252}
     
    270302        // Copy the wrapped part
    271303        if (mEnd)
    272             memcpy(data + mEnd, mData, mEnd * sizeof(unsigned long));
     304            memcpy(data + (mLength - mEnd), mData, mEnd * sizeof(unsigned long));
    273305    }
    274306    else
  • trunk/src/VBox/Main/PerformanceImpl.cpp

    r10779 r10868  
    202202                                  ComSafeArrayOut (IPerformanceMetric *, outMetrics))
    203203{
     204    LogFlowThisFuncEnter();
    204205    //LogFlowThisFunc (("mState=%d, mType=%d\n", mState, mType));
    205206
     
    234235    }
    235236    retMetrics.detachTo (ComSafeArrayOutArg(outMetrics));
     237    LogFlowThisFuncLeave();
    236238    return rc;
    237239}
     
    271273                       ComSafeArrayInArg (objects));
    272274
    273     /// @todo (r=dmik) why read lock below? individual elements get modified!
    274 
    275     AutoReadLock alock (this); /* Need a read lock to access mBaseMetrics */
    276                                /* Write lock is not needed since we are */
    277                                /* fiddling with enable bit only. No harm */
    278                                /* the readers may see it differently. */
     275    AutoWriteLock alock (this); /* Write lock is not needed atm since we are */
     276                                /* fiddling with enable bit only, but we */
     277                                /* care for those who come next :-). */
    279278
    280279    BaseMetricList::iterator it;
     
    296295                       ComSafeArrayInArg (objects));
    297296
    298     /// @todo (r=dmik) why read lock below? individual elements get modified!
    299 
    300     AutoReadLock alock (this); /* Need a read lock to access mBaseMetrics */
    301                                /* Write lock is not needed since we are */
    302                                /* fiddling with enable bit only. No harm */
    303                                /* the readers may see it differently. */
     297    AutoWriteLock alock (this); /* Write lock is not needed atm since we are */
     298                                /* fiddling with enable bit only, but we */
     299                                /* care for those who come next :-). */
    304300
    305301    BaseMetricList::iterator it;
     
    323319    CheckComRCReturnRC (autoCaller.rc());
    324320
     321    pm::Filter filter (ComSafeArrayInArg (metricNames),
     322                       ComSafeArrayInArg (objects));
     323
    325324    AutoReadLock alock (this);
    326325
    327     int i;
    328     MetricList::const_iterator it;
    329326    /* Let's compute the size of the resulting flat array */
    330     size_t flatSize = 0, numberOfMetrics = 0;
     327    size_t flatSize = 0;
     328    MetricList filteredMetrics;
     329    MetricList::iterator it;
    331330    for (it = m.metrics.begin(); it != m.metrics.end(); ++it)
    332     {
    333         /* @todo Filtering goes here! */
    334         flatSize += (*it)->getLength();
    335         ++numberOfMetrics;
    336     }
     331        if (filter.match ((*it)->getObject(), (*it)->getName()))
     332        {
     333            filteredMetrics.push_back (*it);
     334            flatSize += (*it)->getLength();
     335        }
     336
     337    int i = 0;
    337338    size_t flatIndex = 0;
     339    size_t numberOfMetrics = filteredMetrics.size();
    338340    com::SafeArray <BSTR> retNames (numberOfMetrics);
    339341    com::SafeIfaceArray <IUnknown> retObjects (numberOfMetrics);
     
    342344    com::SafeArray <LONG> retData (flatSize);
    343345
    344     for (it = m.metrics.begin(), i = 0; it != m.metrics.end(); ++it)
     346    for (it = filteredMetrics.begin(); it != filteredMetrics.end(); ++it, ++i)
    345347    {
    346348        /* @todo Filtering goes here! */
     
    354356        retLengths[i] = length;
    355357        retIndices[i] = flatIndex;
    356         ++i;
    357358        flatIndex += length;
    358359    }
     
    371372void PerformanceCollector::registerBaseMetric (pm::BaseMetric *baseMetric)
    372373{
     374    LogFlowThisFuncEnter();
    373375    AutoCaller autoCaller (this);
    374376    if (!SUCCEEDED (autoCaller.rc())) return;
    375377
    376378    AutoWriteLock alock (this);
     379    LogFlowThisFunc (("obj=%p name=%s\n", baseMetric->getObject(), baseMetric->getName()));
    377380    m.baseMetrics.push_back (baseMetric);
     381    LogFlowThisFuncLeave();
    378382}
    379383
    380384void PerformanceCollector::registerMetric (pm::Metric *metric)
    381385{
     386    LogFlowThisFuncEnter();
    382387    AutoCaller autoCaller (this);
    383388    if (!SUCCEEDED (autoCaller.rc())) return;
    384389
    385390    AutoWriteLock alock (this);
     391    LogFlowThisFunc (("obj=%p name=%s\n", metric->getObject(), metric->getName()));
    386392    m.metrics.push_back (metric);
     393    LogFlowThisFuncLeave();
    387394}
    388395
    389396void PerformanceCollector::unregisterBaseMetricsFor (const ComPtr <IUnknown> &aObject)
    390397{
     398    LogFlowThisFuncEnter();
    391399    AutoCaller autoCaller (this);
    392400    if (!SUCCEEDED (autoCaller.rc())) return;
    393401
    394402    AutoWriteLock alock (this);
    395     std::remove_if (m.baseMetrics.begin(), m.baseMetrics.end(),
    396                     std::bind2nd (std::mem_fun (&pm::BaseMetric::associatedWith),
    397                                   aObject));
     403    LogFlowThisFunc (("before remove_if: m.baseMetrics.size()=%d, obj=%p\n", m.baseMetrics.size(), aObject));
     404    BaseMetricList::iterator it = std::remove_if (
     405        m.baseMetrics.begin(), m.baseMetrics.end(), std::bind2nd (
     406            std::mem_fun (&pm::BaseMetric::associatedWith), aObject));
     407    m.baseMetrics.erase(it, m.baseMetrics.end());
     408    LogFlowThisFunc (("after remove_if: m.baseMetrics.size()=%d\n", m.baseMetrics.size()));
     409    LogFlowThisFuncLeave();
    398410}
    399411
    400412void PerformanceCollector::unregisterMetricsFor (const ComPtr <IUnknown> &aObject)
    401413{
     414    LogFlowThisFuncEnter();
    402415    AutoCaller autoCaller (this);
    403416    if (!SUCCEEDED (autoCaller.rc())) return;
    404417
    405418    AutoWriteLock alock (this);
    406     std::remove_if (m.metrics.begin(), m.metrics.end(),
    407                     std::bind2nd (std::mem_fun (&pm::Metric::associatedWith),
    408                                   aObject));
     419    LogFlowThisFunc (("obj=%p\n", aObject));
     420    MetricList::iterator it = std::remove_if (
     421        m.metrics.begin(), m.metrics.end(), std::bind2nd (
     422            std::mem_fun (&pm::Metric::associatedWith), aObject));
     423    m.metrics.erase(it, m.metrics.end());
     424    LogFlowThisFuncLeave();
    409425}
    410426
     
    427443void PerformanceCollector::samplerCallback()
    428444{
     445    LogFlowThisFuncEnter();
    429446    AutoWriteLock alock (this);
    430447
     
    433450                   std::bind2nd (std::mem_fun (&pm::BaseMetric::collectorBeat),
    434451                                 timestamp));
     452    LogFlowThisFuncLeave();
    435453}
    436454
  • trunk/src/VBox/Main/include/MachineImpl.h

    r10797 r10868  
    719719
    720720#ifdef VBOX_WITH_RESOURCE_USAGE_API
    721     void registerMetrics (PerformanceCollector *aCollector);
     721    void registerMetrics (PerformanceCollector *aCollector, RTPROCESS pid);
    722722    void unregisterMetrics (PerformanceCollector *aCollector);
    723723#endif /* VBOX_WITH_RESOURCE_USAGE_API */
  • trunk/src/VBox/Main/include/Performance.h

    r10770 r10868  
    7272        virtual int getProcessMemoryUsage(RTPROCESS process, unsigned long *used) = 0;
    7373
    74         virtual int getRawHostCpuLoad(unsigned long *user, unsigned long *kernel, unsigned long *idle);
    75         virtual int getRawProcessCpuLoad(RTPROCESS process, unsigned long *user, unsigned long *kernel);
     74        virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
     75        virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
    7676    };
    7777
     
    137137        void collect();
    138138    private:
    139         unsigned long mUserPrev;
    140         unsigned long mKernelPrev;
    141         unsigned long mIdlePrev;
     139        uint64_t mUserPrev;
     140        uint64_t mKernelPrev;
     141        uint64_t mIdlePrev;
    142142    };
    143143
     
    199199        void collect();
    200200    private:
    201         unsigned long mHostTotalPrev;
    202         unsigned long mProcessUserPrev;
    203         unsigned long mProcessKernelPrev;
     201        uint64_t mHostTotalPrev;
     202        uint64_t mProcessUserPrev;
     203        uint64_t mProcessKernelPrev;
    204204    };
    205205
     
    305305    public:
    306306        MetricFactorySolaris();
    307         virtual BaseMetric   *createHostCpuLoad(ComPtr<IUnknown> object, SubMetric *user, SubMetric *kernel, SubMetric *idle);
    308         virtual BaseMetric   *createMachineCpuLoad(ComPtr<IUnknown> object, RTPROCESS process, SubMetric *user, SubMetric *kernel);
     307        // Nothing else to do here (yet)
    309308    };
    310309
     
    313312    public:
    314313        MetricFactoryLinux();
    315         virtual BaseMetric   *createHostCpuLoad(ComPtr<IUnknown> object, SubMetric *user, SubMetric *kernel, SubMetric *idle);
    316         virtual BaseMetric   *createMachineCpuLoad(ComPtr<IUnknown> object, RTPROCESS process, SubMetric *user, SubMetric *kernel);
     314        // Nothing else to do here (yet)
    317315    };
    318316
  • trunk/src/VBox/Main/linux/PerformanceLinux.cpp

    r10754 r10868  
    3737    virtual int getProcessMemoryUsage(RTPROCESS process, unsigned long *used);
    3838
    39     virtual int getRawHostCpuLoad(unsigned long *user, unsigned long *kernel, unsigned long *idle);
    40     virtual int getRawProcessCpuLoad(RTPROCESS process, unsigned long *user, unsigned long *kernel);
     39    virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
     40    virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
    4141};
    4242
     
    4949}
    5050
    51 BaseMetric *MetricFactoryLinux::createHostCpuLoad(ComPtr<IUnknown> object, SubMetric *user, SubMetric *kernel, SubMetric *idle)
    52 {
    53     Assert(mHAL);
    54     return new HostCpuLoadRaw(mHAL, object, user, kernel, idle);
    55 }
    56 
    57 BaseMetric *MetricFactoryLinux::createMachineCpuLoad(ComPtr<IUnknown> object, RTPROCESS process, SubMetric *user, SubMetric *kernel)
    58 {
    59     Assert(mHAL);
    60     return new MachineCpuLoadRaw(mHAL, object, process, user, kernel);
    61 }
    62 
    6351// Collector HAL for Linux
    6452
    65 int CollectorLinux::getRawHostCpuLoad(unsigned long *user, unsigned long *kernel, unsigned long *idle)
     53int CollectorLinux::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
    6654{
    6755    int rc = VINF_SUCCESS;
    68     unsigned long nice;
     56    unsigned long u32user, u32nice, u32kernel, u32idle;
    6957    FILE *f = fopen("/proc/stat", "r");
    7058
    7159    if (f)
    7260    {
    73         if (fscanf(f, "cpu %lu %lu %lu %lu", user, &nice, kernel, idle) == 4)
    74             *user += nice;
     61        if (fscanf(f, "cpu %lu %lu %lu %lu", &u32user, &u32nice, &u32kernel, &u32idle) == 4)
     62        {
     63            *user   = (uint64_t)u32user + u32nice;
     64            *kernel = u32kernel;
     65            *idle   = u32idle;
     66        }
    7567        else
    7668            rc = VERR_FILE_IO_ERROR;
     
    8375}
    8476
    85 int CollectorLinux::getRawProcessCpuLoad(RTPROCESS process, unsigned long *user, unsigned long *kernel)
     77int CollectorLinux::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
    8678{
    8779    int rc = VINF_SUCCESS;
     
    9183    int iTmp;
    9284    unsigned uTmp;
    93     unsigned long ulTmp;
     85    unsigned long ulTmp, u32user, u32kernel;
    9486    char buf[80]; /* @todo: this should be tied to max allowed proc name. */
     87
     88    uint64_t uHostUser, uHostKernel, uHostIdle;
     89    rc = getRawHostCpuLoad(uHostUser, uHostKernel, uHostIdle);
     90    if (RT_FAILURE(rc))
     91        return rc;
     92    *total = (uint64_t)uHostUser + uHostKernel + uHostIdle;
    9593
    9694    RTStrAPrintf(&pszName, "/proc/%d/stat", process);
     
    103101        if (fscanf(f, "%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu",
    104102                   &pid2, buf, &c, &iTmp, &iTmp, &iTmp, &iTmp, &iTmp, &uTmp,
    105                    &ulTmp, &ulTmp, &ulTmp, &ulTmp, user, kernel) == 15)
     103                   &ulTmp, &ulTmp, &ulTmp, &ulTmp, &u32user, &u32kernel) == 15)
    106104        {
    107105            Assert((pid_t)process == pid2);
     106            *user = u32user;
     107            *kernel = u32kernel;
    108108        }
    109109        else
  • trunk/src/VBox/Main/testcase/Makefile.kmk

    r10056 r10868  
    6868tstAPI_DEPS     = $(VBOX_PATH_SDK)/include/VirtualBox_XPCOM.h
    6969endif
     70ifdef VBOX_WITH_RESOURCE_USAGE_API
     71tstAPI_DEFS += VBOX_WITH_RESOURCE_USAGE_API
     72endif
    7073
    7174
  • trunk/src/VBox/Main/testcase/tstAPI.cpp

    r10783 r10868  
    926926#endif
    927927
    928 #if 1
     928#ifdef VBOX_WITH_RESOURCE_USAGE_API
    929929    do {
    930930        // Get collector
     
    948948        printf ("Getting a machine object named '%ls'...\n", name.raw());
    949949        CHECK_RC_BREAK (virtualBox->FindMachine (name, machine.asOutParam()));
    950 
    951         // Setup base metrics
    952         com::SafeIfaceArray<IUnknown> objects(2);
    953         host.queryInterfaceTo(&objects[0]);
    954         machine.queryInterfaceTo(&objects[1]);
    955         CHECK_ERROR_BREAK (collector, SetupMetrics(ComSafeArrayAsInParam(baseMetrics),
    956                                                    ComSafeArrayAsInParam(objects), 1u, 10u) );
    957950
    958951        // Open session
     
    969962        CHECK_RC_BREAK (session->COMGETTER(Machine) (sessionMachine.asOutParam()));
    970963
     964        // Setup base metrics
     965        // Note that one needs to set up metrics after a session is open for a machine.
     966        com::SafeIfaceArray<IUnknown> objects(2);
     967        host.queryInterfaceTo(&objects[0]);
     968        sessionMachine.queryInterfaceTo(&objects[1]);
     969        CHECK_ERROR_BREAK (collector, SetupMetrics(ComSafeArrayAsInParam(baseMetrics),
     970                                                   ComSafeArrayAsInParam(objects), 1u, 10u) );
     971
    971972        // Get console
    972973        ComPtr <IConsole> console;
     
    984985        CHECK_RC (console->Pause());
    985986
    986         RTThreadSleep(3000); // Sleep for 10 seconds
     987        RTThreadSleep(10000); // Sleep for 10 seconds
    987988
    988989        printf("Metrics collected with DSL machine paused: ---------------------\n");
     
    997998        session->Close();
    998999    } while (false);
    999 #endif
     1000#endif /* VBOX_WITH_RESOURCE_USAGE_API */
    10001001
    10011002    printf ("Press enter to release Session and VirtualBox instances...");
     
    10171018}
    10181019
    1019 #if 1
     1020#ifdef VBOX_WITH_RESOURCE_USAGE_API
    10201021void queryMetrics (ComPtr <IPerformanceCollector> collector,
    10211022                   ComSafeArrayIn (IUnknown *, objects))
     
    10231024    HRESULT rc;
    10241025
    1025     Bstr metricNames[] = { L"CPU/Load/User:avg,CPU/Load/System:avg,CPU/Load/Idle:avg,RAM/Usage/Total,RAM/Usage/Used:avg" };
     1026    //Bstr metricNames[] = { L"CPU/Load/User:avg,CPU/Load/System:avg,CPU/Load/Idle:avg,RAM/Usage/Total,RAM/Usage/Used:avg" };
     1027    Bstr metricNames[] = { L"*" };
    10261028    com::SafeArray<BSTR> metrics (1);
    10271029    metricNames[0].cloneTo (&metrics [0]);
     
    10631065    }
    10641066}
    1065 #endif
     1067#endif /* VBOX_WITH_RESOURCE_USAGE_API */
  • trunk/src/VBox/Main/win/PerformanceWin.cpp

    r10753 r10868  
    2222 */
    2323
    24 #include <pdh.h>
    25 #include <pdhmsg.h>
     24#include <Wbemidl.h>
    2625#include <iprt/err.h>
    2726
     27#include "Logging.h"
    2828#include "Performance.h"
    29 
    30 //#pragma comment(lib, "pdh.lib")
    3129
    3230namespace pm {
     
    4442    virtual int getProcessMemoryUsage(RTPROCESS process, unsigned long *used);
    4543
     44    virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
     45    virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
    4646private:
    47     int         convertPdhStatusToRTErr(PDH_STATUS pdhStatus);
    48     HCOUNTER    addCounter(HQUERY hQuery, LPCTSTR name);
    49     int         getCounterValue(HCOUNTER hCounter, DWORD flags, unsigned long *value);
    50 
    51     HQUERY      mHostCpuLoadQuery;
    52     HCOUNTER    mHostCpuLoadUserCounter;
    53     HCOUNTER    mHostCpuLoadKernelCounter;
    54     HCOUNTER    mHostCpuLoadIdleCounter;
     47    long        getPropertyHandle(IWbemObjectAccess *objAccess, LPCWSTR name);
     48    int         getObjects(IWbemHiPerfEnum *mEnum, IWbemObjectAccess ***objArray, DWORD *numReturned);
     49
     50    IWbemRefresher     *mRefresher;
     51    IWbemServices      *mNameSpace;
     52
     53    IWbemHiPerfEnum    *mEnumProcessor;
     54    long               mEnumProcessorID;
     55    long               mHostCpuLoadNameHandle;
     56    long               mHostCpuLoadUserHandle;
     57    long               mHostCpuLoadKernelHandle;
     58    long               mHostCpuLoadIdleHandle;
     59
     60    IWbemHiPerfEnum    *mEnumProcess;
     61    long               mEnumProcessID;
     62    long               mProcessPIDHandle;
     63    long               mProcessCpuLoadUserHandle;
     64    long               mProcessCpuLoadKernelHandle;
     65    long               mProcessCpuLoadTimestampHandle;
     66    long               mProcessMemoryUsedHandle;
    5567};
    5668
     
    6173}
    6274
    63 CollectorWin::CollectorWin() : mHostCpuLoadQuery(0)
    64 {
    65     PDH_STATUS pdhStatus;
    66 
    67     pdhStatus = PdhOpenQuery(NULL, NULL, &mHostCpuLoadQuery);
    68     if (pdhStatus != ERROR_SUCCESS)
     75CollectorWin::CollectorWin() : mRefresher(0), mNameSpace(0), mEnumProcessor(0), mEnumProcess(0)
     76{
     77    HRESULT                 hr = S_OK;
     78    IWbemConfigureRefresher *pConfig = NULL;
     79    IWbemLocator            *pWbemLocator = NULL;
     80    BSTR                    bstrNameSpace = NULL;
     81
     82    if (SUCCEEDED (hr = CoCreateInstance(
     83        CLSID_WbemLocator,
     84        NULL,
     85        CLSCTX_INPROC_SERVER,
     86        IID_IWbemLocator,
     87        (void**) &pWbemLocator)))
     88    {
     89        // Connect to the desired namespace.
     90        bstrNameSpace = SysAllocString(L"\\\\.\\root\\cimv2");
     91        if (bstrNameSpace)
     92        {
     93            hr = pWbemLocator->ConnectServer(
     94                bstrNameSpace,
     95                NULL, // User name
     96                NULL, // Password
     97                NULL, // Locale
     98                0L,   // Security flags
     99                NULL, // Authority
     100                NULL, // Wbem context
     101                &mNameSpace);
     102        }
     103        pWbemLocator->Release();
     104        SysFreeString(bstrNameSpace);
     105    }
     106
     107    if (FAILED (hr)) {
     108        Log (("Failed to get namespace. HR = %x\n", hr));
    69109        return;
    70 
    71     mHostCpuLoadUserCounter   = addCounter (mHostCpuLoadQuery,
    72                                             L"\\Processor(_Total)\\% User Time");
    73     mHostCpuLoadKernelCounter = addCounter (mHostCpuLoadQuery,
    74                                             L"\\Processor(_Total)\\% Privileged Time");
    75     mHostCpuLoadIdleCounter   = addCounter (mHostCpuLoadQuery,
    76                                             L"\\Processor(_Total)\\% Idle Time");
     110    }
     111
     112    if (SUCCEEDED (hr = CoCreateInstance(
     113        CLSID_WbemRefresher,
     114        NULL,
     115        CLSCTX_INPROC_SERVER,
     116        IID_IWbemRefresher,
     117        (void**) &mRefresher)))
     118    {
     119        if (SUCCEEDED (hr = mRefresher->QueryInterface(
     120            IID_IWbemConfigureRefresher,
     121            (void **)&pConfig)))
     122        {
     123            // Add an enumerator to the refresher.
     124            if (SUCCEEDED (hr = pConfig->AddEnum(
     125                mNameSpace,
     126                L"Win32_PerfRawData_PerfOS_Processor",
     127                0,
     128                NULL,
     129                &mEnumProcessor,
     130                &mEnumProcessorID)))
     131            {
     132                hr = pConfig->AddEnum(
     133                    mNameSpace,
     134                    L"Win32_PerfRawData_PerfProc_Process",
     135                    0,
     136                    NULL,
     137                    &mEnumProcess,
     138                    &mEnumProcessID);
     139            }
     140            pConfig->Release();
     141        }
     142    }
     143
     144
     145    if (FAILED (hr)) {
     146        Log (("Failed to add enumerators. HR = %x\n", hr));
     147        return;
     148    }
     149
     150    // Retrieve property handles
     151
     152    if (FAILED (hr = mRefresher->Refresh(0L)))
     153    {
     154        Log (("Refresher failed. HR = %x\n", hr));
     155        return;
     156    }
     157
     158    IWbemObjectAccess       **apEnumAccess = NULL;
     159    DWORD                   dwNumReturned = 0;
     160
     161    if (RT_FAILURE(getObjects(mEnumProcessor, &apEnumAccess, &dwNumReturned)))
     162        return;
     163
     164    mHostCpuLoadNameHandle   = getPropertyHandle(apEnumAccess[0], L"Name");
     165    mHostCpuLoadUserHandle   = getPropertyHandle(apEnumAccess[0], L"PercentUserTime");
     166    mHostCpuLoadKernelHandle = getPropertyHandle(apEnumAccess[0], L"PercentPrivilegedTime");
     167    mHostCpuLoadIdleHandle   = getPropertyHandle(apEnumAccess[0], L"PercentProcessorTime");
     168
     169    delete [] apEnumAccess;
     170
     171    if (RT_FAILURE(getObjects(mEnumProcess, &apEnumAccess, &dwNumReturned)))
     172        return;
     173
     174    mProcessPIDHandle              = getPropertyHandle(apEnumAccess[0], L"IDProcess");
     175    mProcessCpuLoadUserHandle      = getPropertyHandle(apEnumAccess[0], L"PercentUserTime");
     176    mProcessCpuLoadKernelHandle    = getPropertyHandle(apEnumAccess[0], L"PercentPrivilegedTime");
     177    mProcessCpuLoadTimestampHandle = getPropertyHandle(apEnumAccess[0], L"Timestamp_Sys100NS");
     178    mProcessMemoryUsedHandle       = getPropertyHandle(apEnumAccess[0], L"WorkingSet");
     179
     180    delete [] apEnumAccess;
    77181}
    78182
    79183CollectorWin::~CollectorWin()
    80184{
    81     if (mHostCpuLoadQuery)
    82         PdhCloseQuery (mHostCpuLoadQuery);
    83 }
    84 
    85 int CollectorWin::convertPdhStatusToRTErr(PDH_STATUS pdhStatus)
    86 {
    87     switch (pdhStatus)
    88     {
    89         case PDH_INVALID_ARGUMENT:
    90             return VERR_INVALID_PARAMETER;
    91         case PDH_INVALID_HANDLE:
    92             return VERR_INVALID_HANDLE;
    93     }
    94 
    95     return RTErrConvertFromWin32(pdhStatus);
    96 }
    97 
    98 HCOUNTER CollectorWin::addCounter(HQUERY hQuery, LPCTSTR name)
    99 {
    100     PDH_STATUS pdhStatus;
    101     HCOUNTER   hCounter;
    102 
    103     pdhStatus = PdhAddCounter (hQuery, name, 0, &hCounter);
    104     if (pdhStatus != ERROR_SUCCESS)
    105         return 0;
    106     return hCounter;
    107 }
    108 
    109 int CollectorWin::getCounterValue(HCOUNTER hCounter, DWORD flags, unsigned long *value)
    110 {
    111     PDH_STATUS           pdhStatus;
    112     PDH_FMT_COUNTERVALUE fmtValue;
    113 
    114     pdhStatus = PdhGetFormattedCounterValue (hCounter,
    115                                              PDH_FMT_LONG | flags,
    116                                              NULL,
    117                                              &fmtValue);
    118     if (pdhStatus != ERROR_SUCCESS)
    119         return convertPdhStatusToRTErr(pdhStatus);
    120    
    121     *value = fmtValue.longValue;
    122    
     185    if (NULL != mNameSpace)
     186    {
     187        mNameSpace->Release();
     188    }
     189    if (NULL != mEnumProcessor)
     190    {
     191        mEnumProcessor->Release();
     192    }
     193    if (NULL != mEnumProcess)
     194    {
     195        mEnumProcess->Release();
     196    }
     197    if (NULL != mRefresher)
     198    {
     199        mRefresher->Release();
     200    }
     201}
     202
     203long CollectorWin::getPropertyHandle(IWbemObjectAccess *objAccess, LPCWSTR name)
     204{
     205    HRESULT hr;
     206    CIMTYPE tmp;
     207    long    handle;
     208
     209    if (FAILED (hr = objAccess->GetPropertyHandle(
     210        name,
     211        &tmp,
     212        &handle)))
     213    {
     214        Log (("Failed to get property handle for '%ls'. HR = %x\n", name, hr));
     215        return 0;   /// @todo use throw
     216    }
     217
     218    return handle;
     219}
     220
     221int CollectorWin::getObjects(IWbemHiPerfEnum *mEnum, IWbemObjectAccess ***objArray, DWORD *numReturned)
     222{
     223    HRESULT hr;
     224    DWORD   dwNumObjects = 0;
     225
     226    *objArray    = NULL;
     227    *numReturned = 0;
     228    hr = mEnum->GetObjects(0L, dwNumObjects, *objArray, numReturned);
     229
     230    // If the buffer was not big enough,
     231    // allocate a bigger buffer and retry.
     232    if (hr == WBEM_E_BUFFER_TOO_SMALL
     233        && *numReturned > dwNumObjects)
     234    {
     235        *objArray = new IWbemObjectAccess*[*numReturned];
     236        if (NULL == *objArray)
     237        {
     238            Log (("Could not allocate enumerator access objects\n"));
     239            return VERR_NO_MEMORY;
     240        }
     241
     242        SecureZeroMemory(*objArray,
     243            *numReturned*sizeof(IWbemObjectAccess*));
     244        dwNumObjects = *numReturned;
     245
     246        if (FAILED (hr = mEnum->GetObjects(0L,
     247            dwNumObjects, *objArray, numReturned)))
     248        {
     249            delete [] objArray;
     250            Log (("Failed to get objects from enumerator. HR = %x\n", hr));
     251            return VERR_INTERNAL_ERROR;
     252        }
     253    }
     254    else if (FAILED (hr))
     255    {
     256        Log (("Failed to get objects from enumerator. HR = %x\n", hr));
     257        return VERR_INTERNAL_ERROR;
     258    }
     259
    123260    return VINF_SUCCESS;
    124261}
     
    126263int CollectorWin::getHostCpuLoad(unsigned long *user, unsigned long *kernel, unsigned long *idle)
    127264{
    128     int         rc;
    129     PDH_STATUS  pdhStatus;
    130 
    131     pdhStatus = PdhCollectQueryData (mHostCpuLoadQuery);
    132     if (pdhStatus != ERROR_SUCCESS)
    133         return convertPdhStatusToRTErr(pdhStatus);
    134 
    135     rc = getCounterValue (mHostCpuLoadUserCounter, PDH_FMT_1000, user);
    136     AssertRCReturn(rc, rc);
    137 
    138     rc = getCounterValue (mHostCpuLoadKernelCounter, PDH_FMT_1000, kernel);
    139     AssertRCReturn(rc, rc);
    140 
    141     rc = getCounterValue (mHostCpuLoadIdleCounter, PDH_FMT_1000, idle);
    142     AssertRCReturn(rc, rc);
    143 
    144     return VINF_SUCCESS;
     265    return VERR_NOT_IMPLEMENTED;
     266}
     267
     268int CollectorWin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
     269{
     270    HRESULT hr;
     271    IWbemObjectAccess       **apEnumAccess = NULL;
     272    DWORD                   dwNumReturned = 0;
     273
     274    LogFlowThisFuncEnter();
     275
     276    if (FAILED (hr = mRefresher->Refresh(0L)))
     277    {
     278        Log (("Refresher failed. HR = %x\n", hr));
     279        return VERR_INTERNAL_ERROR;
     280    }
     281
     282    int rc = getObjects(mEnumProcessor, &apEnumAccess, &dwNumReturned);
     283    if (RT_FAILURE(rc))
     284        return rc;
     285
     286    for (unsigned i = 0; i < dwNumReturned; i++)
     287    {
     288        long  bytesRead = 0;
     289        WCHAR tmpBuf[200];
     290
     291        if (FAILED (hr = apEnumAccess[i]->ReadPropertyValue(
     292            mHostCpuLoadNameHandle,
     293            sizeof(tmpBuf),
     294            &bytesRead,
     295            (byte*)tmpBuf)))
     296        {
     297            Log (("Failed to read 'Name' property. HR = %x\n", hr));
     298            return VERR_INTERNAL_ERROR;
     299        }
     300        if (wcscmp(tmpBuf, L"_Total") == 0)
     301        {
     302            if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
     303                mHostCpuLoadUserHandle,
     304                user)))
     305            {
     306            Log (("Failed to read 'PercentUserTime' property. HR = %x\n", hr));
     307                return VERR_INTERNAL_ERROR;
     308            }
     309            if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
     310                mHostCpuLoadKernelHandle,
     311                kernel)))
     312            {
     313            Log (("Failed to read 'PercentPrivilegedTime' property. HR = %x\n", hr));
     314                return VERR_INTERNAL_ERROR;
     315            }
     316            if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
     317                mHostCpuLoadIdleHandle,
     318                idle)))
     319            {
     320            Log (("Failed to read 'PercentProcessorTime' property. HR = %x\n", hr));
     321                return VERR_INTERNAL_ERROR;
     322            }
     323            rc = VINF_SUCCESS;
     324        }
     325        apEnumAccess[i]->Release();
     326        apEnumAccess[i] = NULL;
     327    }
     328    delete [] apEnumAccess;
     329
     330    LogFlowThisFunc(("user=%lu kernel=%lu idle=%lu\n", *user, *kernel, *idle));
     331    LogFlowThisFuncLeave();
     332
     333    return rc;
    145334}
    146335
    147336int CollectorWin::getHostCpuMHz(unsigned long *mhz)
    148337{
    149     return E_NOTIMPL;
     338    return VERR_NOT_IMPLEMENTED;
    150339}
    151340
     
    169358int CollectorWin::getProcessCpuLoad(RTPROCESS process, unsigned long *user, unsigned long *kernel)
    170359{
    171     return E_NOTIMPL;
     360    return VERR_NOT_IMPLEMENTED;
     361}
     362
     363int CollectorWin::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
     364{
     365    HRESULT hr;
     366    IWbemObjectAccess       **apEnumAccess = NULL;
     367    DWORD                   dwNumReturned = 0;
     368
     369    LogFlowThisFuncEnter();
     370
     371    if (FAILED (hr = mRefresher->Refresh(0L)))
     372    {
     373        Log (("Refresher failed. HR = %x\n", hr));
     374        return VERR_INTERNAL_ERROR;
     375    }
     376
     377    int rc = getObjects(mEnumProcess, &apEnumAccess, &dwNumReturned);
     378    if (RT_FAILURE(rc))
     379        return rc;
     380
     381    rc = VERR_NOT_FOUND;
     382
     383    for (unsigned i = 0; i < dwNumReturned; i++)
     384    {
     385        DWORD dwIDProcess;
     386
     387        if (FAILED (hr = apEnumAccess[i]->ReadDWORD(
     388            mProcessPIDHandle,
     389            &dwIDProcess)))
     390        {
     391            Log (("Failed to read 'IDProcess' property. HR = %x\n", hr));
     392            return VERR_INTERNAL_ERROR;
     393        }
     394        LogFlowThisFunc (("Matching machine process %x against %x...\n", process, dwIDProcess));
     395        if (dwIDProcess == process)
     396        {
     397            LogFlowThisFunc (("Match found.\n"));
     398            if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
     399                mProcessCpuLoadUserHandle,
     400                user)))
     401            {
     402            Log (("Failed to read 'PercentUserTime' property. HR = %x\n", hr));
     403                return VERR_INTERNAL_ERROR;
     404            }
     405            if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
     406                mProcessCpuLoadKernelHandle,
     407                kernel)))
     408            {
     409            Log (("Failed to read 'PercentPrivilegedTime' property. HR = %x\n", hr));
     410                return VERR_INTERNAL_ERROR;
     411            }
     412            if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
     413                mProcessCpuLoadTimestampHandle,
     414                total)))
     415            {
     416            Log (("Failed to read 'Timestamp_Sys100NS' property. HR = %x\n", hr));
     417                return VERR_INTERNAL_ERROR;
     418            }
     419            rc = VINF_SUCCESS;
     420        }
     421        apEnumAccess[i]->Release();
     422        apEnumAccess[i] = NULL;
     423    }
     424    delete [] apEnumAccess;
     425
     426    LogFlowThisFunc(("user=%lu kernel=%lu total=%lu\n", *user, *kernel, *total));
     427    LogFlowThisFuncLeave();
     428
     429    return rc;
    172430}
    173431
    174432int CollectorWin::getProcessMemoryUsage(RTPROCESS process, unsigned long *used)
    175433{
    176     return E_NOTIMPL;
    177 }
    178 
    179 }
     434    HRESULT hr;
     435    IWbemObjectAccess       **apEnumAccess = NULL;
     436    DWORD                   dwNumReturned = 0;
     437
     438    LogFlowThisFuncEnter();
     439
     440    if (FAILED (hr = mRefresher->Refresh(0L)))
     441    {
     442        Log (("Refresher failed. HR = %x\n", hr));
     443        return VERR_INTERNAL_ERROR;
     444    }
     445
     446    int rc = getObjects(mEnumProcess, &apEnumAccess, &dwNumReturned);
     447    if (RT_FAILURE(rc))
     448        return rc;
     449
     450    rc = VERR_NOT_FOUND;
     451
     452    for (unsigned i = 0; i < dwNumReturned; i++)
     453    {
     454        DWORD dwIDProcess;
     455
     456        if (FAILED (hr = apEnumAccess[i]->ReadDWORD(
     457            mProcessPIDHandle,
     458            &dwIDProcess)))
     459        {
     460            Log (("Failed to read 'IDProcess' property. HR = %x\n", hr));
     461            return VERR_INTERNAL_ERROR;
     462        }
     463        if (dwIDProcess == process)
     464        {
     465            uint64_t u64used = 0;
     466
     467            if (FAILED (hr = apEnumAccess[i]->ReadQWORD(
     468                mProcessMemoryUsedHandle,
     469                &u64used)))
     470            {
     471            Log (("Failed to read 'WorkingSet' property. HR = %x\n", hr));
     472                return VERR_INTERNAL_ERROR;
     473            }
     474            *used = (unsigned long)(u64used / 1024);
     475            rc = VINF_SUCCESS;
     476        }
     477        apEnumAccess[i]->Release();
     478        apEnumAccess[i] = NULL;
     479    }
     480    delete [] apEnumAccess;
     481
     482    LogFlowThisFunc(("used=%lu\n", *used));
     483    LogFlowThisFuncLeave();
     484
     485    return rc;
     486}
     487
     488}
  • trunk/src/VBox/Main/win/svcmain.cpp

    r8155 r10868  
    173173#endif
    174174    _ASSERTE(SUCCEEDED(hRes));
     175    /*
     176     * Need to initialize security to access performance enumerators.
     177     */
     178    hRes = CoInitializeSecurity(
     179        NULL,
     180        -1,
     181        NULL,
     182        NULL,
     183        RPC_C_AUTHN_LEVEL_NONE,
     184        RPC_C_IMP_LEVEL_IMPERSONATE,
     185        NULL, EOAC_NONE, 0);
     186    _ASSERTE(SUCCEEDED(hRes));
    175187    _Module.Init(ObjectMap, hInstance, &LIBID_VirtualBox);
    176188    _Module.dwThreadID = GetCurrentThreadId();
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette