linux PerfCollect收集日志及perfview分析

news2024/11/6 19:37:38

Perfview:https://github.com/Microsoft/perfview/releases
PerfCollect:https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md

Linux 环境中运行的 ASP.NET Core应用中收集跟踪
PerfCollect(https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md) 是一个 bash 脚本,它使用本机 Linux 分析工具(Perf 和 LTTng)收集 Linux 上的跟踪信息,以便 PerfView 进行分析

#Download Perfcollect.
curl -OL http://aka.ms/perfcollect (网络原因,因此下载存为文件名:/root/perfcollect)

#Make the script executable.
chmod +x ./perfcollect

#Install tracing prerequisites - these are the actual tracing libraries. For details on prerequisites, see below.
sudo ./perfcollect install

##Collecting a Trace

#[App] Setup the application shell - this enables tracing configuration inside of CoreCLR.
export COMPlus_PerfMapEnabled=1
export COMPlus_EnableEventLog=1

#[Trace] Start collection.
sudo ./perfcollect collect trace_202306082203

运行效果:


perfcollect文件内容如下:

#!/bin/bash 

#############################################################################################################
# .NET Performance Data Collection Script
#############################################################################################################

#############################################################################################################
#
# ***** HOW TO USE THIS SCRIPT *****
#
# This script can be used to collect and view performance data collected with perf_event on Linux.

# It's job is to make it simple to collect performance traces.
#
# How to collect a performance trace:
# 1. Prior to starting the .NET process, set the environment variable COMPlus_PerfMapEnabled=1.
#    This tells the runtime to emit information that enables perf_event to resolve JIT-compiled code symbols.
# 
# 2. Setup your system to reproduce the performance issue you'd like to capture.  Data collection can be
#    started on already running processes.
# 
# 3. [Usage #1] use "collect" command
#    - Run this script: sudo ./perfcollect collect samplePerfTrace. This will start data collection.
#    - Let the repro run as long as you need to capture the performance problem.
#    - Hit CTRL+C to stop collection.
#
#    [Usage #2] use "start" and "stop" command
#    - Run this script: sudo ./perfcollect start samplePerfTrace. This will start data colletion.
#    - Let the repro run as long as you need to capture the performance problem.
#    - Run: sudo ./perfcollect stop samplePerfTrace. This will stop collection.
#
# 4. When collection is stopped, the script will create a trace.zip file matching the name specified on the
#    command line.  This file will contain the trace, JIT-compiled symbol information, and all debugging
#    symbols for binaries referenced by this trace that were available on the machine at collection time.
#
# How to view a performance trace:
# 1. Run this script: ./perfcollect view samplePerfTrace.trace.zip
#    This will extract the trace, place and register all symbol files and JIT-compiled symbol information
#    and start the perf_event viewer.  By default, you will be looking at a callee view - stacks are ordered
#    top down.  For a caller or bottom up view, specify '-graphtype caller'. 
#############################################################################################################

######################################
## FOR DEBUGGING ONLY
######################################
# set -x

######################################
## Collection Options
## NOTE: These values represent the collection defaults.
######################################

# Set when we parse command line arguments to determine if we should enable specific collection options.
collect_cpu=1
collect_threadTime=0
collect_offcpu=0
collect_system=0

######################################
## .NET Event Categories
######################################

# Separate GCCollectOnly list because our LTTng implementation doesn't set tracepoint verbosity.
# Once tracepoint verbosity is set, we can set verbosity and collapse this with DotNETRuntime_GCKeyword.
declare -a DotNETRuntime_GCKeyword_GCCollectOnly=(
    DotNETRuntime:GCStart
    DotNETRuntime:GCStart_V1
    DotNETRuntime:GCStart_V2
    DotNETRuntime:GCEnd
    DotNETRuntime:GCEnd_V1
    DotNETRuntime:GCRestartEEEnd
    DotNETRuntime:GCRestartEEEnd_V1
    DotNETRuntime:GCHeapStats
    DotNETRuntime:GCHeapStats_V1
    DotNETRuntime:GCCreateSegment
    DotNETRuntime:GCCreateSegment_V1
    DotNETRuntime:GCFreeSegment
    DotNETRuntime:GCFreeSegment_V1
    DotNETRuntime:GCRestartEEBegin
    DotNETRuntime:GCRestartEEBegin_V1
    DotNETRuntime:GCSuspendEEEnd
    DotNETRuntime:GCSuspendEEEnd_V1
    DotNETRuntime:GCSuspendEEBegin
    DotNETRuntime:GCSuspendEEBegin_V1
    DotNETRuntime:GCCreateConcurrentThread
    DotNETRuntime:GCTerminateConcurrentThread
    DotNETRuntime:GCFinalizersEnd
    DotNETRuntime:GCFinalizersEnd_V1
    DotNETRuntime:GCFinalizersBegin
    DotNETRuntime:GCFinalizersBegin_V1
    DotNETRuntime:GCMarkStackRoots
    DotNETRuntime:GCMarkFinalizeQueueRoots
    DotNETRuntime:GCMarkHandles
    DotNETRuntime:GCMarkOlderGenerationRoots
    DotNETRuntime:FinalizeObject
    DotNETRuntime:GCTriggered
    DotNETRuntime:IncreaseMemoryPressure
    DotNETRuntime:DecreaseMemoryPressure
    DotNETRuntime:GCMarkWithType
    DotNETRuntime:GCPerHeapHistory_V3
    DotNETRuntime:GCGlobalHeapHistory_V2
    DotNETRuntime:GCCreateConcurrentThread_V1
    DotNETRuntime:GCTerminateConcurrentThread_V1
)


declare -a DotNETRuntime_GCKeyword=(
    DotNETRuntime:GCStart
    DotNETRuntime:GCStart_V1
    DotNETRuntime:GCStart_V2
    DotNETRuntime:GCEnd
    DotNETRuntime:GCEnd_V1
    DotNETRuntime:GCRestartEEEnd
    DotNETRuntime:GCRestartEEEnd_V1
    DotNETRuntime:GCHeapStats
    DotNETRuntime:GCHeapStats_V1
    DotNETRuntime:GCCreateSegment
    DotNETRuntime:GCCreateSegment_V1
    DotNETRuntime:GCFreeSegment
    DotNETRuntime:GCFreeSegment_V1
    DotNETRuntime:GCRestartEEBegin
    DotNETRuntime:GCRestartEEBegin_V1
    DotNETRuntime:GCSuspendEEEnd
    DotNETRuntime:GCSuspendEEEnd_V1
    DotNETRuntime:GCSuspendEEBegin
    DotNETRuntime:GCSuspendEEBegin_V1
    DotNETRuntime:GCAllocationTick
    DotNETRuntime:GCAllocationTick_V1
    DotNETRuntime:GCAllocationTick_V2
    DotNETRuntime:GCAllocationTick_V3
    DotNETRuntime:GCCreateConcurrentThread
    DotNETRuntime:GCTerminateConcurrentThread
    DotNETRuntime:GCFinalizersEnd
    DotNETRuntime:GCFinalizersEnd_V1
    DotNETRuntime:GCFinalizersBegin
    DotNETRuntime:GCFinalizersBegin_V1
    DotNETRuntime:GCMarkStackRoots
    DotNETRuntime:GCMarkFinalizeQueueRoots
    DotNETRuntime:GCMarkHandles
    DotNETRuntime:GCMarkOlderGenerationRoots
    DotNETRuntime:FinalizeObject
    DotNETRuntime:PinObjectAtGCTime
    DotNETRuntime:GCTriggered
    DotNETRuntime:IncreaseMemoryPressure
    DotNETRuntime:DecreaseMemoryPressure
    DotNETRuntime:GCMarkWithType
    DotNETRuntime:GCJoin_V2
    DotNETRuntime:GCPerHeapHistory_V3
    DotNETRuntime:GCGlobalHeapHistory_V2
    DotNETRuntime:GCCreateConcurrentThread_V1
    DotNETRuntime:GCTerminateConcurrentThread_V1
)

declare -a DotNETRuntime_TypeKeyword=(
    DotNETRuntime:BulkType
)

declare -a DotNETRuntime_GCHeapDumpKeyword=(
    DotNETRuntime:GCBulkRootEdge
    DotNETRuntime:GCBulkRootConditionalWeakTableElementEdge
    DotNETRuntime:GCBulkNode
    DotNETRuntime:GCBulkEdge
    DotNETRuntime:GCBulkRootCCW
    DotNETRuntime:GCBulkRCW
    DotNETRuntime:GCBulkRootStaticVar
)

declare -a DotNETRuntime_GCSampledObjectAllocationHighKeyword=(
    DotNETRuntime:GCSampledObjectAllocationHigh
)

declare -a DotNETRuntime_GCHeapSurvivalAndMovementKeyword=(
    DotNETRuntime:GCBulkSurvivingObjectRanges
    DotNETRuntime:GCBulkMovedObjectRanges
    DotNETRuntime:GCGenerationRange
)

declare -a DotNETRuntime_GCHandleKeyword=(
    DotNETRuntime:SetGCHandle
    DotNETRuntime:DestroyGCHandle
)

declare -a DotNETRuntime_GCSampledObjectAllocationLowKeyword=(
    DotNETRuntime:GCSampledObjectAllocationLow
)

declare -a DotNETRuntime_ThreadingKeyword=(
    DotNETRuntime:WorkerThreadCreate
    DotNETRuntime:WorkerThreadTerminate
    DotNETRuntime:WorkerThreadRetire
    DotNETRuntime:WorkerThreadUnretire
    DotNETRuntime:IOThreadCreate
    DotNETRuntime:IOThreadCreate_V1
    DotNETRuntime:IOThreadTerminate
    DotNETRuntime:IOThreadTerminate_V1
    DotNETRuntime:IOThreadRetire
    DotNETRuntime:IOThreadRetire_V1
    DotNETRuntime:IOThreadUnretire
    DotNETRuntime:IOThreadUnretire_V1
    DotNETRuntime:ThreadpoolSuspensionSuspendThread
    DotNETRuntime:ThreadpoolSuspensionResumeThread
    DotNETRuntime:ThreadPoolWorkerThreadStart
    DotNETRuntime:ThreadPoolWorkerThreadStop
    DotNETRuntime:ThreadPoolWorkerThreadRetirementStart
    DotNETRuntime:ThreadPoolWorkerThreadRetirementStop
    DotNETRuntime:ThreadPoolWorkerThreadAdjustmentSample
    DotNETRuntime:ThreadPoolWorkerThreadAdjustmentAdjustment
    DotNETRuntime:ThreadPoolWorkerThreadAdjustmentStats
    DotNETRuntime:ThreadPoolWorkerThreadWait
    DotNETRuntime:ThreadPoolWorkingThreadCount
    DotNETRuntime:ThreadPoolIOPack
    DotNETRuntime:GCCreateConcurrentThread_V1
    DotNETRuntime:GCTerminateConcurrentThread_V1
)

declare -a DotNETRuntime_ThreadingKeyword_ThreadTransferKeyword=(
    DotNETRuntime:ThreadPoolEnqueue
    DotNETRuntime:ThreadPoolDequeue
    DotNETRuntime:ThreadPoolIOEnqueue
    DotNETRuntime:ThreadPoolIODequeue
    DotNETRuntime:ThreadCreating
    DotNETRuntime:ThreadRunning
)

declare -a DotNETRuntime_NoKeyword=(
    DotNETRuntime:ExceptionThrown
    DotNETRuntime:Contention
    DotNETRuntime:RuntimeInformationStart
    DotNETRuntime:EventSource
)

declare -a DotNETRuntime_ExceptionKeyword=(
    DotNETRuntime:ExceptionThrown_V1
    DotNETRuntime:ExceptionCatchStart
    DotNETRuntime:ExceptionCatchStop
    DotNETRuntime:ExceptionFinallyStart
    DotNETRuntime:ExceptionFinallyStop
    DotNETRuntime:ExceptionFilterStart
    DotNETRuntime:ExceptionFilterStop
    DotNETRuntime:ExceptionThrownStop
)

declare -a DotNETRuntime_ContentionKeyword=(
    DotNETRuntime:ContentionStart_V1
    DotNETRuntime:ContentionStop
    DotNETRuntime:ContentionStop_V1
)

declare -a DotNETRuntime_StackKeyword=(
    DotNETRuntime:CLRStackWalk
)

declare -a DotNETRuntime_AppDomainResourceManagementKeyword=(
    DotNETRuntime:AppDomainMemAllocated
    DotNETRuntime:AppDomainMemSurvived
)

declare -a DotNETRuntime_AppDomainResourceManagementKeyword_ThreadingKeyword=(
    DotNETRuntime:ThreadCreated
    DotNETRuntime:ThreadTerminated
    DotNETRuntime:ThreadDomainEnter
)

declare -a DotNETRuntime_InteropKeyword=(
    DotNETRuntime:ILStubGenerated
    DotNETRuntime:ILStubCacheHit
)

declare -a DotNETRuntime_JitKeyword_NGenKeyword=(
    DotNETRuntime:DCStartCompleteV2
    DotNETRuntime:DCEndCompleteV2
    DotNETRuntime:MethodDCStartV2
    DotNETRuntime:MethodDCEndV2
    DotNETRuntime:MethodDCStartVerboseV2
    DotNETRuntime:MethodDCEndVerboseV2
    DotNETRuntime:MethodLoad
    DotNETRuntime:MethodLoad_V1
    DotNETRuntime:MethodLoad_V2
    DotNETRuntime:MethodUnload
    DotNETRuntime:MethodUnload_V1
    DotNETRuntime:MethodUnload_V2
    DotNETRuntime:MethodLoadVerbose
    DotNETRuntime:MethodLoadVerbose_V1
    DotNETRuntime:MethodLoadVerbose_V2
    DotNETRuntime:MethodUnloadVerbose
    DotNETRuntime:MethodUnloadVerbose_V1
    DotNETRuntime:MethodUnloadVerbose_V2
)

declare -a DotNETRuntime_JitKeyword=(
    DotNETRuntime:MethodJittingStarted
    DotNETRuntime:MethodJittingStarted_V1
)

declare -a DotNETRuntime_JitTracingKeyword=(
    DotNETRuntime:MethodJitInliningSucceeded
    DotNETRuntime:MethodJitInliningFailed
    DotNETRuntime:MethodJitTailCallSucceeded
    DotNETRuntime:MethodJitTailCallFailed
)

declare -a DotNETRuntime_JittedMethodILToNativeMapKeyword=(
    DotNETRuntime:MethodILToNativeMap
)

declare -a DotNETRuntime_LoaderKeyword=(
    DotNETRuntime:ModuleDCStartV2
    DotNETRuntime:ModuleDCEndV2
    DotNETRuntime:DomainModuleLoad
    DotNETRuntime:DomainModuleLoad_V1
    DotNETRuntime:ModuleLoad
    DotNETRuntime:ModuleUnload
    DotNETRuntime:AssemblyLoad
    DotNETRuntime:AssemblyLoad_V1
    DotNETRuntime:AssemblyUnload
    DotNETRuntime:AssemblyUnload_V1
    DotNETRuntime:AppDomainLoad
    DotNETRuntime:AppDomainLoad_V1
    DotNETRuntime:AppDomainUnload
    DotNETRuntime:AppDomainUnload_V1
)

declare -a DotNETRuntime_LoaderKeyword=(
    DotNETRuntime:ModuleLoad_V1
    DotNETRuntime:ModuleLoad_V2
    DotNETRuntime:ModuleUnload_V1
    DotNETRuntime:ModuleUnload_V2
)

declare -a DotNETRuntime_SecurityKeyword=(
    DotNETRuntime:StrongNameVerificationStart
    DotNETRuntime:StrongNameVerificationStart_V1
    DotNETRuntime:StrongNameVerificationStop
    DotNETRuntime:StrongNameVerificationStop_V1
    DotNETRuntime:AuthenticodeVerificationStart
    DotNETRuntime:AuthenticodeVerificationStart_V1
    DotNETRuntime:AuthenticodeVerificationStop
    DotNETRuntime:AuthenticodeVerificationStop_V1
)

declare -a DotNETRuntime_DebuggerKeyword=(
    DotNETRuntime:DebugIPCEventStart
    DotNETRuntime:DebugIPCEventEnd
    DotNETRuntime:DebugExceptionProcessingStart
    DotNETRuntime:DebugExceptionProcessingEnd
)

declare -a DotNETRuntime_CodeSymbolsKeyword=(
    DotNETRuntime:CodeSymbols
)

declare -a DotNETRuntime_CompilationKeyword=(
    DotNETRuntime:TieredCompilationSettings
    DotNETRuntime:TieredCompilationPause
    DotNETRuntime:TieredCompilationResume
    DotNETRuntime:TieredCompilationBackgroundJitStart
    DotNETRuntime:TieredCompilationBackgroundJitStop
    DotNETRuntimeRundown:TieredCompilationSettingsDCStart
)

# Separate GCCollectOnly list because our LTTng implementation doesn't set tracepoint verbosity.
# Once tracepoint verbosity is set, we can set verbosity and collapse this with DotNETRuntimePrivate_GCPrivateKeyword.
declare -a DotNETRuntimePrivate_GCPrivateKeyword_GCCollectOnly=(
    DotNETRuntimePrivate:GCDecision
    DotNETRuntimePrivate:GCDecision_V1
    DotNETRuntimePrivate:GCSettings
    DotNETRuntimePrivate:GCSettings_V1
    DotNETRuntimePrivate:GCPerHeapHistory
    DotNETRuntimePrivate:GCPerHeapHistory_V1
    DotNETRuntimePrivate:GCGlobalHeapHistory
    DotNETRuntimePrivate:GCGlobalHeapHistory_V1
    DotNETRuntimePrivate:PrvGCMarkStackRoots
    DotNETRuntimePrivate:PrvGCMarkStackRoots_V1
    DotNETRuntimePrivate:PrvGCMarkFinalizeQueueRoots
    DotNETRuntimePrivate:PrvGCMarkFinalizeQueueRoots_V1
    DotNETRuntimePrivate:PrvGCMarkHandles
    DotNETRuntimePrivate:PrvGCMarkHandles_V1
    DotNETRuntimePrivate:PrvGCMarkCards
    DotNETRuntimePrivate:PrvGCMarkCards_V1
    DotNETRuntimePrivate:BGCBegin
    DotNETRuntimePrivate:BGC1stNonConEnd
    DotNETRuntimePrivate:BGC1stConEnd
    DotNETRuntimePrivate:BGC2ndNonConBegin
    DotNETRuntimePrivate:BGC2ndNonConEnd
    DotNETRuntimePrivate:BGC2ndConBegin
    DotNETRuntimePrivate:BGC2ndConEnd
    DotNETRuntimePrivate:BGCPlanEnd
    DotNETRuntimePrivate:BGCSweepEnd
    DotNETRuntimePrivate:BGCDrainMark
    DotNETRuntimePrivate:BGCRevisit
    DotNETRuntimePrivate:BGCOverflow
    DotNETRuntimePrivate:BGCAllocWaitBegin
    DotNETRuntimePrivate:BGCAllocWaitEnd
    DotNETRuntimePrivate:GCFullNotify
    DotNETRuntimePrivate:GCFullNotify_V1
    DotNETRuntimePrivate:PrvFinalizeObject
    DotNETRuntimePrivate:PinPlugAtGCTime
)

declare -a DotNETRuntimePrivate_GCPrivateKeyword=(
    DotNETRuntimePrivate:GCDecision
    DotNETRuntimePrivate:GCDecision_V1
    DotNETRuntimePrivate:GCSettings
    DotNETRuntimePrivate:GCSettings_V1
    DotNETRuntimePrivate:GCOptimized
    DotNETRuntimePrivate:GCOptimized_V1
    DotNETRuntimePrivate:GCPerHeapHistory
    DotNETRuntimePrivate:GCPerHeapHistory_V1
    DotNETRuntimePrivate:GCGlobalHeapHistory
    DotNETRuntimePrivate:GCGlobalHeapHistory_V1
    DotNETRuntimePrivate:GCJoin
    DotNETRuntimePrivate:GCJoin_V1
    DotNETRuntimePrivate:PrvGCMarkStackRoots
    DotNETRuntimePrivate:PrvGCMarkStackRoots_V1
    DotNETRuntimePrivate:PrvGCMarkFinalizeQueueRoots
    DotNETRuntimePrivate:PrvGCMarkFinalizeQueueRoots_V1
    DotNETRuntimePrivate:PrvGCMarkHandles
    DotNETRuntimePrivate:PrvGCMarkHandles_V1
    DotNETRuntimePrivate:PrvGCMarkCards
    DotNETRuntimePrivate:PrvGCMarkCards_V1
    DotNETRuntimePrivate:BGCBegin
    DotNETRuntimePrivate:BGC1stNonConEnd
    DotNETRuntimePrivate:BGC1stConEnd
    DotNETRuntimePrivate:BGC2ndNonConBegin
    DotNETRuntimePrivate:BGC2ndNonConEnd
    DotNETRuntimePrivate:BGC2ndConBegin
    DotNETRuntimePrivate:BGC2ndConEnd
    DotNETRuntimePrivate:BGCPlanEnd
    DotNETRuntimePrivate:BGCSweepEnd
    DotNETRuntimePrivate:BGCDrainMark
    DotNETRuntimePrivate:BGCRevisit
    DotNETRuntimePrivate:BGCOverflow
    DotNETRuntimePrivate:BGCAllocWaitBegin
    DotNETRuntimePrivate:BGCAllocWaitEnd
    DotNETRuntimePrivate:GCFullNotify
    DotNETRuntimePrivate:GCFullNotify_V1
    DotNETRuntimePrivate:PrvFinalizeObject
    DotNETRuntimePrivate:PinPlugAtGCTime
)

declare -a DotNETRuntimePrivate_StartupKeyword=(
    DotNETRuntimePrivate:EEStartupStart
    DotNETRuntimePrivate:EEStartupStart_V1
    DotNETRuntimePrivate:EEStartupEnd
    DotNETRuntimePrivate:EEStartupEnd_V1
    DotNETRuntimePrivate:EEConfigSetup
    DotNETRuntimePrivate:EEConfigSetup_V1
    DotNETRuntimePrivate:EEConfigSetupEnd
    DotNETRuntimePrivate:EEConfigSetupEnd_V1
    DotNETRuntimePrivate:LdSysBases
    DotNETRuntimePrivate:LdSysBases_V1
    DotNETRuntimePrivate:LdSysBasesEnd
    DotNETRuntimePrivate:LdSysBasesEnd_V1
    DotNETRuntimePrivate:ExecExe
    DotNETRuntimePrivate:ExecExe_V1
    DotNETRuntimePrivate:ExecExeEnd
    DotNETRuntimePrivate:ExecExeEnd_V1
    DotNETRuntimePrivate:Main
    DotNETRuntimePrivate:Main_V1
    DotNETRuntimePrivate:MainEnd
    DotNETRuntimePrivate:MainEnd_V1
    DotNETRuntimePrivate:ApplyPolicyStart
    DotNETRuntimePrivate:ApplyPolicyStart_V1
    DotNETRuntimePrivate:ApplyPolicyEnd
    DotNETRuntimePrivate:ApplyPolicyEnd_V1
    DotNETRuntimePrivate:LdLibShFolder
    DotNETRuntimePrivate:LdLibShFolder_V1
    DotNETRuntimePrivate:LdLibShFolderEnd
    DotNETRuntimePrivate:LdLibShFolderEnd_V1
    DotNETRuntimePrivate:PrestubWorker
    DotNETRuntimePrivate:PrestubWorker_V1
    DotNETRuntimePrivate:PrestubWorkerEnd
    DotNETRuntimePrivate:PrestubWorkerEnd_V1
    DotNETRuntimePrivate:GetInstallationStart
    DotNETRuntimePrivate:GetInstallationStart_V1
    DotNETRuntimePrivate:GetInstallationEnd
    DotNETRuntimePrivate:GetInstallationEnd_V1
    DotNETRuntimePrivate:OpenHModule
    DotNETRuntimePrivate:OpenHModule_V1
    DotNETRuntimePrivate:OpenHModuleEnd
    DotNETRuntimePrivate:OpenHModuleEnd_V1
    DotNETRuntimePrivate:ExplicitBindStart
    DotNETRuntimePrivate:ExplicitBindStart_V1
    DotNETRuntimePrivate:ExplicitBindEnd
    DotNETRuntimePrivate:ExplicitBindEnd_V1
    DotNETRuntimePrivate:ParseXml
    DotNETRuntimePrivate:ParseXml_V1
    DotNETRuntimePrivate:ParseXmlEnd
    DotNETRuntimePrivate:ParseXmlEnd_V1
    DotNETRuntimePrivate:InitDefaultDomain
    DotNETRuntimePrivate:InitDefaultDomain_V1
    DotNETRuntimePrivate:InitDefaultDomainEnd
    DotNETRuntimePrivate:InitDefaultDomainEnd_V1
    DotNETRuntimePrivate:InitSecurity
    DotNETRuntimePrivate:InitSecurity_V1
    DotNETRuntimePrivate:InitSecurityEnd
    DotNETRuntimePrivate:InitSecurityEnd_V1
    DotNETRuntimePrivate:AllowBindingRedirs
    DotNETRuntimePrivate:AllowBindingRedirs_V1
    DotNETRuntimePrivate:AllowBindingRedirsEnd
    DotNETRuntimePrivate:AllowBindingRedirsEnd_V1
    DotNETRuntimePrivate:EEConfigSync
    DotNETRuntimePrivate:EEConfigSync_V1
    DotNETRuntimePrivate:EEConfigSyncEnd
    DotNETRuntimePrivate:EEConfigSyncEnd_V1
    DotNETRuntimePrivate:FusionBinding
    DotNETRuntimePrivate:FusionBinding_V1
    DotNETRuntimePrivate:FusionBindingEnd
    DotNETRuntimePrivate:FusionBindingEnd_V1
    DotNETRuntimePrivate:LoaderCatchCall
    DotNETRuntimePrivate:LoaderCatchCall_V1
    DotNETRuntimePrivate:LoaderCatchCallEnd
    DotNETRuntimePrivate:LoaderCatchCallEnd_V1
    DotNETRuntimePrivate:FusionInit
    DotNETRuntimePrivate:FusionInit_V1
    DotNETRuntimePrivate:FusionInitEnd
    DotNETRuntimePrivate:FusionInitEnd_V1
    DotNETRuntimePrivate:FusionAppCtx
    DotNETRuntimePrivate:FusionAppCtx_V1
    DotNETRuntimePrivate:FusionAppCtxEnd
    DotNETRuntimePrivate:FusionAppCtxEnd_V1
    DotNETRuntimePrivate:Fusion2EE
    DotNETRuntimePrivate:Fusion2EE_V1
    DotNETRuntimePrivate:Fusion2EEEnd
    DotNETRuntimePrivate:Fusion2EEEnd_V1
    DotNETRuntimePrivate:SecurityCatchCall
    DotNETRuntimePrivate:SecurityCatchCall_V1
    DotNETRuntimePrivate:SecurityCatchCallEnd
    DotNETRuntimePrivate:SecurityCatchCallEnd_V1
)

declare -a DotNETRuntimePrivate_StackKeyword=(
    DotNETRuntimePrivate:CLRStackWalkPrivate
)

declare -a DotNETRuntimePrivate_PerfTrackPrivateKeyword=(
    DotNETRuntimePrivate:ModuleRangeLoadPrivate
)

declare -a DotNETRuntimePrivate_BindingKeyword=(
    DotNETRuntimePrivate:BindingPolicyPhaseStart
    DotNETRuntimePrivate:BindingPolicyPhaseEnd
    DotNETRuntimePrivate:BindingNgenPhaseStart
    DotNETRuntimePrivate:BindingNgenPhaseEnd
    DotNETRuntimePrivate:BindingLookupAndProbingPhaseStart
    DotNETRuntimePrivate:BindingLookupAndProbingPhaseEnd
    DotNETRuntimePrivate:LoaderPhaseStart
    DotNETRuntimePrivate:LoaderPhaseEnd
    DotNETRuntimePrivate:BindingPhaseStart
    DotNETRuntimePrivate:BindingPhaseEnd
    DotNETRuntimePrivate:BindingDownloadPhaseStart
    DotNETRuntimePrivate:BindingDownloadPhaseEnd
    DotNETRuntimePrivate:LoaderAssemblyInitPhaseStart
    DotNETRuntimePrivate:LoaderAssemblyInitPhaseEnd
    DotNETRuntimePrivate:LoaderMappingPhaseStart
    DotNETRuntimePrivate:LoaderMappingPhaseEnd
    DotNETRuntimePrivate:LoaderDeliverEventsPhaseStart
    DotNETRuntimePrivate:LoaderDeliverEventsPhaseEnd
    DotNETRuntimePrivate:FusionMessageEvent
    DotNETRuntimePrivate:FusionErrorCodeEvent
)

declare -a DotNETRuntimePrivate_SecurityPrivateKeyword=(
    DotNETRuntimePrivate:EvidenceGenerated
    DotNETRuntimePrivate:ModuleTransparencyComputationStart
    DotNETRuntimePrivate:ModuleTransparencyComputationEnd
    DotNETRuntimePrivate:TypeTransparencyComputationStart
    DotNETRuntimePrivate:TypeTransparencyComputationEnd
    DotNETRuntimePrivate:MethodTransparencyComputationStart
    DotNETRuntimePrivate:MethodTransparencyComputationEnd
    DotNETRuntimePrivate:FieldTransparencyComputationStart
    DotNETRuntimePrivate:FieldTransparencyComputationEnd
    DotNETRuntimePrivate:TokenTransparencyComputationStart
    DotNETRuntimePrivate:TokenTransparencyComputationEnd
)

declare -a DotNETRuntimePrivate_PrivateFusionKeyword=(
    DotNETRuntimePrivate:NgenBindEvent
)

declare -a DotNETRuntimePrivate_NoKeyword=(
    DotNETRuntimePrivate:FailFast
)

declare -a DotNETRuntimePrivate_InteropPrivateKeyword=(
    DotNETRuntimePrivate:CCWRefCountChange
)

declare -a DotNETRuntimePrivate_GCHandlePrivateKeyword=(
    DotNETRuntimePrivate:PrvSetGCHandle
    DotNETRuntimePrivate:PrvDestroyGCHandle
)

declare -a DotNETRuntimePrivate_LoaderHeapPrivateKeyword=(
    DotNETRuntimePrivate:AllocRequest
)

declare -a DotNETRuntimePrivate_MulticoreJitPrivateKeyword=(
    DotNETRuntimePrivate:MulticoreJit
    DotNETRuntimePrivate:MulticoreJitMethodCodeReturned
)

declare -a DotNETRuntimePrivate_DynamicTypeUsageKeyword=(
    DotNETRuntimePrivate:IInspectableRuntimeClassName
    DotNETRuntimePrivate:WinRTUnbox
    DotNETRuntimePrivate:CreateRCW
    DotNETRuntimePrivate:RCWVariance
    DotNETRuntimePrivate:RCWIEnumerableCasting
    DotNETRuntimePrivate:CreateCCW
    DotNETRuntimePrivate:CCWVariance
    DotNETRuntimePrivate:ObjectVariantMarshallingToNative
    DotNETRuntimePrivate:GetTypeFromGUID
    DotNETRuntimePrivate:GetTypeFromProgID
    DotNETRuntimePrivate:ConvertToCallbackEtw
    DotNETRuntimePrivate:BeginCreateManagedReference
    DotNETRuntimePrivate:EndCreateManagedReference
    DotNETRuntimePrivate:ObjectVariantMarshallingToManaged
)

declare -a EventSource=(
    DotNETRuntime:EventSource
)

declare -a LTTng_Kernel_ProcessLifetimeKeyword=(
    sched_process_exec
    sched_process_exit
)


######################################
## Global Variables
######################################

# Install without waiting for standard input
forceInstall=0

# Declare an array of events to collect.
declare -a eventsToCollect

# Use Perf_Event
usePerf=1

# Use LTTng
useLTTng=1

# LTTng Installed
lttngInstalled=0

# Collect hardware events
collect_HWevents=0

# Set to 1 when the CTRLC_Handler gets invoked.
handlerInvoked=0

# Log file
declare logFile
logFilePrefix='/tmp/perfcollect'
logEnabled=0

# Collect info to pass between processes
collectInfoFile=$(dirname `mktemp -u`)/'perfcollect.sessioninfo'

######################################
## Logging Functions
######################################
LogAppend()
{
    if (( $logEnabled == 1 ))
    then
        echo $* >> $logFile
    fi
}

RunSilent()
{
    if (( $logEnabled == 1 ))
    then
        echo "Running \"$*\"" >> $logFile
        $* >> $logFile 2>&1
        echo "" >> $logFile
    else
        $* > /dev/null 2>&1
    fi
}

RunSilentBackground()
{
    if (( $logEnabled == 1 ))
    then
        echo "Running \"$*\"" >> $logFile
        $* >> $logFile 2>&1  & sleep 1
        echo "" >> $logFile
    else
        $* > /dev/null 2>&1  & sleep 1
    fi

    # When handler is invoked, kill the process.
    pid=$!
    for (( ; ; ))
    do
        if [ "$handlerInvoked" == "1" ]
        then
            kill -INT $pid
            break;
        else
            sleep 1
        fi	
    done

    # Wait for the process to exit.
    wait $pid
}

InitializeLog()
{
    # Pick the log file name.
    logFile="$logFilePrefix.log"
    while [ -f $logFile ];
    do
        logFile="$logFilePrefix.$RANDOM.log"
    done

    # Mark the log as enabled.
    logEnabled=1

    # Start the log
    date=`date`
    echo "Log started at ${date}" > $logFile
    echo '' >> $logFile

    # The system information.
    LogAppend 'Machine info: '  `uname -a`
    if [ "$perfcmd" != "" ]
    then
        LogAppend 'perf version:'   `$perfcmd --version 2>&1`
    fi
    if [ "$lttngcmd" != "" ]
    then
        LogAppend 'LTTng version: ' `$lttngcmd --version`
    fi
    LogAppend
}

CloseLog()
{
    LogAppend "END LOG FILE"
    LogAppend "NOTE: It is normal for the log file to end right before the trace is actually compressed.  This occurs because the log file is part of the archive, and thus can't be written anymore."

    # The log itself doesn't need to be closed,
    # but we need to tell the script not to log anymore.
    logEnabled=0
}


######################################
## Helper Functions
######################################

##
# Console text color modification helpers.
##
RedText()
{
    tput=`GetCommandFullPath "tput"`
    if [ "$tput" != "" ]
    then
    	$tput setaf 1
    fi
}

GreenText()
{
    tput=`GetCommandFullPath "tput"`
    if [ "$tput" != "" ]
    then
        $tput setaf 2
    fi
}

BlueText()
{
    tput=`GetCommandFullPath "tput"`
    if [ "$tput" != "" ]
    then
        $tput setaf 6
    fi
}
YellowText()
{
    tput=`GetCommandFullPath "tput"`
    if [ "$tput" != "" ]
    then
        $tput setaf 3
    fi
}

ResetText()
{
    tput=`GetCommandFullPath "tput"`
    if [ "$tput" != "" ]
    then
        $tput sgr0
    fi
}

# $1 == Status message
WriteStatus()
{
    LogAppend $*
    BlueText
    echo $1
    ResetText
}

# $1 == Message.
WriteWarning()
{
    LogAppend $*
    YellowText
    echo $1
    ResetText
}

# $1 == Message.
FatalError()
{
    RedText
    echo "ERROR: $1"
    ResetText
    PrintUsage
    exit 1
}

EnsureRoot()
{
    # Warn non-root users.
    if [ `whoami` != "root" ]
    then
        RedText
        echo "This script must be run as root."
        ResetText
        exit 1;
    fi
}

######################################
# Command Discovery
######################################
DiscoverCommands()
{
    perfcmd=`GetCommandFullPath "perf"`
    if [ "$(IsDebian)" == "1" ]
    then
        # Test perf to see if it successfully runs or fails because it doesn't match the kernel version.
        $perfcmd --version > /dev/null 2>&1
        if [ "$?" == "1" ]
        then
            perfoutput=$($perfcmd 2>&1)
            if [ $? -eq 1 ]
            then
                perfMarker="perf_"
                if [[ "$perfoutput" == *"$perfMarker"* ]]
                then
                    foundWorkingPerf=0
                    WriteWarning "Perf is installed, but does not exactly match the version of the running kernel."
                    WriteWarning "This is often OK, and we'll try to workaround this."
                    WriteStatus "Attempting to find a working copy of perf."
                    # Attempt to find an existing version of perf to use.
                    # Order the search by newest directory first.
                    baseDir="/usr/bin"
                    searchPath="$baseDir/perf_*"
                    for fileName in $(ls -d --sort=time $searchPath)
                    do
                        $($fileName > /dev/null 2>&1)
                        if [ $? -eq 1 ]
                        then
                            # If $? == 1, then use this copy of perf.
                            perfcmd=$fileName
                            foundWorkingPerf=1
                            break;
                        fi
                    done
                    if [ $foundWorkingPerf -eq 0 ]
                    then
                        FatalError "Unable to find a working copy of perf.  Try re-installing via ./perfcollect install."
                    fi
                    WriteStatus "...FINISHED"
                fi
            fi
        fi
    fi

    # Check to see if perf is installed, but doesn't exactly match the current kernel version.
    # This happens when the kernel gets upgraded, or when running inside of a container where the
    # host and container OS versions don't match.
    perfoutput=$($perfcmd 2>&1)
    if [ $? -eq 2 ]
    then
        # Check the beginning of the output to see if it matches the kernel warning.
        warningText="WARNING: perf not found for kernel"
        if [[ "$perfoutput" == "$warningText"* ]]
        then
            foundWorkingPerf=0
            WriteWarning "Perf is installed, but does not exactly match the version of the running kernel."
            WriteWarning "This is often OK, and we'll try to workaround this."
            WriteStatus "Attempting to find a working copy of perf."
            # Attempt to find an existing version of perf to use.
            # Order the search by newest directory first.
            baseDir="/usr/lib/linux-tools"
            searchPath="$baseDir/*/"
            for dirName in $(ls -d --sort=time $searchPath)
            do
                candidatePerfPath="$dirName""perf"
                $($candidatePerfPath > /dev/null 2>&1)
                if [ $? -eq 1 ]
                then
                    # If $? == 1, then use this copy of perf.
                    perfcmd=$candidatePerfPath
                    foundWorkingPerf=1
                    break;
                fi
            done
            if [ $foundWorkingPerf -eq 0 ]
            then
                FatalError "Unable to find a working copy of perf.  Try re-installing via ./perfcollect install."
            fi
            WriteStatus "...FINISHED"
        fi
    fi

    lttngcmd=`GetCommandFullPath "lttng"`
    zipcmd=`GetCommandFullPath "zip"`
    unzipcmd=`GetCommandFullPath "unzip"`
    objdumpcmd=`GetCommandFullPath "objdump"`
}

GetCommandFullPath()
{
    echo `command -v $1`
}

######################################
# Prerequisite Installation
######################################

IsMariner()
{
    local mariner=0
    if [ -f /etc/lsb-release ]
    then
        local flavor=`cat /etc/lsb-release | grep DISTRIB_ID`
        if [ "$flavor" == "DISTRIB_ID=\"Mariner\"" ]
        then
            mariner=1
        fi
    fi

    echo $mariner
}

InstallPerf_Mariner()
{
    # Disallow non-root users.
    EnsureRoot

    # Install perf
    tdnf install -y kernel-tools

    # Install zip and unzip
    tdnf install -y zip unzip
}

IsAlpine()
{
    local alpine=0
    local apk=`GetCommandFullPath "apk"`
    if [ "$apk" != "" ]
    then
        alpine=1
    fi

    echo $alpine
}

InstallPerf_Alpine()
{
    # Disallow non-root users.
    EnsureRoot

    # Install perf
    apk add perf --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community

    # Install zip and unzip
    apk add zip unzip
}

IsRHEL()
{
    local rhel=0
    if [ -f /etc/redhat-release ]
    then
        rhel=1
    fi

    echo $rhel
}

InstallPerf_RHEL()
{
    # Disallow non-root users.
    EnsureRoot

    # Install perf
    yum install perf zip unzip
}

IsDebian()
{
    local debian=0
    local uname=`uname -a`
    if [[ $uname =~ .*Debian.* ]]
    then
        debian=1
    elif [ -f /etc/debian_version ]
    then
        debian=1
    fi

    echo $debian
}

InstallPerf_Debian()
{
    # Disallow non-root users.
    EnsureRoot

    # Check for the existence of the linux-tools package.
    pkgName='linux-tools'
    pkgCount=`apt-cache search $pkgName | grep -c $pkgName`
    if [ "$pkgCount" == "0" ]
    then
        pkgName='linux-perf'
        pkgCount=`apt-cache search $pkgName | grep -c $pkgName`
        if [ "$pkgCount" == "0" ]
        then
            FatalError "Unable to find a perf package to install."
        fi
    fi

    # Install zip and perf.
    apt-get install -y zip binutils $pkgName
}

IsSUSE()
{
    local suse=0
    if [ -f /usr/bin/zypper ]
    then
        suse=1
    fi

    echo $suse
}

InstallPerf_SUSE()
{
    # Disallow non-root users.
    EnsureRoot

    # Install perf.
    zypper install perf zip unzip
}

IsUbuntu()
{
    local ubuntu=0
    if [ -f /etc/lsb-release ]
    then
        local flavor=`cat /etc/lsb-release | grep DISTRIB_ID`
        if [ "$flavor" == "DISTRIB_ID=Ubuntu" ]
        then
            ubuntu=1
        fi
    fi
        
    echo $ubuntu
}

InstallPerf_Ubuntu()
{
    # Disallow non-root users.
    EnsureRoot

    # Install packages.
    BlueText
    echo "Installing perf_event packages."
    ResetText

    # Handle Azure instances.
    release=`uname -r`
    if [[ "$release" == *"-azure" ]]
    then
        apt-get install -y linux-tools-azure zip software-properties-common
    else
        apt-get install -y linux-tools-common linux-tools-`uname -r` linux-cloud-tools-`uname -r` zip software-properties-common
    fi
}

InstallPerf()
{
    if [ "$(IsUbuntu)" == "1" ]
    then
        InstallPerf_Ubuntu
    elif [ "$(IsSUSE)" == "1" ]
    then
        InstallPerf_SUSE
    elif [ "$(IsDebian)" == "1" ]
    then
        InstallPerf_Debian
    elif [ "$(IsRHEL)" == "1" ]
    then
        InstallPerf_RHEL
    elif [ "$(IsAlpine)" == "1" ]
    then
        InstallPerf_Alpine
    elif [ "$(IsMariner)" == "1" ]
    then
        InstallPerf_Mariner
    else
        FatalError "Auto install unsupported for this distribution.  Install perf manually to continue."
    fi
}

InstallLTTng_RHEL()
{
    # Disallow non-root users.
    EnsureRoot

    local isRHEL7=0
    local isRHEL8=0
    if [ -e /etc/redhat-release ]; then
        local redhatRelease=$(</etc/redhat-release)
        if   [[ $redhatRelease == "CentOS Linux release 7."* || $redhatRelease == "Red Hat Enterprise Linux "*"release 7."* ]]; then
            isRHEL7=1
        elif [[ $redhatRelease == "CentOS Linux release 8."* || $redhatRelease == "Red Hat Enterprise Linux "*"release 8."* ]]; then
            isRHEL8=1
        fi
    fi

    if  [ "$isRHEL7" == "1" ] || [ "$isRHEL8" == "1" ]
    then
        packageRepo="https://packages.efficios.com/repo.files/EfficiOS-RHEL7-x86-64.repo"

        if [ "$forceInstall" != 1 ]
        then
            # Prompt for confirmation, since we need to add a new repository.
            BlueText
            echo "LTTng installation requires that a new package repo be added to your yum configuration."
            echo "The package repo url is: $packageRepo"
            echo ""
            read -p "Would you like to add the LTTng package repo to your YUM configuration? [Y/N]" resp
            ResetText
        fi

        # Make sure that wget is installed.
        BlueText
        echo "Installing wget.  Required to add package repo."
        ResetText
        yum install wget

        # Connect to the LTTng package repo.
        wget -P /etc/yum.repos.d/ $packageRepo

        # Import package signing key.
        rpmkeys --import https://packages.efficios.com/rhel/repo.key

        # Update the yum package database.
        yum updateinfo
    fi

    # Install LTTng
    yum install -y lttng-tools lttng-ust babeltrace
    if  [ "$isRHEL7" == "1" ]
    then
        yum install -y kmod-lttng-modules
    else
        YellowText
        echo "LTTng kernel package (kmod-lttng-modules) is not available on this platform."
        ResetText
    fi
}

InstallLTTng_Debian()
{
    # Disallow non-root users.
    EnsureRoot

    # Install LTTng
    apt-get install -y lttng-tools liblttng-ust-dev
}

InstallLTTng_SUSE()
{
    # Disallow non-root users.
    EnsureRoot

    # Package repo url
    packageRepo="http://download.opensuse.org/repositories/devel:/tools:/lttng/openSUSE_13.2/devel:tools:lttng.repo"

    if [ "$forceInstall" != 1 ]
    then
        # Prompt for confirmation, since we need to add a new repository.
        BlueText
        echo "LTTng installation requires that a new package repo be added to your zypper configuration."
        echo "The package repo url is: $packageRepo"
        echo ""
        read -p "Would you like to add the LTTng package repo to your zypper configuration? [Y/N]" resp
        ResetText
    fi
    if [ "$resp" == "Y" ] || [ "$resp" == "y" ] || [ "$forceInstall" == 1 ]
    then

        # Add package repo.
        BlueText
        echo "Adding LTTng repo and running zypper refresh."
        ResetText
        zypper addrepo $packageRepo
        zypper refresh

        # Install packages.
        BlueText
        echo "Installing LTTng packages."
        ResetText
        zypper install lttng-tools lttng-modules lttng-ust-devel
    fi
}

InstallLTTng_Ubuntu()
{
    # Disallow non-root users.
    EnsureRoot

    # Install packages.
    BlueText
    echo "Installing LTTng packages."
    ResetText
    apt-get install -y lttng-tools lttng-modules-dkms liblttng-ust0
}

InstallLTTng()
{
    if command -v lttng &> /dev/null
    then
        lttngInstalled=1

        BlueText
        echo "LTTng already installed."
        ResetText
        return
    fi

    if [ "$(IsUbuntu)" == "1" ]
    then
        InstallLTTng_Ubuntu
    elif [ "$(IsSUSE)" == "1" ]
    then
        InstallLTTng_SUSE
    elif [ "$(IsDebian)" == "1" ]
    then
        InstallLTTng_Debian
    elif [ "$(IsRHEL)" == "1" ]
    then
        InstallLTTng_RHEL
    elif [ "$(IsMariner)" == "1" ]
    then
        echo "Collection of events from lttng-ust is not supported on Mariner for .NET workloads, so no lttng-ust events will be collected."
    elif [ "$(IsAlpine)" == "1" ]
    then
        echo "lttng-tools is not available on Alpine Linux, so no lttng-ust events will be collected."
    else
        FatalError "Auto install unsupported for this distribution.  Install lttng and lttng-ust packages manually."
    fi
}

SupportsAutoInstall()
{
    local supportsAutoInstall=0
    if [ "$(IsUbuntu)" == "1" ] || [ "$(IsSUSE)" == "1" ] || [ "$(IsMariner)" == "1" ]
    then
        supportsAutoInstall=1
    fi
    
    echo $supportsAutoInstall
}

EnsurePrereqsInstalled()
{
    # If perf is not installed, then bail, as it is currently required.
    if [ "$perfcmd" == "" ]
    then
        RedText
        echo "Perf not installed."
        if  [ "$(SupportsAutoInstall)" == "1" ]
        then
            echo "Run ./perfcollect install"
            echo "or install perf manually."
        else
            echo "Install perf to proceed."
        fi
        ResetText
        exit 1
    fi

    # Disable LTTng use if running on Alpine or Mariner.
    if [ "$(IsAlpine)" == "1" ] || [ "$(IsMariner)" == "1" ]
    then
        useLTTng=0
    fi

    # If LTTng is installed, consider using it.
    if [ "$lttngcmd" == "" ] && [ "$useLTTng" == "1" ]
    then
        RedText
        echo "LTTng not installed."
        if  [ "$(SupportsAutoInstall)" == "1" ]
        then
            echo "Run ./perfcollect install"
            echo "or install LTTng manually."
        else
            echo "Install LTTng to proceed."
        fi
        ResetText
        exit 1

    fi

    # If zip or unzip are not installing, then bail.
    if [ "$zipcmd" == "" ] || [ "$unzipcmd" == "" ]
    then
        RedText
        echo "Zip and unzip are not installed."
        if [ "$(SupportsAutoInstall)" == "1" ]
        then
            echo "Run ./perfcollect install"
            echo "or install zip and unzip manually."
        else
            echo "Install zip and unzip to proceed."
        fi
        ResetText
        exit 1
    fi
}

######################################
# Argument Processing
######################################
action=''
inputTraceName=''
collectionPid=''
processFilter=''
graphType=''
perfOpt=''
viewer='perf'
gcCollectOnly=''
gcOnly=''
gcWithHeap=''
events=''

ProcessArguments()
{
    # Set the action
    action=$1

    # Actions with no arguments.
    if [ "$action" == "livetrace" ] || [ "$action" == "setenv" ]
    then
        return
    fi
    
    # Not enough arguments.
    if [ "$#" -le "1" ]
    then
        FatalError "Not enough arguments have been specified."
    fi

    # Validate action name.
    if [ "$action" != "collect" ] && [ "$action" != "view" ] \
    && [ "$action" != "start" ] && [ "$action" != "stop" ]
    then
        FatalError "Invalid action specified."
    fi

    # Set the data file.
    inputTraceName=$2
    if [ "$inputTraceName" == "" ] 
    then
        FatalError "Invalid trace name specified."
    fi

    # Process remaining arguments.
    # First copy the args into an array so that we can walk the array.
    args=( "$@" )
    for (( i=2; i<${#args[@]}; i++ ))
    do
        # Get the arg.
        local arg=${args[$i]}

        # Convert the arg to lower case.
        arg=`echo $arg | tr '[:upper:]' '[:lower:]'`

        # Get the arg value.
        if [ ${i+1} -lt $# ]
        then
            local value=${args[$i+1]}

            # Keep the cases of '-events' value to match keyword variables
            if [ "-events" != "$arg" ]
            then
                # Convert the value to lower case.
                value=`echo $value | tr '[:upper:]' '[:lower:]'`
            fi
        fi

        # Match the arg to a known value.
        if [ "-pid" == "$arg" ]
        then
            collectionPid=$value
            i=$i+1
        elif [ "-processfilter" == "$arg" ]
        then
            processFilter=$value
            i=$i+1
        elif [ "-graphtype" == "$arg" ]
        then
            graphType=$value
            i=$i+1
        elif [ "-threadtime" == "$arg" ]
        then
            collect_threadTime=1
        elif [ "-offcpu" == "$arg" ]
        then
            # Perf doesn't support capturing cpu-clock events and sched events concurrently.
            collect_cpu=0
            collect_offcpu=1
        elif [ "-system" == "$arg" ]
        then
            collect_system=1
        elif [ "-hwevents" == "$arg" ]
        then
            collect_HWevents=1
        elif [ "-perfopt" == "$arg" ]
        then
            perfOpt=$value
            i=$i+1
        elif [ "-viewer" == "$arg" ]
        then
            viewer=$value
            i=$i+1

            # Validate the viewer.
            if [ "$viewer" != "perf" ] && [ "$viewer" != "lttng" ]
            then
                FatalError "Invalid viewer specified.  Valid values are 'perf' and 'lttng'."
            fi
        elif [ "-nolttng" == "$arg" ]
        then
            useLTTng=0
        elif [ "-noperf" == "$arg" ]
        then 
            usePerf=0
        elif [ "-gccollectonly" == "$arg" ]
        then
            gcCollectOnly=1
        elif [ "-gconly" == "$arg" ]
        then
            gcOnly=1
        elif [ "-gcwithheap" == "$arg" ]
        then
            gcWithHeap=1
        elif [ "-events" == "$arg" ]
        then
            events=$value
            i=$i+1
        elif [ "-collectsec" == "$arg" ]
        then
            duration=$value
            i=$i+1
        else
            echo "Unknown arg ${arg}, ignored..."
        fi
    done
    
}



##
# LTTng collection
##
lttngSessionName=''
lttngTraceDir=''
CreateLTTngSession()
{
    if [ "$action" == "livetrace" ]
    then
        output=`$lttngcmd create --live`
    else
        output=`$lttngcmd create`
    fi

    lttngSessionName=`echo $output | grep -o "Session.*created." | sed 's/\(Session \| created.\)//g'`
    lttngTraceDir=`echo $output | grep -o "Traces.*" | sed 's/\(Traces will be written in \|\)//g' | sed 's/\(Traces will be output to \|\)//g'`
}

SetupLTTngSession()
{
    
    # Setup per-event context information.
    RunSilent "$lttngcmd add-context --userspace --type vpid"
    RunSilent "$lttngcmd add-context --userspace --type vtid"
    RunSilent "$lttngcmd add-context --userspace --type procname"
    RunSilent "$lttngcmd add-context --kernel -t pid -t procname"

    if [ "$action" == "livetrace" ]
    then
        RunSilent "$lttngcmd enable-event --userspace --tracepoint DotNETRuntime:EventSource"
    elif [ "$gcCollectOnly" == "1" ]
    then
        usePerf=0
        EnableLTTngEvents ${DotNETRuntime_GCKeyword_GCCollectOnly[@]}
        EnableLTTngEvents ${DotNETRuntimePrivate_GCPrivateKeyword_GCCollectOnly[@]}
        EnableLTTngEvents ${DotNETRuntime_ExceptionKeyword[@]}
    elif [ "$gcOnly" == "1" ]
    then
        usePerf=0
        EnableLTTngEvents ${DotNETRuntime_GCKeyword[@]}
        EnableLTTngEvents ${DotNETRuntimePrivate_GCPrivateKeyword[@]}
        EnableLTTngEvents ${DotNETRuntime_JitKeyword[@]}
        EnableLTTngEvents ${DotNETRuntime_LoaderKeyword[@]}
        EnableLTTngEvents ${DotNETRuntime_ExceptionKeyword[@]}
    elif [ "$gcWithHeap" == "1" ]
    then
        usePerf=0
        EnableLTTngEvents ${DotNETRuntime_GCKeyword[@]}
        EnableLTTngEvents ${DotNETRuntime_GCHeapSurvivalAndMovementKeyword[@]}
    else
        if [ "$events" == "" ]
        then
            # Enable the default set of events.
            EnableLTTngEvents ${DotNETRuntime_ThreadingKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_ThreadingKeyword_ThreadTransferKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_NoKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_ExceptionKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_ContentionKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_JitKeyword_NGenKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_JitKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_LoaderKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_GCKeyword_GCCollectOnly[@]}
            EnableLTTngEvents ${DotNETRuntimePrivate_GCPrivateKeyword_GCCollectOnly[@]}
            EnableLTTngEvents ${DotNETRuntimePrivate_BindingKeyword[@]}
            EnableLTTngEvents ${DotNETRuntimePrivate_MulticoreJitPrivateKeyword[@]}
            EnableLTTngEvents ${DotNETRuntime_CompilationKeyword[@]}
        elif [ "$events" == "threading" ]
        then
            EnableLTTngEvents ${DotNETRuntime_ThreadingKeyword[@]}
        else     
            # Enable other keywords
            events=(${events//","/" "})
            for event in ${events[@]}
            do 
                if [ "${!event}" == "" ] || [ "$( echo `declare -p $event | grep -- -a` )" == "" ]
                then
                    echo Invalid keyword $event, skipped...
                    continue
                fi
                local -a 'keywords=("${'"$event"'[@]}")'
                if [ "${event#"LTTng_Kernel_"}" != "$event" ]
                then 
                    EnableLTTngKernelEvents ${keywords[@]}
                else
                    EnableLTTngEvents ${keywords[@]}
                fi
            done
        fi
    fi
}

DestroyLTTngSession()
{
    RunSilent "$lttngcmd destroy $lttngSessionName"
}

StartLTTngCollection()
{
    CreateLTTngSession
    SetupLTTngSession

    RunSilent "$lttngcmd start $lttngSessionName"
}

StopLTTngCollection()
{
    RunSilent "$lttngcmd stop $lttngSessionName"
    DestroyLTTngSession
}

# $@ == event names to be enabled
EnableLTTngEvents()
{
    args=( "$@" )
    for (( i=0; i<${#args[@]}; i++ ))
    do
        RunSilent "$lttngcmd enable-event -s $lttngSessionName -u --tracepoint ${args[$i]}"
    done
}

EnableLTTngKernelEvents()
{
    args=( "$@" )
    for (( i=0; i<${#args[@]}; i++ ))
    do 
        RunSilent "$lttngcmd enable-event -s $lttngSessionName -k ${args[$i]}"
    done
}

##
# Helper that processes collected data.
# This helper is called when the CTRL+C signal is handled.
##
ProcessCollectedData()
{
    # Make a new target directory.
    local traceSuffix=".trace"
    local traceName=$inputTraceName
    local directoryName=$traceName$traceSuffix
    mkdir $directoryName

    # Save LTTng trace files.
    if [ "$useLTTng" == "1" ]
    then
        LogAppend "Saving LTTng trace files."

        if [ -d $lttngTraceDir ]
        then
            RunSilent "mkdir lttngTrace"
            RunSilent "cp -r $lttngTraceDir lttngTrace"
        fi
    fi

    if [ "$usePerf" == 1 ]
    then
        # Get any perf-$pid.map files that were used by the
        # trace and store them alongside the trace.
        local writeCrossgenWarning=1
        LogAppend "Saving perf.map files."
        RunSilent "$perfcmd buildid-list --with-hits"
        local mapFiles=`$perfcmd buildid-list --with-hits | grep /tmp/perf- | cut -d ' ' -f 2`
        for mapFile in $mapFiles
        do
            if [ -f $mapFile ]
            then
                LogAppend "Saving $mapFile"

                # Change permissions on the file before saving, as perf will need to access the file later
                # in this script when running perf script.
                RunSilent "chown root $mapFile"
                RunSilent "cp $mapFile ."

                local perfinfoFile=${mapFile/perf/perfinfo}

                LogAppend "Attempting to find ${perfinfoFile}"

                if [ -f $perfinfoFile ]
                then
                    LogAppend "Saving $perfinfoFile"
                    RunSilent "chown root $perfinfoFile"
                    RunSilent "cp $perfinfoFile ."
                else
                    LogAppend "Skipping ${perfinfoFile}."
                fi
            else
                LogAppend "Skipping $mapFile.  Some managed symbols may not be resolvable, but trace is still valid."
            fi

            # Also check for jit-<pid>.dump files.
            # Convert to jit dump file name.
            local pid=`echo $mapFile | awk -F"-" '{print $NF}' | awk -F"." '{print $1}'`
            local path=`echo $mapFile | awk -F"/" '{OFS="/";NF--;print $0;}'`
            local jitDumpFile="$path/jit-$pid.dump"

            if [ -f $jitDumpFile ]
            then
                LogAppend "Saving $jitDumpFile"
                RunSilent "cp $jitDumpFile ."
                writeCrossgenWarning=0
            fi
        done

        WriteStatus "Generating native image symbol files"

        # Get the list of loaded images and use the path to libcoreclr.so to find crossgen.
        # crossgen is expected to sit next to libcoreclr.so.
        local buildidList=`$perfcmd buildid-list | grep libcoreclr.so | cut -d ' ' -f 2`
        local crossgenCmd=''
        local crossgenDir=''
        for file in $buildidList
        do
            crossgenDir=`dirname "${file}"`
            if [ -f ${crossgenDir}/crossgen ]
            then
                crossgenCmd=${crossgenDir}/crossgen
                LogAppend "Found crossgen at ${crossgenCmd}"
                break
            fi
        done

        OLDIFS=$IFS

        imagePaths=""

        if [ "$crossgenCmd" != "" ]
        then
                local perfinfos=`ls . | grep perfinfo | cut -d ' ' -f 2`
                for perfinfo in $perfinfos
                do
                    if [ -f $perfinfo ]
                    then
                        IFS=";"
                        while read command dll guid; do
                            if [ $command ]; then
                                if [ $command = "ImageLoad" ]; then
                                    if [ -f $dll ]; then
                                        imagePaths="${dll}:${imagePaths}"
                                    fi
                                fi
                            fi
                        done < $perfinfo
                        IFS=$OLDIFS
                    fi
                done

                IFS=":"
                LogAppend "Generating PerfMaps for native images"
                for path in $imagePaths
                do
                    if [ `echo ${path} | grep ^.*\.dll$` ]
                    then
                        IFS=""
                        LogAppend "Generating PerfMap for ${path}"
                        LogAppend "Running ${crossgenCmd} /r $imagePaths /CreatePerfMap . ${path}"
                        ${crossgenCmd} /r $imagePaths /CreatePerfMap . ${path} >> $logFile 2>&1
                        IFS=":"
                    else
                        LogAppend "Skipping ${path}"
                    fi
                done
        else
        if [ "$buildidList" != "" ] && [ $writeCrossgenWarning -eq 1 ]
        then
            LogAppend "crossgen not found, skipping native image map generation."
            WriteStatus "...SKIPPED"
            WriteWarning "Crossgen not found.  Framework symbols will be unavailable."
            WriteWarning "See https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md#resolving-framework-symbols for details."
        fi
        fi

        IFS=$OLDIFS

        WriteStatus "...FINISHED"

        if [ "$objdumpcmd" != "" ]
        then
            # Create debuginfo files (separate symbols) for all modules in the trace.
            WriteStatus "Saving native symbols"

            # Get the list of DSOs with hits in the trace file (those that are actually used).
            # Filter out /tmp/perf-$pid.map files and files that end in .dll.
            local dsosWithHits=`$perfcmd buildid-list --with-hits | grep -v /tmp/perf- | grep -v .dll$`
            for dso in $dsosWithHits
            do
                # Build up tuples of buildid and binary path.
                local processEntry=0
                if [ -f $dso ]
                then
                    local pathToBinary=$dso
                    processEntry=1
                else
                    local buildid=$dso
                    pathToBinary=''
                fi

                # Once we have a tuple for a binary path that exists, process it.
                if [ "$processEntry" == "1" ]
                then
                    # Get the binary name without path.
                    local binaryName=`basename $pathToBinary`

                    # Build the debuginfo file name.
                    local destFileName=$binaryName.debuginfo

                    # Build the destination directory for the debuginfo file.
                    local currentDir=`pwd`
                    local destDir=$currentDir/debuginfo/$buildid

                    # Build the full path to the debuginfo file.
                    local destPath=$destDir/$destFileName

                    # Check to see if the DSO contains symbols, and if so, build the debuginfo file.
                    local noSymbols=`$objdumpcmd -t $pathToBinary | grep "no symbols" -c`
                    if [ "$noSymbols" == "0" ]
                    then
                        LogAppend "Generating debuginfo for $binaryName with buildid=$buildid"
                        RunSilent "mkdir -p $destDir"
                        RunSilent "objcopy --only-keep-debug $pathToBinary $destPath"
                    else
                        LogAppend "Skipping $binaryName with buildid=$buildid.  No symbol information."
                    fi
                fi
            done

            WriteStatus "...FINISHED"
        fi

        WriteStatus "Resolving JIT and R2R symbols"

        originalFile="perf.data"
        inputFile="perf-jit.data"
        RunSilent $perfcmd inject --input $originalFile --jit --output $inputFile

        WriteStatus "...FINISHED"

        WriteStatus "Exporting perf.data file"

        outputDumpFile="perf.data.txt"

        LogAppend "Running $perfcmd script -i $inputFile --fields comm,pid,tid,cpu,time,period,event,ip,sym,dso,trace > $outputDumpFile"
        $perfcmd script -i $inputFile --fields comm,pid,tid,cpu,time,period,event,ip,sym,dso,trace > $outputDumpFile 2>>$logFile
        LogAppend

        # Try capturing without the cpu field which is unavailable in some containerized environments.
        if [ $? -ne 0 ]
        then
            LogAppend "Running $perfcmd script -i $inputFile --fields comm,pid,tid,time,period,event,ip,sym,dso,trace > $outputDumpFile"
            $perfcmd script -i $inputFile --fields comm,pid,tid,time,period,event,ip,sym,dso,trace > $outputDumpFile 2>>$logFile
            LogAppend
        fi

        # If the dump file is zero length, try to collect without the period field, which was added recently.
        if [ ! -s $outputDumpFile ]
        then
            LogAppend "Running $perfcmd script -i $inputFile --fields comm,pid,tid,time,event,ip,sym,dso,trace > $outputDumpFile"
            $perfcmd script -i $inputFile --fields comm,pid,tid,time,event,ip,sym,dso,trace > $outputDumpFile 2>>$logFile
            LogAppend
        fi

        WriteStatus "...FINISHED"
    fi

    WriteStatus "Compressing trace files"
    
    # Move all collected files to the new directory.
    RunSilent "mv -f * $directoryName"

    # Close the log - this stops all writing to the log, so that we can move it into the archive.
    CloseLog

    # Move the log file to the new directory and rename it to the standard log name.
    RunSilent "mv $logFile $directoryName/perfcollect.log"

    # Compress the data.
    local archiveSuffix=".zip"
    local archiveName=$directoryName$archiveSuffix
    RunSilent "$zipcmd -r $archiveName $directoryName"

    # Move back to the original directory.
    popd > /dev/null

    # Move the archive.
    RunSilent "mv $tempDir/$archiveName ."

    WriteStatus "...FINISHED"

    WriteStatus "Cleaning up artifacts"

    # Delete the temp directory.
    RunSilent "rm -rf $tempDir $collectInfoFile"

    WriteStatus "...FINISHED"

    # Tell the user where the trace is.
    WriteStatus
    WriteStatus "Trace saved to $archiveName"
}

##
# Handle the CTRL+C signal.
##
CTRLC_Handler()
{
    # Mark the handler invoked.
    handlerInvoked=1
}

EndCollect()
{
    # The user must either use "collect stop" or CTRL+C to stop collection.
    if [ "$action" == "stop" ]
    then 
        # Recover trace info in the previous program.
        source $collectInfoFile  # TODO: exit and dispose upon missing file
        # New program started, so go back to the temp directory.
        pushd $tempDir > /dev/null
    fi

    if [ "$useLTTng" == "1" ]
    then
        StopLTTngCollection
    fi

    # Update the user.
    WriteStatus
    WriteStatus "...STOPPED."
    WriteStatus
    WriteStatus "Starting post-processing.  This may take some time."
    WriteStatus

    # The user used CTRL+C to stop collection.
    # When this happens, we catch the signal and finish our work.
    ProcessCollectedData
}

##
# Print usage information.
##
PrintUsage()
{
    echo "This script uses perf_event and LTTng to collect and view performance traces for .NET applications."
    echo "For detailed collection and viewing steps, view this script in a text editor or viewer."
    echo ""
    echo "./perfcollect <action> <tracename>"
    echo "Valid Actions: collect start/stop view livetrace install"
    echo ""
    echo "collect options:"
    echo "By default, collection includes CPU samples collected every ms."
    echo "    -pid          : Only collect data from the specified process id."
    echo "    -threadtime   : Collect events for thread time analysis (on and off cpu)."
    echo "    -offcpu       : Collect events for off-cpu analysis."
    echo "    -hwevents     : Collect (some) hardware counters."
    echo ""
    echo "start:"
    echo "    Start collection, but with Lttng trace ONLY. It needs to be used with 'stop' action."
    echo ""
    echo "stop:"
    echo "    Stop collection if 'start' action is used."
    echo ""
    echo "view options:"
    echo "    -processfilter      : Filter data by the specified process name."
    echo "    -graphtype      : Specify the type of graph.  Valid values are 'caller' and 'callee'.  Default is 'callee'."
    echo "    -viewer          : Specify the data viewer.  Valid values are 'perf' and 'lttng'.  Default is 'perf'."
    echo ""
    echo "livetrace:"
    echo "    Print EventSource events directly to the console.  Root privileges not required."
    echo ""
    echo "install options:"
    echo "    Useful for first-time setup.  Installs/upgrades perf_event and LTTng."
    echo "    -force    : Force installation of required packages without prompt"
    echo ""
}

##
# Validate and set arguments.
##

BuildPerfRecordArgs()
{
    # Start with default collection arguments that record at realtime priority, all CPUs (-a), and collect call stacks (-g)
    collectionArgs="record -k 1 -g"

    # Filter to a single process if desired
    if [ "$collectionPid" != "" ]
    then
        collectionArgs="$collectionArgs --pid=$collectionPid"
    else
        collectionArgs="$collectionArgs -a"
    fi

    # Enable CPU Collection
    if [ $collect_cpu -eq 1 ]
    then
        collectionArgs="$collectionArgs"
        eventsToCollect=( "${eventsToCollect[@]}" "cpu-clock" )

        # If only collecting CPU events, set the sampling rate to 1000.
        # Otherwise, use the default sampling rate to avoid sampling sched events.
        if [ $collect_threadTime -eq 0 ] && [ $collect_offcpu -eq 0 ]
        then
            collectionArgs="$collectionArgs -F 1000"
        fi
    fi

    if [ $collect_system -eq 1 ]
    then
        collectionArgs="$collectionArgs -e major-faults -e minor-faults"
    fi

    # Enable HW counters event collection
    if [ $collect_HWevents -eq 1 ]
    then
        collectionArgs="$collectionArgs -e cycles,instructions,branches,cache-misses"
    fi
    
    # Enable context switches.
    if [ $collect_threadTime -eq 1 ]
    then
        eventsToCollect=( "${eventsToCollect[@]}" "sched:sched_stat_sleep" "sched:sched_switch" "sched:sched_process_exit" )
    fi

    # Enable offcpu collection
    if [ $collect_offcpu -eq 1 ]
    then
        eventsToCollect=( "${eventsToCollect[@]}" "sched:sched_stat_sleep" "sched:sched_switch" "sched:sched_process_exit" )
    fi

    # Build up the set of events.
    local eventString=""
    local comma=","
    for (( i=0; i<${#eventsToCollect[@]}; i++ ))
    do
        # Get the arg.
        eventName=${eventsToCollect[$i]}

        # Build up the comma separated list.
        if [ "$eventString" == "" ]
        then
            eventString=$eventName
        else
            eventString="$eventString$comma$eventName"
        fi

    done

    if [ ! -z ${duration} ]
    then
        durationString="sleep ${duration}"
    fi

    # Add the events onto the collection command line args.    
    collectionArgs="$collectionArgs -e $eventString $durationString"
}

DoCollect()
{
    # Ensure the script is run as root.
    EnsureRoot

    # Build collection args.
    # Places the resulting args in $collectionArgs
    BuildPerfRecordArgs

    # Trap CTRL+C
    trap CTRLC_Handler SIGINT

    # Create a temp directory to use for collection.
    local tempDir=`mktemp -d`
    LogAppend "Created temp directory $tempDir"

    # Switch to the directory.
    pushd $tempDir > /dev/null

    # Start LTTng collection.
    if [ "$useLTTng" == "1" ]
    then
        StartLTTngCollection
    fi

    # Tell the user that collection has started and how to exit.
    if [ "$duration" != "" ]
    then
        WriteStatus "Collection started. Collection will automatically stop in $duration second(s).  Press CTRL+C to stop early."
    elif [ "$action" == "start" ]
    then
        WriteStatus "Collection started."
    else
        WriteStatus "Collection started. Press CTRL+C to stop."
    fi

    # Start perf record.
    if [ "$action" == "start" ]
    then 
        # Skip perf, which can only be stopped by CTRL+C.
        # Pass trace directory and session name to the stop process.
        popd > /dev/null
        usePerf=0
        declare -p | grep -e lttngTraceDir -e lttngSessionName -e tempDir -e usePerf -e useLttng -e logFile > $collectInfoFile
    else
        if [ "$usePerf" == "1" ]
        then
            if [ ! -z ${duration} ]
            then
                RunSilent $perfcmd $collectionArgs
            else
                RunSilentBackground $perfcmd $collectionArgs
            fi
        else
            # Wait here until CTRL+C handler gets called when user types CTRL+C.
            LogAppend "Waiting for CTRL+C handler to get called."

            waitTime=0
            for (( ; ; ))
            do
                if [ "$handlerInvoked" == "1" ]
                then
                    break;
                fi

                # Wait and then check to see if the handler has been invoked or we've crossed the duration threshold.
                sleep 1
                waitTime=$waitTime+1
                if (( duration > 0 && duration <= waitTime ))
                then
                    break;
                fi
            done
        fi
        # End collection if action is 'collect'.
        EndCollect
    fi
}

DoLiveTrace()
{
    # Start the session
    StartLTTngCollection

    # View the event stream (until the user hits CTRL+C)
    WriteStatus "Listening for events from LTTng.  Hit CTRL+C to stop."
    $lttngcmd view

    # Stop the LTTng sessoin
    StopLTTngCollection
}

# $1 == Path to directory containing trace files
PropSymbolsAndMapFilesForView()
{
    # Get the current directory
    local currentDir=`pwd`    

    # Copy map files to /tmp since they aren't supported by perf buildid-cache.
    local mapFiles=`find -name *.map`
    for mapFile in $mapFiles
    do
        echo "Copying $mapFile to /tmp."
        cp $mapFile /tmp
    done

    # Cache all debuginfo files saved with the trace in the buildid cache.
    local debugInfoFiles=`find $currentDir -name *.debuginfo`
    for debugInfoFile in $debugInfoFiles
    do
        echo "Caching $debugInfoFile in buildid cache using perf buildid-cache."
        $perfcmd buildid-cache --add=$debugInfoFile
    done
}

SetEnvironment()
{
    WriteStatus "Starting a new shell configured for profiling."
    WriteStatus "When done, type 'exit' to exit the profiling shell."
    bash -c 'export COMPlus_PerfMapEnabled=1;export COMPlus_EnableEventLog=1; exec bash'
    WriteStatus "Profiling shell exited."
}

DoView()
{
    # Generate a temp directory to extract the trace files into.
    local tempDir=`mktemp -d`
    
    # Extract the trace files.
    $unzipcmd $inputTraceName -d $tempDir
    
    # Move the to temp directory.
    pushd $tempDir
    cd `ls`

    # Select the viewer.
    if [ "$viewer" == "perf" ]
    then
        # Prop symbols and map files.
        PropSymbolsAndMapFilesForView `pwd`

        # Choose the view
        if [ "$graphType" == "" ]
        then
            graphType="callee"
        elif [ "$graphType" != "callee" ] && [ "$graphType" != "caller"]
        then
            FatalError "Invalid graph type specified.  Valid values are 'callee' and 'caller'."
        fi

        # Filter to specific process names if desired.
        if [ "$processFilter" != "" ]
        then
            processFilter="--comms=$processFilter"
        fi

        # Execute the viewer.
        $perfcmd report -n -g graph,0.5,$graphType $processFilter $perfOpt
    elif [ "$viewer" == "lttng" ]
    then
        babeltrace lttngTrace/ | more
    fi
    
    # Switch back to the original directory.
    popd

    # Delete the temp directory.
    rm -rf $tempDir
}

#####################################
## Main Script Start
#####################################

# No arguments
if [ "$#" == "0" ]
then
    PrintUsage
    exit 0
fi

# Install perf if requested.  Do this before all other validation.
if [ "$1" == "install" ]
then
    if [ "$2" == "-force" ]
    then 
        forceInstall=1
    fi
    InstallPerf
    InstallLTTng
    exit 0
fi

# Discover external commands that will be called by this script.
DiscoverCommands

# Initialize the log.
if [ "$1" != "stop" ]
then
    InitializeLog
fi

# Process arguments.
ProcessArguments $@

# Ensure prerequisites are installed.
EnsurePrereqsInstalled

# Take the appropriate action.
if [ "$action" == "collect" ] || [ "$action" == "start" ]
then
    DoCollect
elif [ "$action" == "stop" ]
then
    EndCollect
elif [ "$action" == "setenv" ]
then
    SetEnvironment
elif [ "$action" == "view" ]
then
    DoView
elif [ "$action" == "livetrace" ]
then
    DoLiveTrace
fi

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/625079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5.6.1 Ext JS之标签页的关闭和批零关闭

Tab Panel 是包含多个标签页的面板, 这是一种很常用的组件, 类似于浏览器的标签页。关于 Ext JS的Tab Panel的基本使用可以参考: [Ext JS3.9] 标签面板(TabPanel )介绍与开发, 本篇介绍如何关闭单个标签页和批量关闭标签页。 Tab 标签页的可关闭 默认状况下,标签页是无…

Bitmiracle Docotic.Pdf 9.015 Crack

Docotic.Pdf 库是正确的法语和强大的编程和界面&#xff0c;可以让用户和开发人员创建专业和高质量的 PDF 文件&#xff0c;甚至可以阅读和修改那些已经存在的。它具有干净而强大的编程接口&#xff0c;能够帮助用户创建质量非常好的 PDF 文档。在这个库的帮助下&#xff0c;用…

linux log

linux log 一:printk日志级别二:printk打印消息控制printkprintk消息等级查看与修改/etc/rsyslog.conf 一:printk日志级别 数字越小级别越高 二:printk打印消息控制 console_loglevel&#xff1a;只有当printk打印消息的log优先级高于console_loglevel时&#xff0c;才能输出…

C++编译一些常见的错误集锦

目录 1、段错误&#xff08;Segmentation Fault&#xff09; 2、强异常保证&#xff08;strong exception guarantee&#xff09; 3、有效但未定义的状态&#xff08;valid but unspecified state&#xff09; 1、段错误&#xff08;Segmentation Fault&#xff09; &#…

Material—— VAT(Houdini To UE)

目录 一&#xff0c;介绍 二&#xff0c;柔体 二&#xff0c;刚体 一&#xff0c;介绍 VAT是将动画数据存储在纹理中&#xff0c;通过GPU运算来实现动画的技术&#xff1b;VAT纹理包含每个顶点在不同帧的位置信息&#xff0c;而每个像素代表一个顶点在某个时间点的位置&…

Linux项目流程 + 用git将本地代码上传到gitee

目录 前言 一个"进度条"项目 git上传代码 首次上传代码 安装git 克隆仓库连接 将文件传入路径目录 文件上传三板斧 后续提交更新 git实用用法补充 前言 本文将介绍如何使用makefile编写项目并将其上传到git远程仓库。Makefile是一种用于自动化构建、编译…

Python爬虫:从后端分析为什么你爬虫爬取不到数据

仅仅是小编总结的三点而已&#xff0c;可能不是很全面&#xff0c;如果之后小编了解到新的知识点&#xff0c;可能还会增加的哈&#xff01; 1. 最简单的爬虫代码 也就是各位最常使用的&#xff0c;直接利用requests模块访问当前网站链接&#xff0c;利用相关解析模块从而获取…

第三届陕西省大学生网络安全技能大赛wp

文章目录 第三届陕西省大学生网络安全技能大赛wpwebezpoptestezrceunserializeEsc4pe_T0_Mong0 misc管道可是雪啊飘进双眼 第三届陕西省大学生网络安全技能大赛wp web ezpop 在源码找到base64 解码&#xff1a; /pop3ZTgMw.php&#xff0c;访问获得源码&#xff1a; <?…

通过帮助中心提高客户满意度,帮助中心的最佳实践方式

随着技术的不断发展和产品的不断更新&#xff0c;消费者对产品的需求也越来越高。在这个竞争激烈的市场中&#xff0c;企业必须建立一个完善的帮助中心&#xff0c;来提供及时、准确的技术支持和解决方案&#xff0c;以满足客户的各种需求。这样能够有效地提高客户满意度&#…

java+springboot8高校教职工教师档案管理系统

系统设计遵循界面层、业务逻辑层和数据访问层的Web开发三层架构。采用B/S结构,使得系统更加容易维护。系统的设计与实现主要实现角色有管理员和用户,管理员在后台管理用户表模块、token表模块、公告信息模块、教职工模块、工资信息模块、调动离职模块、配置文件模块、出勤信息模…

好用工具第3期:全平台免费音乐LxMusic

LxMusic 1. 概要 LxMusic 是一个跨平台的开源的音乐播放器。支持本地音乐文件播放以及各大平台的音乐搜索播放和下载所有歌曲。 LxMusic 是一个开源项目, 项目地址是: 桌面端 https://github.com/lyswhut/lx-music-desktop 移动端 https://github.com/lyswhut/lx-music-mobi…

Ficus 第二弹,突破限制器的 Markdown 编辑管理软件!

大家好&#xff0c;我们是 ggG 团队&#xff0c;我们开发的 markdown 笔记管理软件 Ficus Beta 版本正式发布了。详情可以见我们官网&#xff0c;也可以来我们仓库查看。 相对于 Alpha 版本&#xff08;可以在我们之前的博客中查看&#xff09;&#xff0c;主要有 3 点明显的提…

【网络】协议定制+序列化/反序列化

为什么要序列化&#xff1f; 如果光看定义很难理解序列化的意义&#xff0c;那么我们可以从另一个角度来推导出什么是序列化, 那么究竟序列化的目的是什么&#xff1f; 其实序列化最终的目的是为了对象可以跨平台存储&#xff0c;和进行网络传输。而我们进行跨平台存储和网络…

[第一章 web入门]常见的搜集

首先就利用御剑或者dirsearch扫描一下网站后台&#xff0c;可以得到以下三个路径返回正常 robots.txt index.php~ .index.php.swp 其实我没扫描出来&#xff0c;这些都是看的别人的wp才做出来的&#xff0c;一开始我其实意识到御剑其实就是有一个字典&#xff0c;然后按照字典的…

【蓝桥杯算法题】用java遍写税收计算

【蓝桥杯算法题】用java遍写税收计算 题目&#xff1a;劳务报酬税收计算&#xff1a;输入 m &#xff0c;输出税后收入。如果 m <800&#xff0c;不扣税&#xff0c; 如果800< m <4000&#xff0e;则 m 减去800后的金额扣除20&#xff05;所得税。如果 m >4000&…

SpringBoot项目使用CXF框架开发Soap通信接口

SpringBoot项目使用CXF框架开发Soap通信接口 物料准备&#xff1a; 1.引入cxf官方提供的SpringBootStarter依赖 2.定义需要暴雷给外部调用的java接口以及接口的实现类 3.配置CxfConfig类&#xff0c;把你定义的java接口注册为webservice的Endpoint 引入cxf-spring-boot-st…

Solidwoks PDM Add-ins (C#) 创建菜单命令

演示如何创建 C# Add-ins :将菜单命令添加到库视图的上下文相关菜单的。 注意&#xff1a;由于 SOLIDWORKS PDM Professional 无法强制重新加载add-ins&#xff0c;因此必须重新启动所有客户端计算机以确保使用最新版本的add-ins。 启动VS。新建项目&#xff0c;选择类库。在“…

数据结构——队列

数据结构——队列 文章目录 数据结构——队列前言队列基本概念队列的基本操作队列的顺序存储结构创建顺序队列代码入队操作代码出队操作代码顺序队列的关键语句 队列的链式存储结构链式队列初始化链式队列判断空链式队列的入队操作链式队列的出队操作 循环队列循环队列基本思想…

「Tech初见」Linux驱动之chrdev

目录 免责声明I. MotivationII. SolutionS1 - 主次设备号S2 - 设备驱动程序S3 - 字符设备驱动程序 III. Result 免责声明 「Tech初见」系列的文章&#xff0c;是本人第一次接触的话题 对所谓真理的理解暂时可能还不到位&#xff0c;避免不了会出现令人嗤鼻的谬论 所以&#…