/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include "StandaloneTools_precompiled.h" #include "ProfilerDataAggregator.hxx" #include #include "ProfilerDataView.hxx" #include "ProfilerEvents.h" #include #include #include "Source/Driller/Workspaces/Workspace.h" namespace Driller { // used against m_roiVersion to silently clear and reinitialize on internal updates static const int dataAggregatorVersion = 2; // USER SETTINGS are local only, global settings to the application // designed to be used for window placement, global preferences, that kind of thing class ProfilerDataAggregatorSavedState : public AZ::UserSettings { public: AZ_RTTI(ProfilerDataAggregatorSavedState, "{98494FFE-783F-48A7-A35F-714138425640}", AZ::UserSettings); AZ_CLASS_ALLOCATOR(ProfilerDataAggregatorSavedState, AZ::SystemAllocator, 0); struct RegisterOfInterest { AZ_RTTI(RegisterOfInterest, "{885335FD-79D1-4462-B637-177FC0FCF01C}"); AZStd::string m_name; float m_dataScale; int m_usesDelta; int m_useSubValue; virtual ~RegisterOfInterest() {} static void Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { serialize->Class() ->Field("m_name", &RegisterOfInterest::m_name) ->Field("m_dataScale", &RegisterOfInterest::m_dataScale) ->Field("m_usesDelta", &RegisterOfInterest::m_usesDelta) ->Field("m_useSubValue", &RegisterOfInterest::m_useSubValue) ->Version(3); } } }; int m_activeViewCount; AZStd::vector m_registersOfInterest; int m_roiVersion; ProfilerDataAggregatorSavedState() : m_activeViewCount(0) , m_roiVersion(1) {} static void Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { RegisterOfInterest::Reflect(context); serialize->Class() ->Field("m_activeViewCount", &ProfilerDataAggregatorSavedState::m_activeViewCount) ->Field("m_registersOfInterest", &ProfilerDataAggregatorSavedState::m_registersOfInterest) ->Field("m_roiVersion", &ProfilerDataAggregatorSavedState::m_roiVersion) ->Version(7); } } }; // WORKSPACES are files loaded and stored independent of the global application // designed to be used for DRL data specific view settings and to pass around class ProfilerDataAggregatorWorkspace : public AZ::UserSettings { public: AZ_RTTI(ProfilerDataAggregatorWorkspace, "{2C41A0B1-E200-448D-8727-5109DF877B0E}", AZ::UserSettings); AZ_CLASS_ALLOCATOR(ProfilerDataAggregatorWorkspace, AZ::SystemAllocator, 0); int m_activeViewCount; AZStd::vector m_activeViewTypes; ProfilerDataAggregatorWorkspace() : m_activeViewCount(0) {} static void Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { serialize->Class() ->Field("m_activeViewCount", &ProfilerDataAggregatorWorkspace::m_activeViewCount) ->Field("m_activeViewTypes", &ProfilerDataAggregatorWorkspace::m_activeViewTypes) ->Version(3); } } }; ////////////////////////////////////////////////////////////////////////// ProfilerDataAggregator::ProfilerDataAggregator(int identity) : Aggregator(identity) , m_currentDisplayRegister(0) , m_dataView(nullptr) { m_parser.SetAggregator(this); // find state and restore it m_persistentState = AZ::UserSettings::CreateFind(AZ_CRC("PROFILER DATA AGGREGATOR SAVED STATE", 0x49c357f6), AZ::UserSettings::CT_GLOBAL); AZ_Assert(m_persistentState, "Persistent State is NULL?"); // please see ::dataAggregatorVersion to control updates if (m_persistentState->m_registersOfInterest.empty() || m_persistentState->m_roiVersion != dataAggregatorVersion) { m_persistentState->m_registersOfInterest.clear(); m_persistentState->m_registersOfInterest.push_back(); m_persistentState->m_registersOfInterest[0].m_name = "Component application tick function"; m_persistentState->m_registersOfInterest[0].m_dataScale = 1.0f / 64000.0f; m_persistentState->m_registersOfInterest[0].m_usesDelta = 1; // this is a delta time calculated on the fly here m_persistentState->m_registersOfInterest[0].m_useSubValue = 0; // user data 0 is m_time from the register union } m_allRegistersOfInterestInData.clear(); if (!m_persistentState->m_registersOfInterest.empty()) { m_allRegistersOfInterestInData.resize(m_persistentState->m_registersOfInterest.size(), NULL); m_allCorrespondingIdsForRegistersOfInterestInData.resize(m_persistentState->m_registersOfInterest.size(), 0); } } ProfilerDataAggregator::~ProfilerDataAggregator() { KillAllViews(); } // this aggregator has to dive deeper into the source data // to synthesize a meaningful -1...+1 value for the main display float ProfilerDataAggregator::ValueAtFrame(FrameNumberType frame) { size_t numEvents = NumOfEventsAtFrame(frame); if (numEvents && frame > 0) { for (EventNumberType eventIndex = m_frameToEventIndex[frame]; eventIndex < static_cast(m_frameToEventIndex[frame] + numEvents); ++eventIndex) { DrillerEvent* drillerEvent = drillerEvent = GetEvents()[ eventIndex ]; if (drillerEvent->GetEventType() == Driller::Profiler::PET_UPDATE_REGISTER) { Driller::ProfilerDrillerUpdateRegisterEvent* reg = static_cast(drillerEvent); for (auto iter = m_allCorrespondingIdsForRegistersOfInterestInData.begin(); iter != m_allCorrespondingIdsForRegistersOfInterestInData.end(); ++iter) { if (reg->GetRegisterId() == *iter) { float t = 0.0f; if (m_persistentState->m_registersOfInterest[m_currentDisplayRegister].m_usesDelta) { switch (m_persistentState->m_registersOfInterest[m_currentDisplayRegister].m_useSubValue) { case 0: t = (float)(reg->GetData().m_valueData.m_value1 - (reg->GetPreviousSample() == NULL ? 0 : reg->GetPreviousSample()->GetData().m_valueData.m_value1)); break; case 1: t = (float)(reg->GetData().m_valueData.m_value2 - (reg->GetPreviousSample() == NULL ? 0 : reg->GetPreviousSample()->GetData().m_valueData.m_value2)); break; case 2: t = (float)(reg->GetData().m_valueData.m_value3 - (reg->GetPreviousSample() == NULL ? 0 : reg->GetPreviousSample()->GetData().m_valueData.m_value3)); break; case 3: t = (float)(reg->GetData().m_valueData.m_value4 - (reg->GetPreviousSample() == NULL ? 0 : reg->GetPreviousSample()->GetData().m_valueData.m_value4)); break; } } else { switch (m_persistentState->m_registersOfInterest[m_currentDisplayRegister].m_useSubValue) { case 0: t = (float)reg->GetData().m_valueData.m_value1; break; case 1: t = (float)reg->GetData().m_valueData.m_value2; break; case 2: t = (float)reg->GetData().m_valueData.m_value3; break; case 3: t = (float)reg->GetData().m_valueData.m_value4; break; } } t *= m_persistentState->m_registersOfInterest[m_currentDisplayRegister].m_dataScale; t = t * 2.0f - 1.0f; t = t > 1.0f ? 1.0f : t; t = t < -1.0f ? -1.0f : t; return t; } } } } } return -1.0f; } QColor ProfilerDataAggregator::GetColor() const { return QColor(255, 127, 0); } QString ProfilerDataAggregator::GetName() const { return "CPU"; } QString ProfilerDataAggregator::GetChannelName() const { return ChannelName(); } QString ProfilerDataAggregator::GetDescription() const { return "Profiler Driller"; } QString ProfilerDataAggregator::GetToolTip() const { return "Information about CPU usage time and function usage tracking)"; } AZ::Uuid ProfilerDataAggregator::GetID() const { return AZ::Uuid("{A6DB5318-82BF-416B-BF3D-FFD187329845}"); } QWidget* ProfilerDataAggregator::DrillDownRequest(FrameNumberType frame) { return DrillDownRequest(frame, Profiler::RegisterInfo::PRT_TIME); } QWidget* ProfilerDataAggregator::DrillDownRequest(FrameNumberType frame, int viewType) { Driller::ProfilerDataView* pdv = NULL; if (m_dataView) { KillAllViews(); } pdv = aznew Driller::ProfilerDataView(this, frame, 0, viewType); if (pdv) { m_dataView = pdv; connect(pdv, SIGNAL(destroyed(QObject*)), this, SLOT(OnDataViewDestroyed(QObject*))); ++m_persistentState->m_activeViewCount; } return pdv; } void ProfilerDataAggregator::OptionsRequest() { char output[64]; GetID().ToString(output, AZ_ARRAY_SIZE(output), true, true); AZ_TracePrintf("Driller", "Options Request for ProfilerDataAggregator %s\n", output); } void ProfilerDataAggregator::OnDataViewDestroyed(QObject* dataView) { if (dataView == m_dataView) { m_dataView = nullptr; --m_persistentState->m_activeViewCount; } } void ProfilerDataAggregator::KillAllViews() { if (m_dataView) { QObject* object = m_dataView; OnDataViewDestroyed(m_dataView); m_dataView = nullptr; delete object; } } void ProfilerDataAggregator::ApplySettingsFromWorkspace(WorkspaceSettingsProvider* provider) { ProfilerDataAggregatorWorkspace* workspace = provider->FindSetting(AZ_CRC("PROFILER DATA AGGREGATOR WORKSPACE", 0xfdb6cb89)); if (workspace) { m_persistentState->m_activeViewCount = workspace->m_activeViewCount; } } void ProfilerDataAggregator::ActivateWorkspaceSettings(WorkspaceSettingsProvider* provider) { ProfilerDataAggregatorWorkspace* workspace = provider->FindSetting(AZ_CRC("PROFILER DATA AGGREGATOR WORKSPACE", 0xfdb6cb89)); if (workspace) { // kill all existing data view windows in preparation of opening the workspace specified ones KillAllViews(); // the internal count should be 0 from the above house cleaning // and incremented back up from the workspace instantiations m_persistentState->m_activeViewCount = 0; for (int i = 0; i < workspace->m_activeViewCount; ++i) { // older workspaces will not have any active view types // therefore this check to default PRT_TIME int discoveredType = Profiler::RegisterInfo::PRT_TIME; if (workspace->m_activeViewTypes.size() > i) { discoveredType = workspace->m_activeViewTypes[i]; } Driller::ProfilerDataView* dataView = qobject_cast(DrillDownRequest(1, discoveredType)); if (dataView) { // apply will overlay the workspace settings on top of the local user settings dataView->ApplySettingsFromWorkspace(provider); // activate will do the heavy lifting dataView->ActivateWorkspaceSettings(provider); } } } } void ProfilerDataAggregator::SaveSettingsToWorkspace(WorkspaceSettingsProvider* provider) { ProfilerDataAggregatorWorkspace* workspace = provider->CreateSetting(AZ_CRC("PROFILER DATA AGGREGATOR WORKSPACE", 0xfdb6cb89)); if (workspace) { workspace->m_activeViewTypes.clear(); workspace->m_activeViewCount = m_persistentState->m_activeViewCount; if (m_dataView) { Driller::ProfilerDataView* dataView = qobject_cast(m_dataView); if (dataView) { workspace->m_activeViewTypes.push_back(dataView->GetViewType()); dataView->SaveSettingsToWorkspace(provider); } } } } //========================================================================= // OnEventLoaded // [7/10/2013] //========================================================================= void ProfilerDataAggregator::OnEventLoaded(DrillerEvent* event) { switch (event->GetEventType()) { case Profiler::PET_NEW_REGISTER: { Driller::ProfilerDrillerNewRegisterEvent* reg = static_cast(event); for (AZStd::size_t idx = 0; idx < m_persistentState->m_registersOfInterest.size(); ++idx) { AZStd::string registerName; if (reg->GetInfo().m_name == NULL) { registerName = AZStd::string::format("%s(%d)" , reg->GetInfo().m_function ? reg->GetInfo().m_function : "N/A" , reg->GetInfo().m_line); } else { registerName = reg->GetInfo().m_name; } if (!qstricmp(registerName.data(), m_persistentState->m_registersOfInterest[idx].m_name.data())) { m_allRegistersOfInterestInData[idx] = reg; m_allCorrespondingIdsForRegistersOfInterestInData[idx] = reg->GetInfo().m_id; } } if (m_lifeTimeThreads.find(reg->GetInfo().m_threadId) == m_lifeTimeThreads.end()) { // this register belongs to a thread which was not AZStd::thread or was NOT reported to the // AZStd::ThreadEventBus. This is possible for middleware and so on. Although we should attempt // to report those threads too (the best we can, with some name at least) // as of now just add the id. // NB: threadId can be be defaulted at 0 if this is an older data set // in which case we do not add it to the threads if (reg->GetInfo().m_threadId != 0) { if (m_lifeTimeThreads.find(reg->GetInfo().m_threadId) == m_lifeTimeThreads.end()) { m_lifeTimeThreads.insert(AZStd::make_pair(reg->GetInfo().m_threadId, nullptr)); } } } break; } case Profiler::PET_UPDATE_REGISTER: { Driller::ProfilerDrillerUpdateRegisterEvent* reg = static_cast(event); for (AZStd::size_t idx = 0; idx < m_allCorrespondingIdsForRegistersOfInterestInData.size(); ++idx) { if (reg->GetRegisterId() == m_allCorrespondingIdsForRegistersOfInterestInData[idx]) { reg->PreComputeForward(m_allRegistersOfInterestInData[idx]); } } } break; case Profiler::PET_ENTER_THREAD: { // make sure we have a valid list with all the threads in the world ProfilerDrillerEnterThreadEvent* newThread = static_cast(event); if (m_lifeTimeThreads.find(newThread->m_threadId) == m_lifeTimeThreads.end()) { m_lifeTimeThreads.insert(AZStd::make_pair(newThread->m_threadId, newThread)); } } break; } } //========================================================================= // Reset() // [7/10/2013] //========================================================================= void ProfilerDataAggregator::Reset() { m_systems.clear(); m_threads.clear(); m_lifeTimeThreads.clear(); m_registers.clear(); KillAllViews(); } void ProfilerDataAggregator::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { ProfilerDataAggregatorSavedState::Reflect(context); ProfilerDataAggregatorWorkspace::Reflect(context); ProfilerDataView::Reflect(context); serialize->Class() ->Version(1) ->SerializeWithNoData(); } } } // namespace Driller