/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #include "cl_common.hpp" #include "cl_thread_trace_amd.h" #include "platform/context.hpp" #include "platform/command.hpp" #include "platform/threadtrace.hpp" #include #include #include /*! \addtogroup API * @{ * * \addtogroup AMD_Extensions * @{ * */ /*! \brief Creates a new HW threadTrace * * \param device must be a valid OpenCL device. * * \param threadTrace the created cl_threadtrace_amd object * * \param errcode_ret A non zero value if OpenCL failed to create cl_threadtrace_amd * - CL_SUCCESS if the function is executed successfully. * - CL_INVALID_DEVICE if the specified context is invalid. * - CL_INVALID_OPERATION if we couldn't create the object * * \return Created cl_threadtrace_amd object */ RUNTIME_ENTRY_RET(cl_threadtrace_amd, clCreateThreadTraceAMD, (cl_device_id device, cl_int* errcode_ret)) { // Make sure we have a valid device object if (!is_valid(device)) { *not_null(errcode_ret) = CL_INVALID_DEVICE; return NULL; } // Create the device thread trace object amd::ThreadTrace* threadTrace = new amd::ThreadTrace(*as_amd(device)); if (threadTrace == NULL) { *not_null(errcode_ret) = CL_INVALID_OPERATION; return NULL; } *not_null(errcode_ret) = CL_SUCCESS; return as_cl(threadTrace); } RUNTIME_EXIT ///*! \brief Destroy a threadTrace object. // * // * \param threadTrace the cl_threadtrace_amd object for release // * // * \return A non zero value if OpenCL failed to release cl_threadtrace_amd // * - CL_SUCCESS if the function is executed successfully. // * - CL_INVALID_OPERATION if we failed to release the object // */ RUNTIME_ENTRY(cl_int, clReleaseThreadTraceAMD, (cl_threadtrace_amd threadTrace)) { if (!is_valid(threadTrace)) { return CL_INVALID_OPERATION; } as_amd(threadTrace)->release(); return CL_SUCCESS; } RUNTIME_EXIT // // *! \brief Increments the cl_threadtrace_amd object reference count. // * // * \param threadTrace the cl_threadtrace_amd object for retain // * // * \return A non zero value if OpenCL failed to retain cl_threadtrace_amd // * - CL_SUCCESS if the function is executed successfully. // * - CL_INVALID_OPERATION if we failed to release the object // */ RUNTIME_ENTRY(cl_int, clRetainThreadTraceAMD, (cl_threadtrace_amd threadTrace)) { if (!is_valid(threadTrace)) { return CL_INVALID_OPERATION; } as_amd(threadTrace)->retain(); return CL_SUCCESS; } RUNTIME_EXIT // // *! \brief Sets the cl_threadtrace_amd object configuration parameter. // * // * \param thread_trace the cl_threadtrace_amd object to set configuration parameter // * // * \param config_param the cl_thread_trace_param // * // * \param param_value corresponding to configParam // * // * \return A non zero value if OpenCL failed to set threadTrace buffer parameter // * - CL_INVALID_VALUE if the thread_trace is invalid thread trace object. // * - CL_INVALID_VALUE if the invalid config_param or param_value enum values , are used. // * - CL_INVALID_EVENT_WAIT_LIST if event_wait_list is NULL and num_events_in_wait_list > 0, or // event_wait_list is not NULL and num_events_in_wait_list is 0, // * - or if event objects in event_wait_list are not valid events. // * - CL_OUT_OF_RESOURCES if there is a failure to allocate resources required by the OpenCL // implementation on the device. // * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the // OpenCL implementation on the host. // */ RUNTIME_ENTRY(cl_int, clSetThreadTraceParamAMD, (cl_threadtrace_amd thread_trace, cl_thread_trace_param config_param, cl_uint param_value)) { if (!is_valid(thread_trace)) { return CL_INVALID_OPERATION; } switch (config_param) { case CL_THREAD_TRACE_PARAM_TOKEN_MASK: if (param_value > CL_THREAD_TRACE_TOKEN_MASK_ALL_SI) { return CL_INVALID_VALUE; } as_amd(thread_trace)->setTokenMask(param_value); break; case CL_THREAD_TRACE_PARAM_REG_MASK: if (param_value > CL_THREAD_TRACE_REG_MASK_ALL_SI) { return CL_INVALID_VALUE; } as_amd(thread_trace)->setRegMask(param_value); break; case CL_THREAD_TRACE_PARAM_VM_ID_MASK: if (param_value > CL_THREAD_TRACE_VM_ID_MASK_SINGLE_DETAIL) { return CL_INVALID_VALUE; } as_amd(thread_trace)->setVmIdMask(param_value); break; case CL_THREAD_TRACE_PARAM_INSTRUCTION_MASK: if (param_value > CL_THREAD_TRACE_INST_MASK_IMMEDIATE_CI) { return CL_INVALID_VALUE; } as_amd(thread_trace)->setInstMask(param_value); break; case CL_THREAD_TRACE_PARAM_COMPUTE_UNIT_TARGET: as_amd(thread_trace)->setCU(param_value); break; case CL_THREAD_TRACE_PARAM_SHADER_ARRAY_TARGET: as_amd(thread_trace)->setSH(param_value); break; case CL_THREAD_TRACE_PARAM_SIMD_MASK: as_amd(thread_trace)->setSIMD(param_value); break; case CL_THREAD_TRACE_PARAM_USER_DATA: as_amd(thread_trace)->setUserData(param_value); break; case CL_THREAD_TRACE_PARAM_CAPTURE_MODE: if (param_value > CL_THREAD_TRACE_CAPTURE_SELECT_DETAIL) { return CL_INVALID_VALUE; } as_amd(thread_trace)->setCaptureMode(param_value); break; case CL_THREAD_TRACE_PARAM_IS_WRAPPED: as_amd(thread_trace)->setIsWrapped(true); break; case CL_THREAD_TRACE_PARAM_RANDOM_SEED: as_amd(thread_trace)->setRandomSeed(param_value); break; } return CL_SUCCESS; } RUNTIME_EXIT /*! \brief Get specific information about the OpenCL Thread Trace. * * \param threadTrace_info_param is an enum that identifies the Thread Trace information being * queried. * * \param param_value is a pointer to memory location where appropriate values * for a given \a threadTrace_info_param will be returned. If \a param_value is NULL, * it is ignored. * * \param param_value_size specifies the size in bytes of memory pointed to by * \a param_value. This size in bytes must be >= size of return type. * * \param param_value_size_ret returns the actual size in bytes of data being * queried by param_value. If \a param_value_size_ret is NULL, it is ignored. * * \return One of the following values: * CL_INVALID_OPERATION if cl_threadtrace_amd object is not valid * - CL_INVALID_VALUE if \a param_name is not one of the supported * values or if size in bytes specified by \a param_value_size is < size of * return type and \a param_value is not a NULL value. * - CL_SUCCESS if the function is executed successfully. * */ RUNTIME_ENTRY(cl_int, clGetThreadTraceInfoAMD, (cl_threadtrace_amd thread_trace /* threadTrace */, cl_threadtrace_info thread_trace_info_param, size_t param_value_size, void* param_value, size_t* param_value_size_ret)) { if (!is_valid(thread_trace)) { return CL_INVALID_OPERATION; } // Find the thread trace object, associated with the specified device const device::ThreadTrace* devThreadTrace = as_amd(thread_trace)->getDeviceThreadTrace(); const size_t seNum = as_amd(thread_trace)->deviceSeNumThreadTrace(); switch (thread_trace_info_param) { case CL_THREAD_TRACE_SE: { return amd::clGetInfo(seNum, param_value_size, param_value, param_value_size_ret); } case CL_THREAD_TRACE_BUFFERS_SIZE: { // Make sure we found a valid thread trace object if (devThreadTrace == NULL) { return CL_INVALID_OPERATION; } std::unique_ptr bufSize2Se(new uint[seNum]); if (bufSize2Se.get() == NULL) { return CL_OUT_OF_HOST_MEMORY; } if (!devThreadTrace->info(thread_trace_info_param, bufSize2Se.get(), seNum)) { return CL_INVALID_VALUE; } const size_t valueSize = seNum * sizeof(unsigned int); if (param_value != NULL && param_value_size < valueSize) { return CL_INVALID_VALUE; } *not_null(param_value_size_ret) = valueSize; if (param_value != NULL) { ::memcpy(param_value, bufSize2Se.get(), valueSize); if (param_value_size > valueSize) { ::memset(static_cast
(param_value) + valueSize, '\0', param_value_size - valueSize); } } return CL_SUCCESS; } } return CL_SUCCESS; } RUNTIME_EXIT /* \brief Enqueues the command for the specified thread trace object. * * \param command_queue must be a valid OpenCL command queue. * * \param thread_trace specifies the cl_threadtrace_amd object. * * \param event_wait_list specify [is a pointer to] events that need to * complete before this particular command can be executed. * If \a event_wait_list is NULL, then this particular command does not wait * on any event to complete. If \a event_wait_list is NULL, * \a num_events_in_wait_list must be 0. If \a event_wait_list is not NULL, * the list of events pointed to by \a event_wait_list must be valid and * \a num_events_in_wait_list must be greater than 0. The events specified in * \a event_wait_list act as synchronization points. * * \param num_events_in_wait_list specify the number of events in * \a event_wait_list. It must be 0 if \a event_wait_list is NULL. It must be * greater than 0 if \a event_wait_list is not NULL. * * \param event returns an event object that identifies this particular * command and can be used to query or queue a wait for this particular * command to complete. \a event can be NULL in which case it will not be * possible for the application to query the status of this command or queue a * wait for this command to complete. * \return A non zero value if OpenCL failed to release threadTrace * - CL_INVALID_COMMAND_QUEUE if command_queue is not a valid command-queue. * - CL_INVALID_CONTEXT if the context associated with command_queue and events in event_wait_list * are not the same. * - CL_INVALID_VALUE if the thread_trace is invalid thread trace object . * - CL_INVALID_VALUE if the invalid command name enum value , not described in the * cl_threadtrace_command_name_amd, is used. * - CL_INVALID_OPERATION if the command enqueue failed. It can happen in the following cases: * o BEGIN_COMMAND is queued for thread trace object for which memory object/s was/were not * bound.. * o END_COMMAND is queued for thread trace object, for which BEGIN_COMMAND was not queued. * o PAUSE_COMMAND is queued for thread trace object, for which BEGIN_COMMAND was not * queued. * o RESUME_COMMAND is queued for thread trace object, for which PAUSE_COMMAND was not * queued. * - CL_INVALID_EVENT_WAIT_LIST if event_wait_list is NULL and num_events_in_wait_list > 0, or * event_wait_list is not NULL and num_events_in_wait_list is 0, or if event objects in * event_wait_list are not valid events. * - CL_OUT_OF_RESOURCES if there is a failure to allocate resources required by the OpenCL * implementation on the device. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the OpenCL * implementation on the host. */ RUNTIME_ENTRY(cl_int, clEnqueueThreadTraceCommandAMD, (cl_command_queue command_queue, cl_threadtrace_amd thread_trace, cl_threadtrace_command_name_amd command_name, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event)) { // Check if command queue is valid if (!is_valid(command_queue)) { return CL_INVALID_COMMAND_QUEUE; } // Check if thread trace is valid if (!is_valid(thread_trace)) { return CL_INVALID_OPERATION; } amd::ThreadTrace* amdThreadTrace = as_amd(thread_trace); amd::HostQueue* hostQueue = as_amd(command_queue)->asHostQueue(); if (NULL == hostQueue) { return CL_INVALID_COMMAND_QUEUE; } // Check that device associated with the command queue is the same as with thread trace if (&hostQueue->device() != &amdThreadTrace->device()) { return CL_INVALID_DEVICE; } amd::Command::EventWaitList eventWaitList; cl_int err = amd::clSetEventWaitList(eventWaitList, *hostQueue, num_events_in_wait_list, event_wait_list); if (err != CL_SUCCESS) { return err; } // Create a new command for the threadTraces amd::ThreadTraceCommand* command = NULL; switch (command_name) { case CL_THREAD_TRACE_BEGIN_COMMAND: if ((amdThreadTrace->getState() != amd::ThreadTrace::MemoryBound) && (amdThreadTrace->getState() != amd::ThreadTrace::End)) { return CL_INVALID_OPERATION; } amdThreadTrace->setState(amd::ThreadTrace::Begin); command = new amd::ThreadTraceCommand( *hostQueue, eventWaitList, static_cast(&amdThreadTrace->threadTraceConfig()), *amdThreadTrace, amd::ThreadTraceCommand::Begin, CL_COMMAND_THREAD_TRACE); break; case CL_THREAD_TRACE_END_COMMAND: if ((amdThreadTrace->getState() != amd::ThreadTrace::Begin) && (amdThreadTrace->getState() != amd::ThreadTrace::Pause)) { return CL_INVALID_OPERATION; } amdThreadTrace->setState(amd::ThreadTrace::End); command = new amd::ThreadTraceCommand(*hostQueue, eventWaitList, &amdThreadTrace->threadTraceConfig(), *amdThreadTrace, amd::ThreadTraceCommand::End, CL_COMMAND_THREAD_TRACE); break; case CL_THREAD_TRACE_PAUSE_COMMAND: if (amdThreadTrace->getState() != amd::ThreadTrace::Begin) { return CL_INVALID_OPERATION; } amdThreadTrace->setState(amd::ThreadTrace::Pause); command = new amd::ThreadTraceCommand( *hostQueue, eventWaitList, &amdThreadTrace->threadTraceConfig(), *amdThreadTrace, amd::ThreadTraceCommand::Pause, CL_COMMAND_THREAD_TRACE); break; case CL_THREAD_TRACE_RESUME_COMMAND: if (amdThreadTrace->getState() != amd::ThreadTrace::Pause) { return CL_INVALID_OPERATION; } amdThreadTrace->setState(amd::ThreadTrace::Begin); command = new amd::ThreadTraceCommand( *hostQueue, eventWaitList, &amdThreadTrace->threadTraceConfig(), *amdThreadTrace, amd::ThreadTraceCommand::Resume, CL_COMMAND_THREAD_TRACE); break; } if (command == NULL) { return CL_OUT_OF_HOST_MEMORY; } // Submit the command to the device command->enqueue(); *not_null(event) = as_cl(&command->event()); if (event == NULL) { command->release(); } return CL_SUCCESS; } RUNTIME_EXIT // ///*! \brief Enqueues the binding command to bind cl_threadtrace_amd to cl_mem object for trace /// recording.. // * // * \param command_queue must be a valid OpenCL command queue. // * // * \param thread_trace specifies the cl_threadtrace_amd object. // * // * \param mem_objects the cl_mem objects for trace recording // * // * \param mem_objects_num the number of cl_mem objects in the mem_objects // * // * \param buffer_size the size of each cl_mem object from mem_objects // * // * \param event_wait_list specify [is a pointer to] events that need to // * complete before this particular command can be executed. // * If \a event_wait_list is NULL, then this particular command does not wait // * on any event to complete. If \a event_wait_list is NULL, // * \a num_events_in_wait_list must be 0. If \a event_wait_list is not NULL, // * the list of events pointed to by \a event_wait_list must be valid and // * \a num_events_in_wait_list must be greater than 0. The events specified in // * \a event_wait_list act as synchronization points. // * // * \param num_events_in_wait_list specify the number of events in // * \a event_wait_list. It must be 0 if \a event_wait_list is NULL. It must be // * greater than 0 if \a event_wait_list is not NULL. // * // * \param event returns an event object that identifies this particular // * command and can be used to query or queue a wait for this particular // * command to complete. \a event can be NULL in which case it will not be // * possible for the application to query the status of this command or queue a // * wait for this command to complete. // * \return A non zero value if OpenCL failed to set threadTrace buffer parameter // * - CL_INVALID_COMMAND_QUEUE if command_queue is not a valid command-queue. // * - CL_INVALID_CONTEXT if the context associated with command_queue and events in // event_wait_list are not the same. // * - CL_INVALID_VALUE if the thread_trace is invalid thread trace object. // * - CL_INVALID_VALUE if the buffer_size is negative or zero. // * - CL_INVALID_VALUE if the sub_buffers_num I less than 1. // * - CL_INVALID_OPERATION if the mem_objects_num is not equal to the number of Shader Engines of // the [GPU] device. // * - CL_INVALID_MEM_OBJECT if one on memory objects in the mem_objects array is not a valid // memory object or memory_objects is NULL. // * - CL_MEM_OBJECT_ALLOCATION_FAILURE if there is a failure to allocate memory for the data store // associated from the memory objects of the mem_objects array. // * - CL_INVALID_EVENT_WAIT_LIST if event_wait_list is NULL and num_events_in_wait_list > 0, or // event_wait_list is not NULL and num_events_in_wait_list is 0, or if event objects in // event_wait_list are not valid events. // * - CL_OUT_OF_RESOURCES if there is a failure to allocate resources required by the OpenCL // implementation on the device. // * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the // * OpenCL implementation on the host. // */ RUNTIME_ENTRY(cl_int, clEnqueueBindThreadTraceBufferAMD, (cl_command_queue command_queue, cl_threadtrace_amd thread_trace, cl_mem* mem_objects, cl_uint mem_objects_num, cl_uint buffer_size, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event)) { // Check if command queue is valid if (!is_valid(command_queue)) { return CL_INVALID_COMMAND_QUEUE; } // Check if thread trace is valid if (!is_valid(thread_trace)) { return CL_INVALID_OPERATION; } // Check if input values are valid if ((mem_objects == NULL) || (buffer_size <= 0)) { return CL_INVALID_VALUE; } amd::ThreadTrace* amdThreadTrace = as_amd(thread_trace); // Check if the number of bound memory objects is the same as the number of SEs if (amdThreadTrace->deviceSeNumThreadTrace() != mem_objects_num) { return CL_INVALID_OPERATION; } // Check if memory objects ,bound the thread trace,are valid for (size_t i = 0; i < mem_objects_num; ++i) { cl_mem obj = mem_objects[i]; if (!is_valid(obj)) { return CL_INVALID_MEM_OBJECT; } } amd::HostQueue* hostQueue = as_amd(command_queue)->asHostQueue(); if (NULL == hostQueue) { return CL_INVALID_COMMAND_QUEUE; } // Check that device associated with the command queue is the same as with thread trace if (&hostQueue->device() != &amdThreadTrace->device()) { return CL_INVALID_DEVICE; } amd::Command::EventWaitList eventWaitList; cl_int err = amd::clSetEventWaitList(eventWaitList, *hostQueue, num_events_in_wait_list, event_wait_list); if (err != CL_SUCCESS) { return err; } amdThreadTrace->setState(amd::ThreadTrace::MemoryBound); // Create a new ThreadTraceMemObjectsCommand command amd::ThreadTraceMemObjectsCommand* command = new amd::ThreadTraceMemObjectsCommand( *hostQueue, eventWaitList, mem_objects_num, mem_objects, buffer_size, *amdThreadTrace, CL_COMMAND_THREAD_TRACE_MEM); if (command == NULL) { return CL_OUT_OF_HOST_MEMORY; } // Make sure we have memory for the command execution if (!command->validateMemory()) { delete command; return CL_OUT_OF_RESOURCES; } // Submit the command to the device command->enqueue(); *not_null(event) = as_cl(&command->event()); if (event == NULL) { command->release(); } return CL_SUCCESS; } RUNTIME_EXIT /*! @} * @} */