rocDecode API Tracing Support (#49)

* rocDecode API Tracing support

* Test bin file added to rocdecode. Need to add validate python methods

* Added option to not make rocDecode tests

* Added rocdecode and rocprofv3 tests

* Added csv test

* Address PR comments. Changed tests to use built-in rocstreambit decoder to remove ffmpeg dependancy. Changed cmake option to disbale tests rather than not build them. Tests work locally, but will fail until rocDecode is built with tracing enabled on CI

* Add option to avoid building rocdecode tests

* Added option to avoid building rocdecode bin file

* Merge conflict error

* CMake files changed in response to review comments. Attempting to implement callbacks.

* Turned off test building for rocdecode

* Minor fixes for review comments

* Review comments

* Updated formatting

* Document changes and format.hpp reversion. Need to remove iterate args support for now for later update.

* Remove iterate args support

* Remove iterate-args

* enforce abi versioning in macro if

* Fix doc error

* removed spaces to fix indentation error

---------

Co-authored-by: Madsen, Jonathan <Jonathan.Madsen@amd.com>
Esse commit está contido em:
Trowbridge, Ian
2025-01-17 16:42:25 -06:00
commit de GitHub
commit e307b89ca4
73 arquivos alterados com 7486 adições e 45 exclusões
+4
Ver Arquivo
@@ -62,6 +62,10 @@ add_subdirectory(thread-trace)
add_subdirectory(pc_sampling)
add_subdirectory(hip-graph-tracing)
add_subdirectory(counter-collection)
if(ROCPROFILER_BUILD_ROCDECODE_TESTS)
add_subdirectory(rocdecode)
endif()
if(ROCPROFILER_BUILD_OPENMP_TESTS)
add_subdirectory(openmp-tools)
endif()
+3
Ver Arquivo
@@ -29,3 +29,6 @@ add_subdirectory(hsa-queue-dependency)
add_subdirectory(hip-graph)
add_subdirectory(hsa-memory-allocation)
add_subdirectory(pc-sampling)
if(ROCPROFILER_BUILD_ROCDECODE_TESTS)
add_subdirectory(rocdecode)
endif()
+43
Ver Arquivo
@@ -0,0 +1,43 @@
#
#
#
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
if(NOT CMAKE_HIP_COMPILER)
find_program(
amdclangpp_EXECUTABLE
NAMES amdclang++
HINTS ${ROCM_PATH} ENV ROCM_PATH /opt/rocm
PATHS ${ROCM_PATH} ENV ROCM_PATH /opt/rocm
PATH_SUFFIXES bin llvm/bin NO_CACHE)
mark_as_advanced(amdclangpp_EXECUTABLE)
if(amdclangpp_EXECUTABLE)
set(CMAKE_HIP_COMPILER "${amdclangpp_EXECUTABLE}")
endif()
endif()
project(rocprofiler-tool-test-app-rocdecode LANGUAGES CXX HIP)
foreach(_TYPE DEBUG MINSIZEREL RELEASE RELWITHDEBINFO)
if("${CMAKE_HIP_FLAGS_${_TYPE}}" STREQUAL "")
set(CMAKE_HIP_FLAGS_${_TYPE} "${CMAKE_CXX_FLAGS_${_TYPE}}")
endif()
endforeach()
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_HIP_STANDARD 17)
set(CMAKE_HIP_EXTENSIONS OFF)
set(CMAKE_HIP_STANDARD_REQUIRED ON)
set_source_files_properties(rocdecode.cpp roc_video_dec.cpp PROPERTIES LANGUAGE HIP)
add_executable(rocdecode)
target_sources(rocdecode PRIVATE rocdecode.cpp roc_video_dec.cpp)
find_package(Threads REQUIRED)
find_package(rocDecode REQUIRED)
target_link_libraries(
rocdecode PRIVATE rocprofiler-sdk::tests-build-flags Threads::Threads hsa-runtime64
rocprofiler-sdk::tests-common-library rocDecode::rocDecode)
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+648
Ver Arquivo
@@ -0,0 +1,648 @@
/*
Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once
#include <assert.h>
#include <hip/hip_runtime.h>
#include <rocdecode/rocdecode.h>
#include <rocdecode/rocparser.h>
#include <stdint.h>
#include <string.h>
#include <chrono>
#include <cstring>
#include <exception>
#include <iostream>
#include <mutex>
#include <queue>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
/*!
* \file
* \brief The AMD Video Decode Library.
*
* \defgroup group_amd_roc_video_dec rocDecode Video Decode: AMD Video Decode API
* \brief AMD The rocDecode video decoder for AMDs GPUs.
*/
#define MAX_FRAME_NUM 16
typedef int(ROCDECAPI* PFNRECONFIGUEFLUSHCALLBACK)(void*, uint32_t, void*);
typedef enum SeiAvcHevcPayloadType_enum
{
SEI_TYPE_TIME_CODE = 136,
SEI_TYPE_USER_DATA_UNREGISTERED = 5
} SeiAvcHevcPayloadType;
typedef enum OutputSurfaceMemoryType_enum
{
OUT_SURFACE_MEM_DEV_INTERNAL =
0, /**< Internal interopped decoded surface memory(original mapped decoded surface) */
OUT_SURFACE_MEM_DEV_COPIED = 1, /**< decoded output will be copied to a separate device memory
(the user doesn't need to call release) **/
OUT_SURFACE_MEM_HOST_COPIED = 2, /**< decoded output will be copied to a separate host memory
(the user doesn't need to call release) **/
OUT_SURFACE_MEM_NOT_MAPPED = 3 /**< < decoded output is not available (interop won't be used):
useful for decode only performance app*/
} OutputSurfaceMemoryType;
#define TOSTR(X) std::to_string(static_cast<int>(X))
#define STR(X) std::string(X)
#if DBGINFO
# define INFO(X) \
std::clog << "[INF] " \
<< " {" << __func__ << "} " \
<< " " << X << std::endl;
#else
# define INFO(X) ;
#endif
#define ERR(X) \
std::cerr << "[ERR] " \
<< " {" << __func__ << "} " \
<< " " << X << std::endl;
inline int
GetChromaPlaneCount(rocDecVideoSurfaceFormat surface_format)
{
int num_planes = 1;
switch(surface_format)
{
case rocDecVideoSurfaceFormat_NV12:
case rocDecVideoSurfaceFormat_P016: num_planes = 1; break;
case rocDecVideoSurfaceFormat_YUV444:
case rocDecVideoSurfaceFormat_YUV444_16Bit: num_planes = 2; break;
case rocDecVideoSurfaceFormat_YUV420:
case rocDecVideoSurfaceFormat_YUV420_16Bit: num_planes = 2; break;
}
return num_planes;
};
inline float
GetChromaHeightFactor(rocDecVideoSurfaceFormat surface_format)
{
float factor = 0.5;
switch(surface_format)
{
case rocDecVideoSurfaceFormat_NV12:
case rocDecVideoSurfaceFormat_P016:
case rocDecVideoSurfaceFormat_YUV420:
case rocDecVideoSurfaceFormat_YUV420_16Bit: factor = 0.5; break;
case rocDecVideoSurfaceFormat_YUV444:
case rocDecVideoSurfaceFormat_YUV444_16Bit: factor = 1.0; break;
}
return factor;
};
class RocVideoDecodeException : public std::exception
{
public:
explicit RocVideoDecodeException(const std::string& message, const int err_code)
: _message(message)
, _err_code(err_code)
{}
explicit RocVideoDecodeException(const std::string& message)
: _message(message)
, _err_code(-1)
{}
virtual const char* what() const throw() override { return _message.c_str(); }
int Geterror_code() const { return _err_code; }
private:
std::string _message;
int _err_code;
};
#define ROCDEC_THROW(X, CODE) \
throw RocVideoDecodeException(" { " + std::string(__func__) + " } " + X, CODE);
#define THROW(X) throw RocVideoDecodeException(" { " + std::string(__func__) + " } " + X);
#define ROCDEC_API_CALL(rocDecAPI) \
do \
{ \
rocDecStatus error_code = rocDecAPI; \
if(error_code != ROCDEC_SUCCESS) \
{ \
std::ostringstream error_log; \
error_log << #rocDecAPI << " returned " << rocDecGetErrorName(error_code) << " at " \
<< __FILE__ << ":" << __LINE__; \
ROCDEC_THROW(error_log.str(), error_code); \
} \
} while(0)
#define HIP_API_CALL(call) \
do \
{ \
hipError_t hip_status = call; \
if(hip_status != hipSuccess) \
{ \
const char* sz_err_name = NULL; \
sz_err_name = hipGetErrorName(hip_status); \
std::ostringstream error_log; \
error_log << "hip API error " << sz_err_name; \
ROCDEC_THROW(error_log.str(), hip_status); \
} \
} while(0)
struct Rect
{
int left;
int top;
int right;
int bottom;
};
struct Dim
{
int w, h;
};
static inline int
align(int value, int alignment)
{
return (value + alignment - 1) & ~(alignment - 1);
}
typedef struct DecFrameBuffer_
{
uint8_t* frame_ptr; /**< device memory pointer for the decoded frame */
int64_t pts; /**< timestamp for the decoded frame */
int picture_index; /**< surface index for the decoded frame */
} DecFrameBuffer;
typedef struct OutputSurfaceInfoType
{
uint32_t output_width; /**< Output width of decoded surface*/
uint32_t output_height; /**< Output height of decoded surface*/
uint32_t output_pitch; /**< Output pitch in bytes of luma plane, chroma pitch can be inferred
based on chromaFormat*/
uint32_t output_vstride; /**< Output vertical stride in case of using internal mem pointer **/
uint32_t chroma_height; /**< Chroma plane height **/
Rect disp_rect; /**< Display area **/
uint32_t bytes_per_pixel; /**< Output BytesPerPixel of decoded image*/
uint32_t bit_depth; /**< Output BitDepth of the image*/
uint32_t num_chroma_planes; /**< Output Chroma number of planes*/
uint64_t output_surface_size_in_bytes; /**< Output Image Size in Bytes; including both luma and
chroma planes*/
rocDecVideoSurfaceFormat surface_format; /**< Chroma format of the decoded image*/
OutputSurfaceMemoryType mem_type; /**< Output mem_type of the surface*/
} OutputSurfaceInfo;
typedef struct ReconfigParams_t
{
PFNRECONFIGUEFLUSHCALLBACK p_fn_reconfigure_flush;
void* p_reconfig_user_struct;
uint32_t reconfig_flush_mode;
} ReconfigParams;
class RocVideoDecoder
{
public:
/**
* @brief Construct a new Roc Video Decoder object
*
* @param device_id : device_id to initialize HIP and VCN
* @param out_mem_type : out_mem_type for the decoded surface
* @param codec : codec type
* @param force_zero_latency : to force zero latency (output in decoding orde)
* @param p_crop_rect : to crop output
* @param extract_user_SEI_Message : enable to extract SEI
* @param disp_delay : output delayed by #disp_delay surfaces
* @param max_width : Max. width for the output surface
* @param max_height : Max. height for the output surface
* @param clk_rate : FPS clock-rate
*/
RocVideoDecoder(int device_id,
OutputSurfaceMemoryType out_mem_type,
rocDecVideoCodec codec,
bool force_zero_latency = false,
const Rect* p_crop_rect = nullptr,
bool extract_user_SEI_Message = false,
uint32_t disp_delay = 0,
int max_width = 0,
int max_height = 0,
uint32_t clk_rate = 1000);
~RocVideoDecoder();
rocDecVideoCodec GetCodecId() { return codec_id_; }
hipStream_t GetStream() { return hip_stream_; }
/**
* @brief Get the output frame width
*/
uint32_t GetWidth()
{
assert(disp_width_);
return disp_width_;
}
/**
* @brief This function is used to get the actual decode width
*/
int GetDecodeWidth()
{
assert(coded_width_);
return coded_width_;
}
/**
* @brief Get the output frame height
*/
uint32_t GetHeight()
{
assert(disp_height_);
return disp_height_;
}
/**
* @brief This function is used to get the current chroma height.
*/
int GetChromaHeight()
{
assert(chroma_height_);
return chroma_height_;
}
/**
* @brief This function is used to get the number of chroma planes.
*/
int GetNumChromaPlanes()
{
assert(num_chroma_planes_);
return num_chroma_planes_;
}
/**
* @brief This function is used to get the current frame size based on pixel format.
*/
virtual int GetFrameSize()
{
assert(disp_width_);
return disp_width_ * (disp_height_ + (chroma_height_ * num_chroma_planes_)) *
byte_per_pixel_;
}
/**
* @brief Get the Bit Depth and BytesPerPixel associated with the pixel format
*
* @return uint32_t
*/
uint32_t GetBitDepth()
{
assert(bitdepth_minus_8_);
return (bitdepth_minus_8_ + 8);
}
uint32_t GetBytePerPixel()
{
assert(byte_per_pixel_);
return byte_per_pixel_;
}
/**
* @brief Functions to get the output surface attributes
*/
size_t GetSurfaceSize()
{
assert(surface_size_);
return surface_size_;
}
uint32_t GetSurfaceStride()
{
assert(surface_stride_);
return surface_stride_;
}
// RocDecImageFormat GetSubsampling() { return subsampling_; }
/**
* @brief Get the name of the output format
*
* @param codec_id
* @return std::string
*/
const char* GetCodecFmtName(rocDecVideoCodec codec_id);
/**
* @brief function to return the name from surface_format_id
*
* @param surface_format_id - enum for surface format
* @return const char*
*/
const char* GetSurfaceFmtName(rocDecVideoSurfaceFormat surface_format_id);
/**
* @brief Get the pointer to the Output Image Info
*
* @param surface_info ptr to output surface info
* @return true
* @return false
*/
bool GetOutputSurfaceInfo(OutputSurfaceInfo** surface_info);
/**
* @brief Function to set the Reconfig Params object
*
* @param p_reconfig_params: pointer to reconfig params struct
* @return true : success
* @return false : fail
*/
bool SetReconfigParams(ReconfigParams* p_reconfig_params, bool b_force_reconfig_flush = false);
/**
* @brief Function to force Reconfigure Flush: needed for random seeking to key frames
*
* @return int 1: Success 0: Fail
*/
int FlushAndReconfigure();
/**
* @brief this function decodes a frame and returns the number of frames avalable for display
*
* @param data - pointer to the data buffer that is to be decode
* @param size - size of the data buffer in bytes
* @param pts - presentation timestamp
* @param flags - video packet flags
* @param num_decoded_pics - nummber of pictures decoded in this call
* @return int - num of frames to display
*/
virtual int DecodeFrame(const uint8_t* data,
size_t size,
int pkt_flags,
int64_t pts = 0,
int* num_decoded_pics = nullptr);
/**
* @brief This function returns a decoded frame and timestamp. This should be called in a loop
* fetching all the available frames
*
*/
virtual uint8_t* GetFrame(int64_t* pts);
/**
* @brief function to release frame after use by the application: Only used with
* "OUT_SURFACE_MEM_DEV_INTERNAL"
*
* @param pTimestamp - timestamp of the frame to be released (unmapped)
* @param b_flushing - true when flushing
* @return true - success
* @return false - falied
*/
virtual bool ReleaseFrame(int64_t pTimestamp, bool b_flushing = false);
/**
* @brief utility function to save image to a file
*
* @param output_file_name - file to write
* @param dev_mem - dev_memory pointer of the frame
* @param image_info - output image info
* @param is_output_RGB - to write in RGB
*/
// void SaveImage(std::string output_file_name, void* dev_mem, OutputImageInfo* image_info, bool
// is_output_RGB = 0);
/**
* @brief Get the Device info for the current device
*
* @param device_name
* @param gcn_arch_name
* @param pci_bus_id
* @param pci_domain_id
* @param pci_device_id
*/
void GetDeviceinfo(std::string& device_name,
std::string& gcn_arch_name,
int& pci_bus_id,
int& pci_domain_id,
int& pci_device_id);
/**
* @brief Helper function to dump decoded output surface to file
*
* @param output_file_name - Output file name
* @param dev_mem - pointer to surface memory
* @param surf_info - surface info
* @param rgb_image_size - image size for rgb (optional). A non_zero value indicates the
* surf_mem holds an rgb interleaved image and the entire size will be dumped to file
*/
virtual void SaveFrameToFile(std::string output_file_name,
void* surf_mem,
OutputSurfaceInfo* surf_info,
size_t rgb_image_size = 0);
/**
* @brief Helper funtion to close a existing file and dump to new file in case of multiple files
* using same decoder
*/
virtual void ResetSaveFrameToFile();
/**
* @brief Get the Num Of Flushed Frames from video decoder object
*
* @return int32_t
*/
int32_t GetNumOfFlushedFrames() { return num_frames_flushed_during_reconfig_; }
/*! \brief Function to wait for the decode completion of the last submitted picture
*/
void WaitForDecodeCompletion();
// Session overhead refers to decoder initialization and deinitialization time
void AddDecoderSessionOverHead(std::thread::id session_id, double duration)
{
session_overhead_[session_id] += duration;
}
double GetDecoderSessionOverHead(std::thread::id session_id)
{
if(session_overhead_.find(session_id) != session_overhead_.end())
{
return session_overhead_[session_id];
}
else
{
return 0;
}
}
/**
* @brief Check if the given Video Codec is supported on the given GPU
*
* @return rocDecStatus
*/
bool CodecSupported(int device_id, rocDecVideoCodec codec_id, uint32_t bit_depth);
/**
* @brief This function reconfigure decoder if there is a change in sequence params.
*/
virtual int ReconfigureDecoder(RocdecVideoFormat* p_video_format);
protected:
/**
* @brief Callback function to be registered for getting a callback when decoding of sequence
* starts
*/
static int ROCDECAPI HandleVideoSequenceProc(void* p_user_data,
RocdecVideoFormat* p_video_format)
{
return ((RocVideoDecoder*) p_user_data)->HandleVideoSequence(p_video_format);
}
/**
* @brief Callback function to be registered for getting a callback when a decoded frame is
* ready to be decoded
*/
static int ROCDECAPI HandlePictureDecodeProc(void* p_user_data, RocdecPicParams* p_pic_params)
{
return ((RocVideoDecoder*) p_user_data)->HandlePictureDecode(p_pic_params);
}
/**
* @brief Callback function to be registered for getting a callback when a decoded frame is
* available for display
*/
static int ROCDECAPI HandlePictureDisplayProc(void* p_user_data,
RocdecParserDispInfo* p_disp_info)
{
return ((RocVideoDecoder*) p_user_data)->HandlePictureDisplay(p_disp_info);
}
/**
* @brief Callback function to be registered for getting a callback when all the unregistered
* user SEI Messages are parsed for a frame.
*/
static int ROCDECAPI HandleSEIMessagesProc(void* p_user_data,
RocdecSeiMessageInfo* p_sei_message_info)
{
return ((RocVideoDecoder*) p_user_data)->GetSEIMessage(p_sei_message_info);
}
/**
* @brief This function gets called when a sequence is ready to be decoded. The function also
gets called when there is format change
*/
int HandleVideoSequence(RocdecVideoFormat* p_video_format);
/**
* @brief This function gets called when a picture is ready to be decoded. cuvidDecodePicture
* is called from this function to decode the picture
*/
int HandlePictureDecode(RocdecPicParams* p_pic_params);
/**
* @brief This function gets called after a picture is decoded and available for display.
Frames are fetched and stored in internal buffer
*/
int HandlePictureDisplay(RocdecParserDispInfo* p_disp_info);
/**
* @brief This function gets called when all unregistered user SEI messages are parsed for a
* frame
*/
int GetSEIMessage(RocdecSeiMessageInfo* p_sei_message_info);
/**
* @brief function to release all internal frames and clear the vp_frames_q_ (used with
* reconfigure): Only used with "OUT_SURFACE_MEM_DEV_INTERNAL"
*
* @return true - success
* @return false - falied
*/
bool ReleaseInternalFrames();
/**
* @brief Function to Initialize GPU-HIP
*
*/
bool InitHIP(int device_id);
/**
* @brief Function to get start time
*
*/
std::chrono::_V2::system_clock::time_point StartTimer();
/**
* @brief Function to get elapsed time
*
*/
double StopTimer(const std::chrono::_V2::system_clock::time_point& start_time);
int num_devices_;
int device_id_;
RocdecVideoParser rocdec_parser_ = nullptr;
rocDecDecoderHandle roc_decoder_ = nullptr;
OutputSurfaceMemoryType out_mem_type_ = OUT_SURFACE_MEM_DEV_INTERNAL;
bool b_extract_sei_message_ = false;
bool b_force_zero_latency_ = false;
uint32_t disp_delay_;
ReconfigParams* p_reconfig_params_ = nullptr;
bool b_force_recofig_flush_ = false;
int32_t num_frames_flushed_during_reconfig_ = 0;
hipDeviceProp_t hip_dev_prop_;
hipStream_t hip_stream_;
rocDecVideoCodec codec_id_ = rocDecVideoCodec_NumCodecs;
rocDecVideoChromaFormat video_chroma_format_ = rocDecVideoChromaFormat_420;
rocDecVideoSurfaceFormat video_surface_format_ = rocDecVideoSurfaceFormat_NV12;
RocdecSeiMessageInfo* curr_sei_message_ptr_ = nullptr;
RocdecSeiMessageInfo sei_message_display_q_[MAX_FRAME_NUM];
RocdecVideoFormat* curr_video_format_ptr_ = nullptr;
int output_frame_cnt_ = 0, output_frame_cnt_ret_ = 0;
int decoded_pic_cnt_ = 0;
int decode_poc_ = 0, pic_num_in_dec_order_[MAX_FRAME_NUM];
int num_alloced_frames_ = 0;
int last_decode_surf_idx_ = 0;
std::ostringstream input_video_info_str_;
int bitdepth_minus_8_ = 0;
uint32_t byte_per_pixel_ = 1;
uint32_t coded_width_ = 0;
uint32_t disp_width_ = 0;
uint32_t coded_height_ = 0;
uint32_t disp_height_ = 0;
uint32_t target_width_ = 0;
uint32_t target_height_ = 0;
int max_width_ = 0, max_height_ = 0;
uint32_t chroma_height_ = 0, chroma_width_ = 0;
uint32_t num_chroma_planes_ = 0;
uint32_t num_components_ = 0;
uint32_t surface_stride_ = 0;
uint32_t surface_vstride_ = 0,
chroma_vstride_ =
0; // vertical stride between planes: used when using internal dev memory
size_t surface_size_ = 0;
OutputSurfaceInfo output_surface_info_ = {};
std::mutex mtx_vp_frame_;
std::vector<DecFrameBuffer> vp_frames_; // vector of decoded frames
std::queue<DecFrameBuffer> vp_frames_q_;
Rect disp_rect_ = {}; // displayable area specified in the bitstream
Rect crop_rect_ = {}; // user specified region of interest within diplayable area disp_rect_
FILE* fp_sei_ = NULL;
FILE* fp_out_ = NULL;
bool is_decoder_reconfigured_ = false;
std::string current_output_filename = "";
uint32_t extra_output_file_count_ = 0;
std::thread::id
decoder_session_id_; // Decoder session identifier. Used to gather session level stats.
std::unordered_map<std::thread::id, double>
session_overhead_; // Records session overhead of initialization+deinitialization time.
// Format is (thread id, duration)
};
+109
Ver Arquivo
@@ -0,0 +1,109 @@
/*
Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <rocdecode/roc_bitstream_reader.h>
#include <rocdecode/rocdecode.h>
#include <rocdecode/rocparser.h>
#include <iostream>
#include "roc_video_dec.h"
int
main(int argc, char** argv)
{
// Get input file
std::string input_file_path{};
for(int i = 1; i < argc; i++)
{
if(!strcmp(argv[i], "-i"))
{
if(++i == argc)
{
std::cerr << "Provide path to input file" << std::endl;
}
input_file_path = argv[i];
continue;
}
}
// Set up bitstreamreader
RocdecBitstreamReader bs_reader = nullptr;
rocDecVideoCodec rocdec_codec_id{};
int bit_depth{};
if(rocDecCreateBitstreamReader(&bs_reader, input_file_path.c_str()) != ROCDEC_SUCCESS)
{
std::cerr << "Failed to create the bitstream reader." << std::endl;
return 1;
}
if(rocDecGetBitstreamCodecType(bs_reader, &rocdec_codec_id) != ROCDEC_SUCCESS)
{
std::cerr << "Failed to get stream codec type." << std::endl;
return 1;
}
if(rocdec_codec_id >= rocDecVideoCodec_NumCodecs)
{
std::cerr << "Unsupported stream file type or codec type by the bitstream reader. Exiting."
<< std::endl;
return 1;
}
if(rocDecGetBitstreamBitDepth(bs_reader, &bit_depth) != ROCDEC_SUCCESS)
{
std::cerr << "Failed to get stream bit depth." << std::endl;
return 1;
}
// Set up video decoder
int device_id = 0;
OutputSurfaceMemoryType mem_type = OUT_SURFACE_MEM_DEV_INTERNAL;
bool b_force_zero_latency = false;
Rect* p_crop_rect = nullptr;
int disp_delay = 1;
bool b_extract_sei_messages = false;
RocVideoDecoder* viddec = new RocVideoDecoder(device_id,
mem_type,
rocdec_codec_id,
b_force_zero_latency,
p_crop_rect,
b_extract_sei_messages,
disp_delay);
uint8_t* pvideo = nullptr;
int n_video_bytes = 0;
int64_t pts = 0;
int pkg_flags = 0;
int decoded_pics = 0;
if(rocDecGetBitstreamPicData(bs_reader, &pvideo, &n_video_bytes, &pts) != ROCDEC_SUCCESS)
{
std::cerr << "Failed to get picture data." << std::endl;
return 1;
}
// Treat 0 bitstream size as end of stream indicator
if(n_video_bytes == 0)
{
pkg_flags |= ROCDEC_PKT_ENDOFSTREAM;
}
viddec->DecodeFrame(pvideo, n_video_bytes, pkg_flags, pts, &decoded_pics);
viddec->DecodeFrame(pvideo, n_video_bytes, pkg_flags, pts, &decoded_pics);
viddec->DecodeFrame(pvideo, n_video_bytes, pkg_flags, pts, &decoded_pics);
if(bs_reader)
{
rocDecDestroyBitstreamReader(bs_reader);
}
}
+11 -1
Ver Arquivo
@@ -26,7 +26,15 @@ from __future__ import absolute_import
def test_perfetto_data(
pftrace_data,
json_data,
categories=("hip", "hsa", "marker", "kernel", "memory_copy", "memory_allocation"),
categories=(
"hip",
"hsa",
"marker",
"kernel",
"memory_copy",
"memory_allocation",
"rocdecode_api",
),
):
mapping = {
@@ -36,6 +44,7 @@ def test_perfetto_data(
"kernel": ("kernel_dispatch", "kernel_dispatch"),
"memory_copy": ("memory_copy", "memory_copy"),
"memory_allocation": ("memory_allocation", "memory_allocation"),
"rocdecode_api": ("rocdecode_api", "rocdecode_api"),
}
# make sure they specified valid categories
@@ -73,6 +82,7 @@ def test_otf2_data(
"kernel": ("kernel_dispatch", "kernel_dispatch"),
"memory_copy": ("memory_copy", "memory_copy"),
"memory_allocation": ("memory_allocation", "memory_allocation"),
"rocdecode_api": ("rocdecode_api", "rocdecode_api"),
}
# make sure they specified valid categories
+53
Ver Arquivo
@@ -0,0 +1,53 @@
#
#
#
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
project(
rocprofiler-tests-rocdecode-tracing
LANGUAGES CXX
VERSION 0.0.0)
find_package(rocprofiler-sdk REQUIRED)
if(ROCPROFILER_MEMCHECK_PRELOAD_ENV)
set(PRELOAD_ENV
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}:$<TARGET_FILE:rocprofiler-sdk-json-tool>")
else()
set(PRELOAD_ENV "LD_PRELOAD=$<TARGET_FILE:rocprofiler-sdk-json-tool>")
endif()
set(ROCDECODE_VIDEO_FILE
"${ROCM_PATH}/share/rocdecode/video/AMD_driving_virtual_20-H265.265")
if(NOT EXISTS "${ROCDECODE_VIDEO_FILE}")
message(
FATAL_ERROR
"Unable to find video file for rocdecode tests: ${ROCDECODE_VIDEO_FILE}")
endif()
add_test(NAME test-rocdecode-tracing-execute COMMAND $<TARGET_FILE:rocdecode> -i
${ROCDECODE_VIDEO_FILE})
set(rocdecode-tracing-env
"${PRELOAD_ENV}"
"ROCPROFILER_TOOL_OUTPUT_FILE=rocdecode-tracing-test.json"
"LD_LIBRARY_PATH=$<TARGET_FILE_DIR:rocprofiler-sdk::rocprofiler-sdk-shared-library>:$ENV{LD_LIBRARY_PATH}"
)
set_tests_properties(
test-rocdecode-tracing-execute
PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT
"${rocdecode-tracing-env}" FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
# copy to binary directory
rocprofiler_configure_pytest_files(COPY validate.py conftest.py CONFIG pytest.ini)
add_test(NAME test-rocdecode-tracing-validate
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --input
${CMAKE_CURRENT_BINARY_DIR}/rocdecode-tracing-test.json)
set_tests_properties(
test-rocdecode-tracing-validate
PROPERTIES TIMEOUT 45 LABELS "integration-tests" DEPENDS
test-rocdecode-tracing-execute FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
+22
Ver Arquivo
@@ -0,0 +1,22 @@
#!/usr/bin/env python3
import json
import pytest
from rocprofiler_sdk.pytest_utils.dotdict import dotdict
def pytest_addoption(parser):
parser.addoption(
"--input",
action="store",
default="rocdecode-tracing-test.json",
help="Input JSON",
)
@pytest.fixture
def input_data(request):
filename = request.config.getoption("--input")
with open(filename, "r") as inp:
return dotdict(json.load(inp))
+5
Ver Arquivo
@@ -0,0 +1,5 @@
[pytest]
addopts = --durations=20 -rA -s -vv
testpaths = validate.py
pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages
+285
Ver Arquivo
@@ -0,0 +1,285 @@
#!/usr/bin/env python3
import sys
import pytest
# helper function
def node_exists(name, data, min_len=1):
assert name in data
assert data[name] is not None
if isinstance(data[name], (list, tuple, dict, set)):
assert len(data[name]) >= min_len, f"{name}:\n{data}"
def test_data_structure(input_data):
"""verify minimum amount of expected data is present"""
data = input_data
node_exists("rocprofiler-sdk-json-tool", data)
sdk_data = data["rocprofiler-sdk-json-tool"]
node_exists("metadata", sdk_data)
node_exists("pid", sdk_data["metadata"])
node_exists("main_tid", sdk_data["metadata"])
node_exists("init_time", sdk_data["metadata"])
node_exists("fini_time", sdk_data["metadata"])
node_exists("agents", sdk_data)
node_exists("call_stack", sdk_data)
node_exists("callback_records", sdk_data)
node_exists("buffer_records", sdk_data)
node_exists("names", sdk_data["callback_records"])
node_exists("hsa_api_traces", sdk_data["callback_records"])
node_exists("hip_api_traces", sdk_data["callback_records"])
node_exists("memory_allocations", sdk_data["callback_records"])
node_exists("rocdecode_api_traces", sdk_data["callback_records"])
node_exists("names", sdk_data["buffer_records"])
node_exists("hsa_api_traces", sdk_data["buffer_records"])
node_exists("hip_api_traces", sdk_data["buffer_records"])
node_exists("memory_allocations", sdk_data["buffer_records"])
node_exists("rocdecode_api_traces", sdk_data["buffer_records"])
def test_size_entries(input_data):
# check that size fields are > 0 but account for function arguments
# which are named "size"
def check_size(data, bt):
if "size" in data.keys():
if isinstance(data["size"], str) and bt.endswith('["args"]'):
pass
else:
assert data["size"] > 0, f"origin: {bt}"
# recursively check the entire data structure
def iterate_data(data, bt):
if isinstance(data, (list, tuple)):
for i, itr in enumerate(data):
if isinstance(itr, dict):
check_size(itr, f"{bt}[{i}]")
iterate_data(itr, f"{bt}[{i}]")
elif isinstance(data, dict):
check_size(data, f"{bt}")
for key, itr in data.items():
iterate_data(itr, f'{bt}["{key}"]')
# start recursive check over entire JSON dict
iterate_data(input_data, "input_data")
def test_timestamps(input_data):
"""Verify starting timestamps are less than ending timestamps"""
data = input_data
sdk_data = data["rocprofiler-sdk-json-tool"]
cb_start = {}
cb_end = {}
for titr in ["hsa_api_traces", "hip_api_traces", "rocdecode_api_traces"]:
for itr in sdk_data["callback_records"][titr]:
cid = itr["correlation_id"]["internal"]
phase = itr["phase"]
if phase == 1:
cb_start[cid] = itr["timestamp"]
elif phase == 2:
cb_end[cid] = itr["timestamp"]
assert cb_start[cid] <= itr["timestamp"]
else:
assert phase == 1 or phase == 2
for itr in sdk_data["buffer_records"][titr]:
assert itr["start_timestamp"] <= itr["end_timestamp"]
for titr in ["memory_allocations"]:
for itr in sdk_data["buffer_records"][titr]:
assert itr["start_timestamp"] < itr["end_timestamp"], f"[{titr}] {itr}"
assert itr["correlation_id"]["internal"] > 0, f"[{titr}] {itr}"
assert itr["correlation_id"]["external"] > 0, f"[{titr}] {itr}"
assert (
sdk_data["metadata"]["init_time"] < itr["start_timestamp"]
), f"[{titr}] {itr}"
assert (
sdk_data["metadata"]["init_time"] < itr["end_timestamp"]
), f"[{titr}] {itr}"
assert (
sdk_data["metadata"]["fini_time"] > itr["start_timestamp"]
), f"[{titr}] {itr}"
assert (
sdk_data["metadata"]["fini_time"] > itr["end_timestamp"]
), f"[{titr}] {itr}"
api_start = cb_start[itr["correlation_id"]["internal"]]
# api_end = cb_end[itr["correlation_id"]["internal"]]
assert api_start < itr["start_timestamp"], f"[{titr}] {itr}"
# assert api_end <= itr["end_timestamp"], f"[{titr}] {itr}"
def test_internal_correlation_ids(input_data):
"""Assure correlation ids are unique"""
data = input_data
sdk_data = data["rocprofiler-sdk-json-tool"]
api_corr_ids = []
for titr in ["hsa_api_traces", "hip_api_traces", "rocdecode_api_traces"]:
for itr in sdk_data["callback_records"][titr]:
api_corr_ids.append(itr["correlation_id"]["internal"])
for itr in sdk_data["buffer_records"][titr]:
api_corr_ids.append(itr["correlation_id"]["internal"])
api_corr_ids_sorted = sorted(api_corr_ids)
api_corr_ids_unique = list(set(api_corr_ids))
for itr in sdk_data["buffer_records"]["memory_allocations"]:
assert itr["correlation_id"]["internal"] in api_corr_ids_unique
len_corr_id_unq = len(api_corr_ids_unique)
assert len(api_corr_ids) != len_corr_id_unq
assert max(api_corr_ids_sorted) == len_corr_id_unq
def test_external_correlation_ids(input_data):
data = input_data
sdk_data = data["rocprofiler-sdk-json-tool"]
extern_corr_ids = []
for titr in ["hsa_api_traces", "hip_api_traces", "rocdecode_api_traces"]:
for itr in sdk_data["callback_records"][titr]:
assert itr["correlation_id"]["external"] > 0
assert itr["thread_id"] == itr["correlation_id"]["external"]
extern_corr_ids.append(itr["correlation_id"]["external"])
extern_corr_ids = list(set(sorted(extern_corr_ids)))
for titr in ["hsa_api_traces", "hip_api_traces", "rocdecode_api_traces"]:
for itr in sdk_data["buffer_records"][titr]:
assert itr["correlation_id"]["external"] > 0, f"[{titr}] {itr}"
assert (
itr["thread_id"] == itr["correlation_id"]["external"]
), f"[{titr}] {itr}"
assert itr["thread_id"] in extern_corr_ids, f"[{titr}] {itr}"
assert itr["correlation_id"]["external"] in extern_corr_ids, f"[{titr}] {itr}"
for titr in ["memory_allocations"]:
for itr in sdk_data["buffer_records"][titr]:
assert itr["correlation_id"]["external"] > 0, f"[{titr}] {itr}"
assert itr["correlation_id"]["external"] in extern_corr_ids, f"[{titr}] {itr}"
for itr in sdk_data["callback_records"][titr]:
assert itr["correlation_id"]["external"] > 0, f"[{titr}] {itr}"
assert itr["correlation_id"]["external"] in extern_corr_ids, f"[{titr}] {itr}"
def get_operation(record, kind_name, op_name=None):
for idx, itr in enumerate(record["names"]):
if kind_name == itr["kind"]:
if op_name is None:
return idx, itr["operations"]
else:
for oidx, oname in enumerate(itr["operations"]):
if op_name == oname:
return oidx
return None
def test_rocdecode_traces(input_data):
data = input_data
sdk_data = data["rocprofiler-sdk-json-tool"]
callback_records = sdk_data["callback_records"]
buffer_records = sdk_data["buffer_records"]
rocdecode_bf_traces = sdk_data["buffer_records"]["rocdecode_api_traces"]
rocdecode_api_bf_ops = get_operation(buffer_records, "ROCDECODE_API")
assert len(rocdecode_api_bf_ops[1]) == 16
rocdecode_cb_traces = sdk_data["callback_records"]["rocdecode_api_traces"]
rocdecode_api_cb_ops = get_operation(callback_records, "ROCDECODE_API")
assert (
rocdecode_api_bf_ops[1] == rocdecode_api_cb_ops[1]
and len(rocdecode_api_cb_ops[1]) == 16
)
# check that buffer and callback records agree
phase_enter_count = 0
phase_end_count = 0
api_calls = []
for api_call in rocdecode_cb_traces:
if api_call["phase"] == 1:
phase_enter_count += 1
api_calls.append(rocdecode_api_cb_ops[1][api_call["operation"]])
if api_call["phase"] == 2:
phase_end_count += 1
assert phase_enter_count == phase_end_count == len(rocdecode_bf_traces)
for call in [
"rocDecCreateBitstreamReader",
"rocDecGetBitstreamCodecType",
"rocDecGetBitstreamBitDepth",
"rocDecCreateVideoParser",
"rocDecGetBitstreamPicData",
"rocDecGetDecoderCaps",
"rocDecCreateDecoder",
"rocDecDecodeFrame",
"rocDecParseVideoData",
"rocDecGetVideoFrame",
"rocDecGetDecodeStatus",
"rocDecDestroyBitstreamReader",
]:
assert call in api_calls
def test_retired_correlation_ids(input_data):
data = input_data
sdk_data = data["rocprofiler-sdk-json-tool"]
def _sort_dict(inp):
return dict(sorted(inp.items()))
api_corr_ids = {}
for titr in ["hsa_api_traces", "hip_api_traces", "rocdecode_api_traces"]:
for itr in sdk_data["buffer_records"][titr]:
corr_id = itr["correlation_id"]["internal"]
assert corr_id not in api_corr_ids.keys()
api_corr_ids[corr_id] = itr
alloc_corr_ids = {}
for titr in ["memory_allocations"]:
for itr in sdk_data["buffer_records"][titr]:
corr_id = itr["correlation_id"]["internal"]
assert corr_id not in alloc_corr_ids.keys()
alloc_corr_ids[corr_id] = itr
retired_corr_ids = {}
for itr in sdk_data["buffer_records"]["retired_correlation_ids"]:
corr_id = itr["internal_correlation_id"]
assert corr_id not in retired_corr_ids.keys()
retired_corr_ids[corr_id] = itr
api_corr_ids = _sort_dict(api_corr_ids)
alloc_corr_ids = _sort_dict(alloc_corr_ids)
retired_corr_ids = _sort_dict(retired_corr_ids)
for cid, itr in alloc_corr_ids.items():
assert cid in retired_corr_ids.keys()
retired_ts = retired_corr_ids[cid]["timestamp"]
end_ts = itr["end_timestamp"]
assert (retired_ts - end_ts) > 0, f"correlation-id: {cid}, data: {itr}"
for cid, itr in api_corr_ids.items():
assert cid in retired_corr_ids.keys()
retired_ts = retired_corr_ids[cid]["timestamp"]
end_ts = itr["end_timestamp"]
assert (retired_ts - end_ts) > 0, f"correlation-id: {cid}, data: {itr}"
assert len(api_corr_ids.keys()) == (len(retired_corr_ids.keys()))
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)
+3
Ver Arquivo
@@ -36,3 +36,6 @@ add_subdirectory(roctracer-roctx)
add_subdirectory(scratch-memory)
add_subdirectory(pc-sampling)
add_subdirectory(collection-period)
if(ROCPROFILER_BUILD_ROCDECODE_TESTS)
add_subdirectory(rocdecode-trace)
endif()
@@ -0,0 +1,52 @@
#
#
#
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
project(
rocprofiler-tests-rocprofv3-rocdecode-tracing
LANGUAGES CXX
VERSION 0.0.0)
find_package(rocprofiler-sdk REQUIRED)
rocprofiler_configure_pytest_files(CONFIG pytest.ini COPY validate.py conftest.py)
string(REPLACE "LD_PRELOAD=" "ROCPROF_PRELOAD=" PRELOAD_ENV
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}")
set(rocdecode-tracing-env "${PRELOAD_ENV}")
set(ROCDECODE_VIDEO_FILE
"${ROCM_PATH}/share/rocdecode/video/AMD_driving_virtual_20-H265.265")
if(NOT EXISTS "${ROCDECODE_VIDEO_FILE}")
message(
FATAL_ERROR
"Unable to find video file for rocdecode tests: ${ROCDECODE_VIDEO_FILE}")
endif()
add_test(
NAME rocprofv3-test-rocdecode-tracing-execute
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> --rocdecode-trace -d
${CMAKE_CURRENT_BINARY_DIR}/%tag%-trace -o out --output-format json otf2 pftrace
csv --log-level env -- $<TARGET_FILE:rocdecode> -i ${ROCDECODE_VIDEO_FILE})
set_tests_properties(
rocprofv3-test-rocdecode-tracing-execute
PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT
"${rocdecode-tracing-env}" FAIL_REGULAR_EXPRESSION "threw an exception")
add_test(
NAME rocprofv3-test-rocdecode-tracing-validate
COMMAND
${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input
${CMAKE_CURRENT_BINARY_DIR}/rocdecode-trace/out_results.json --otf2-input
${CMAKE_CURRENT_BINARY_DIR}/rocdecode-trace/out_results.otf2 --pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/rocdecode-trace/out_results.pftrace --csv-input
${CMAKE_CURRENT_BINARY_DIR}/rocdecode-trace/out_rocdecode_api_trace.csv)
set_tests_properties(
rocprofv3-test-rocdecode-tracing-validate
PROPERTIES TIMEOUT 45 LABELS "integration-tests" DEPENDS
rocprofv3-test-rocdecode-tracing-execute FAIL_REGULAR_EXPRESSION
"AssertionError")
+71
Ver Arquivo
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
import csv
import json
import os
import pytest
from rocprofiler_sdk.pytest_utils.dotdict import dotdict
from rocprofiler_sdk.pytest_utils import collapse_dict_list
from rocprofiler_sdk.pytest_utils.perfetto_reader import PerfettoReader
from rocprofiler_sdk.pytest_utils.otf2_reader import OTF2Reader
def pytest_addoption(parser):
parser.addoption(
"--json-input",
action="store",
default="rocdecode-tracing/out_results.json",
help="Input JSON",
)
parser.addoption(
"--otf2-input",
action="store",
default="rocdecode-tracing/out_results.otf2",
help="Input OTF2",
)
parser.addoption(
"--pftrace-input",
action="store",
default="rocdecode-tracing/out_results.pftrace",
help="Input pftrace file",
)
parser.addoption(
"--csv-input",
action="store",
default="rocdecode-tracing/out_rocdecode_api_trace.csv",
help="Input CSV",
)
@pytest.fixture
def json_data(request):
filename = request.config.getoption("--json-input")
with open(filename, "r") as inp:
return dotdict(collapse_dict_list(json.load(inp)))
@pytest.fixture
def csv_data(request):
filename = request.config.getoption("--csv-input")
data = []
with open(filename, "r") as inp:
reader = csv.DictReader(inp)
for row in reader:
data.append(row)
return data
@pytest.fixture
def otf2_data(request):
filename = request.config.getoption("--otf2-input")
if not os.path.exists(filename):
raise FileExistsError(f"{filename} does not exist")
return OTF2Reader(filename).read()[0]
@pytest.fixture
def pftrace_data(request):
filename = request.config.getoption("--pftrace-input")
return PerfettoReader(filename).read()[0]
+5
Ver Arquivo
@@ -0,0 +1,5 @@
[pytest]
addopts = --durations=20 -rA -s -vv
testpaths = validate.py
pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages
+138
Ver Arquivo
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
import sys
import pytest
import json
from collections import defaultdict
# helper function
def node_exists(name, data, min_len=1):
assert name in data
assert data[name] is not None
if isinstance(data[name], (list, tuple, dict, set)):
assert len(data[name]) >= min_len
def get_operation(record, kind_name, op_name=None):
for idx, itr in enumerate(record["strings"]["buffer_records"]):
if kind_name == itr["kind"]:
if op_name is None:
return idx, itr["operations"]
else:
for oidx, oname in enumerate(itr["operations"]):
if op_name == oname:
return oidx
return None
def test_rocdeocde(json_data):
data = json_data["rocprofiler-sdk-tool"]
buffer_records = data["buffer_records"]
rocdecode_data = buffer_records["rocdecode_api"]
_, bf_op_names = get_operation(data, "ROCDECODE_API")
assert len(bf_op_names) == 16
rocdecode_reported_agent_ids = set()
# check buffering data
for node in rocdecode_data:
assert "size" in node
assert "kind" in node
assert "operation" in node
assert "correlation_id" in node
assert "end_timestamp" in node
assert "start_timestamp" in node
assert "thread_id" in node
assert node.size > 0
assert node.thread_id > 0
assert node.start_timestamp > 0
assert node.end_timestamp > 0
assert node.start_timestamp < node.end_timestamp
assert data.strings.buffer_records[node.kind].kind == "ROCDECODE_API"
assert (
data.strings.buffer_records[node.kind].operations[node.operation]
in bf_op_names
)
def test_csv_data(csv_data):
assert len(csv_data) > 0, "Expected non-empty csv data"
api_calls = []
for row in csv_data:
assert "Domain" in row, "'Domain' was not present in csv data for rocdecode-trace"
assert (
"Function" in row
), "'Function' was not present in csv data for rocdecode-trace"
assert (
"Process_Id" in row
), "'Process_Id' was not present in csv data for rocdecode-trace"
assert (
"Thread_Id" in row
), "'Thread_Id' was not present in csv data for rocdecode-trace"
assert (
"Correlation_Id" in row
), "'Correlation_Id' was not present in csv data for rocdecode-trace"
assert (
"Start_Timestamp" in row
), "'Start_Timestamp' was not present in csv data for rocdecode-trace"
assert (
"End_Timestamp" in row
), "'End_Timestamp' was not present in csv data for rocdecode-trace"
api_calls.append(row["Function"])
assert row["Domain"] == "ROCDECODE_API"
assert int(row["Process_Id"]) > 0
assert int(row["Thread_Id"]) > 0
assert int(row["Start_Timestamp"]) > 0
assert int(row["End_Timestamp"]) > 0
assert int(row["Start_Timestamp"]) < int(row["End_Timestamp"])
for call in [
"rocDecCreateBitstreamReader",
"rocDecGetBitstreamCodecType",
"rocDecGetBitstreamBitDepth",
"rocDecCreateVideoParser",
"rocDecGetBitstreamPicData",
"rocDecGetDecoderCaps",
"rocDecCreateDecoder",
"rocDecDecodeFrame",
"rocDecParseVideoData",
"rocDecGetVideoFrame",
"rocDecGetDecodeStatus",
"rocDecDestroyBitstreamReader",
]:
assert call in api_calls
def test_perfetto_data(pftrace_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_perfetto_data(
pftrace_data,
json_data,
("hip", "hsa", "memory_allocation", "rocdecode_api"),
)
def test_otf2_data(otf2_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_otf2_data(
otf2_data,
json_data,
("hip", "hsa", "memory_allocation", "rocdecode_api"),
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)
+124 -5
Ver Arquivo
@@ -397,6 +397,23 @@ struct rccl_api_callback_record_t
}
};
struct rocdecode_api_callback_record_t
{
uint64_t timestamp = 0;
rocprofiler_callback_tracing_record_t record = {};
rocprofiler_callback_tracing_rocdecode_api_data_t payload = {};
callback_arg_array_t args = {};
template <typename ArchiveT>
void save(ArchiveT& ar) const
{
ar(cereal::make_nvp("timestamp", timestamp));
cereal::save(ar, record);
ar(cereal::make_nvp("payload", payload));
serialize_args(ar, args);
}
};
struct ompt_callback_record_t
{
uint64_t timestamp = 0;
@@ -555,6 +572,7 @@ auto kernel_dispatch_cb_records = std::deque<kernel_dispatch_callback_record_
auto memory_copy_cb_records = std::deque<memory_copy_callback_record_t>{};
auto memory_allocation_cb_records = std::deque<memory_allocation_callback_record_t>{};
auto rccl_api_cb_records = std::deque<rccl_api_callback_record_t>{};
auto rocdecode_api_cb_records = std::deque<rocdecode_api_callback_record_t>{};
auto ompt_cb_records = std::deque<ompt_callback_record_t>{};
int
@@ -824,6 +842,20 @@ tool_tracing_callback(rocprofiler_callback_tracing_record_t record,
runtime_init_cb_records.emplace_back(
runtime_init_callback_record_t{ts, record, *data, std::move(args)});
}
else if(record.kind == ROCPROFILER_CALLBACK_TRACING_ROCDECODE_API)
{
auto* data =
static_cast<rocprofiler_callback_tracing_rocdecode_api_data_t*>(record.payload);
auto args = callback_arg_array_t{};
if(record.phase == ROCPROFILER_CALLBACK_PHASE_EXIT)
rocprofiler_iterate_callback_tracing_kind_operation_args(
record, save_args, record.phase, &args);
static auto _mutex = std::mutex{};
auto _lk = std::unique_lock<std::mutex>{_mutex};
rocdecode_api_cb_records.emplace_back(
rocdecode_api_callback_record_t{ts, record, *data, std::move(args)});
}
else
{
throw std::runtime_error{"unsupported callback kind"};
@@ -843,8 +875,9 @@ auto scratch_memory_records = std::deque<rocprofiler_buffer_tracing_scratch_memo
auto page_migration_records = std::deque<rocprofiler_buffer_tracing_page_migration_record_t>{};
auto corr_id_retire_records =
std::deque<rocprofiler_buffer_tracing_correlation_id_retirement_record_t>{};
auto rccl_api_bf_records = std::deque<rocprofiler_buffer_tracing_rccl_api_record_t>{};
auto ompt_bf_records = std::deque<rocprofiler_buffer_tracing_ompt_record_t>{};
auto rccl_api_bf_records = std::deque<rocprofiler_buffer_tracing_rccl_api_record_t>{};
auto rocdecode_api_bf_records = std::deque<rocprofiler_buffer_tracing_rocdecode_api_record_t>{};
auto ompt_bf_records = std::deque<rocprofiler_buffer_tracing_ompt_record_t>{};
void
tool_tracing_buffered(rocprofiler_context_id_t /*context*/,
@@ -971,6 +1004,13 @@ tool_tracing_buffered(rocprofiler_context_id_t /*context*/,
runtime_init_bf_records.emplace_back(*record);
}
else if(header->kind == ROCPROFILER_BUFFER_TRACING_ROCDECODE_API)
{
auto* record = static_cast<rocprofiler_buffer_tracing_rocdecode_api_record_t*>(
header->payload);
rocdecode_api_bf_records.emplace_back(*record);
}
else
{
throw std::runtime_error{
@@ -1069,6 +1109,9 @@ rocprofiler_context_id_t kernel_dispatch_buffered_ctx = {0};
rocprofiler_context_id_t page_migration_ctx = {0};
rocprofiler_context_id_t runtime_init_callback_ctx = {};
rocprofiler_context_id_t runtime_init_buffered_ctx = {};
rocprofiler_context_id_t rocdecode_api_callback_ctx = {0};
rocprofiler_context_id_t rocdecode_api_buffered_ctx = {0};
// buffers
rocprofiler_buffer_id_t runtime_init_buffered_buffer = {};
rocprofiler_buffer_id_t hsa_api_buffered_buffer = {};
@@ -1082,6 +1125,7 @@ rocprofiler_buffer_id_t counter_collection_buffer = {};
rocprofiler_buffer_id_t scratch_memory_buffer = {};
rocprofiler_buffer_id_t corr_id_retire_buffer = {};
rocprofiler_buffer_id_t rccl_api_buffered_buffer = {};
rocprofiler_buffer_id_t rocdecode_api_buffer = {};
rocprofiler_buffer_id_t ompt_buffered_buffer = {};
auto contexts = std::unordered_map<std::string_view, rocprofiler_context_id_t*>{
@@ -1107,10 +1151,12 @@ auto contexts = std::unordered_map<std::string_view, rocprofiler_context_id_t*>{
{"SCRATCH_MEMORY", &scratch_memory_ctx},
{"CORRELATION_ID_RETIREMENT", &corr_id_retire_ctx},
{"RCCL_API_BUFFERED", &rccl_api_buffered_ctx},
{"ROCDECODE_API_CALLBACK", &rocdecode_api_callback_ctx},
{"ROCDECODE_API_BUFFERED", &rocdecode_api_buffered_ctx},
{"OMPT_BUFFERED", &ompt_buffered_ctx},
};
auto buffers = std::array<rocprofiler_buffer_id_t*, 13>{&runtime_init_buffered_buffer,
auto buffers = std::array<rocprofiler_buffer_id_t*, 14>{&runtime_init_buffered_buffer,
&hsa_api_buffered_buffer,
&hip_api_buffered_buffer,
&marker_api_buffered_buffer,
@@ -1122,7 +1168,8 @@ auto buffers = std::array<rocprofiler_buffer_id_t*, 13>{&runtime_init_buffered_b
&counter_collection_buffer,
&corr_id_retire_buffer,
&rccl_api_buffered_buffer,
&ompt_buffered_buffer};
&ompt_buffered_buffer,
&rocdecode_api_buffer};
auto agents = std::vector<rocprofiler_agent_t>{};
auto agents_map = std::unordered_map<rocprofiler_agent_id_t, rocprofiler_agent_t>{};
@@ -1288,6 +1335,15 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data)
nullptr),
"rccl api callback tracing service configure");
ROCPROFILER_CALL(
rocprofiler_configure_callback_tracing_service(rocdecode_api_callback_ctx,
ROCPROFILER_CALLBACK_TRACING_ROCDECODE_API,
nullptr,
0,
tool_tracing_callback,
nullptr),
"rocdecode api callback tracing service configure");
ROCPROFILER_CALL(
rocprofiler_configure_callback_tracing_service(ompt_callback_ctx,
ROCPROFILER_CALLBACK_TRACING_OMPT,
@@ -1408,6 +1464,15 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data)
&rccl_api_buffered_buffer),
"buffer creation");
ROCPROFILER_CALL(rocprofiler_create_buffer(rocdecode_api_buffered_ctx,
buffer_size,
watermark,
ROCPROFILER_BUFFER_POLICY_LOSSLESS,
tool_tracing_buffered,
tool_data,
&rocdecode_api_buffer),
"buffer creation");
ROCPROFILER_CALL(rocprofiler_create_buffer(ompt_buffered_ctx,
buffer_size,
watermark,
@@ -1532,6 +1597,14 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data)
rccl_api_buffered_buffer),
"buffer tracing service for rccl api configure");
ROCPROFILER_CALL(
rocprofiler_configure_buffer_tracing_service(rocdecode_api_buffered_ctx,
ROCPROFILER_BUFFER_TRACING_ROCDECODE_API,
nullptr,
0,
rocdecode_api_buffer),
"buffer tracing service for rocdecode api configure");
ROCPROFILER_CALL(
rocprofiler_configure_buffer_tracing_service(
ompt_buffered_ctx, ROCPROFILER_BUFFER_TRACING_OMPT, nullptr, 0, ompt_buffered_buffer),
@@ -1701,7 +1774,8 @@ tool_fini(void* tool_data)
<< ", rccl_api_bf_records=" << rccl_api_bf_records.size()
<< ", ompt_bf_records=" << ompt_bf_records.size()
<< ", counter_collection_value_records=" << counter_collection_bf_records.size()
<< "...\n"
<< ", rocdecode_api_callback_records=" << rocdecode_api_cb_records.size()
<< ", rocdecode_api_bf_records=" << rocdecode_api_bf_records.size() << "...\n"
<< std::flush;
auto* _call_stack = static_cast<call_stack_t*>(tool_data);
@@ -1797,6 +1871,7 @@ write_json(call_stack_t* _call_stack)
json_ar(cereal::make_nvp("kernel_dispatch", kernel_dispatch_cb_records));
json_ar(cereal::make_nvp("memory_copies", memory_copy_cb_records));
json_ar(cereal::make_nvp("memory_allocations", memory_allocation_cb_records));
json_ar(cereal::make_nvp("rocdecode_api_traces", rocdecode_api_cb_records));
} catch(std::exception& e)
{
std::cerr << "[" << getpid() << "][" << __FUNCTION__
@@ -1823,6 +1898,7 @@ write_json(call_stack_t* _call_stack)
json_ar(cereal::make_nvp("ompt_traces", ompt_bf_records));
json_ar(cereal::make_nvp("retired_correlation_ids", corr_id_retire_records));
json_ar(cereal::make_nvp("counter_collection", counter_collection_bf_records));
json_ar(cereal::make_nvp("rocdecode_api_traces", rocdecode_api_bf_records));
} catch(std::exception& e)
{
std::cerr << "[" << getpid() << "][" << __FUNCTION__
@@ -1894,6 +1970,8 @@ write_perfetto()
tids.emplace(itr.thread_id);
for(auto itr : ompt_bf_records)
tids.emplace(itr.thread_id);
for(auto itr : rocdecode_api_bf_records)
tids.emplace(itr.thread_id);
for(auto itr : memory_copy_bf_records)
{
@@ -2147,6 +2225,47 @@ write_perfetto()
itr.end_timestamp);
}
for(auto itr : rocdecode_api_bf_records)
{
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = thread_tracks.at(itr.thread_id);
auto _args = callback_arg_array_t{};
auto ritr = std::find_if(
rocdecode_api_cb_records.begin(),
rocdecode_api_cb_records.end(),
[&itr](const auto& citr) {
return (citr.record.correlation_id.internal == itr.correlation_id.internal &&
!citr.args.empty());
});
if(ritr != rocdecode_api_cb_records.end()) _args = ritr->args;
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::rocdecode_api>::name,
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
"begin_ns",
itr.start_timestamp,
"tid",
itr.thread_id,
"kind",
itr.kind,
"operation",
itr.operation,
"corr_id",
itr.correlation_id.internal,
[&](::perfetto::EventContext ctx) {
for(const auto& aitr : _args)
sdk::add_perfetto_annotation(ctx, aitr.first, aitr.second);
});
TRACE_EVENT_END(sdk::perfetto_category<sdk::category::rocdecode_api>::name,
track,
itr.end_timestamp,
"end_ns",
itr.end_timestamp);
}
for(auto itr : ompt_bf_records)
{
auto name = buffer_names.at(itr.kind, itr.operation);