/* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of NVIDIA CORPORATION nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include /* every tool needs to include this once */ #include "nvbit_tool.h" /* nvbit interface file */ #include "nvbit.h" /* global control variables for this tool */ uint32_t instr_begin_interval = 0; uint32_t instr_end_interval = UINT32_MAX; int verbose = 0; /* opcode to id map */ std::map opcode_to_id_map; void nvbit_at_init() { GET_VAR_INT( instr_begin_interval, "INSTR_BEGIN", 0, "Beginning of the instruction interval where to apply instrumentation"); GET_VAR_INT( instr_end_interval, "INSTR_END", UINT32_MAX, "End of the instruction interval where to apply instrumentation"); GET_VAR_INT(verbose, "TOOL_VERBOSE", 0, "Enable verbosity inside the tool"); std::string pad(100, '-'); printf("%s\n", pad.c_str()); } /* Set used to avoid re-instrumenting the same functions multiple times */ std::unordered_set already_instrumented; void instrument_function_if_needed(CUcontext ctx, CUfunction func) { /* Get related functions of the kernel (device function that can be * called by the kernel) */ std::vector related_functions = nvbit_get_related_functions(ctx, func); /* add kernel itself to the related function vector */ related_functions.push_back(func); /* iterate on function */ for (auto f : related_functions) { /* "recording" function was instrumented, if set insertion failed * we have already encountered this function */ if (!already_instrumented.insert(f).second) { continue; } const std::vector &instrs = nvbit_get_instrs(ctx, f); if (verbose) { printf("Inspecting function %s at address 0x%lx\n", nvbit_get_func_name(ctx, f), nvbit_get_func_addr(f)); } /* iterate on all the static instructions in the function */ for (auto instr : instrs) { if (instr->getIdx() < instr_begin_interval || instr->getIdx() >= instr_end_interval || instr->getMemorySpace() == InstrType::MemorySpace::NONE || instr->getMemorySpace() == InstrType::MemorySpace::CONSTANT) { continue; } if (verbose) { instr->print(); } if (opcode_to_id_map.find(instr->getOpcode()) == opcode_to_id_map.end()) { int opcode_id = opcode_to_id_map.size(); opcode_to_id_map[instr->getOpcode()] = opcode_id; printf("OPCODE %s MAPS TO ID %d\n", instr->getOpcode(), opcode_id); } int opcode_id = opcode_to_id_map[instr->getOpcode()]; int mref_idx = 0; /* iterate on the operands */ for (int i = 0; i < instr->getNumOperands(); i++) { /* get the operand "i" */ const InstrType::operand_t *op = instr->getOperand(i); if (op->type == InstrType::OperandType::MREF) { /* insert call to the instrumentation function with its * arguments */ nvbit_insert_call(instr, "instrument_mem", IPOINT_BEFORE); /* predicate value */ nvbit_add_call_arg_guard_pred_val(instr); /* opcode id */ nvbit_add_call_arg_const_val32(instr, opcode_id); /* memory reference 64 bit address */ nvbit_add_call_arg_mref_addr64(instr, mref_idx); mref_idx++; } } } } } /* This call-back is triggered every time a CUDA driver call is encountered. * Here we can look for a particular CUDA driver call by checking at the * call back ids which are defined in tools_cuda_api_meta.h. * This call back is triggered bith at entry and at exit of each CUDA driver * call, is_exit=0 is entry, is_exit=1 is exit. * */ void nvbit_at_cuda_event(CUcontext ctx, int is_exit, nvbit_api_cuda_t cbid, const char *name, void *params, CUresult *pStatus) { /* Identify all the possible CUDA launch events */ if (cbid == API_CUDA_cuLaunch || cbid == API_CUDA_cuLaunchKernel_ptsz || cbid == API_CUDA_cuLaunchGrid || cbid == API_CUDA_cuLaunchGridAsync || cbid == API_CUDA_cuLaunchKernel) { /* cast params to cuLaunch_params since if we are here we know these are * the right parameters type */ cuLaunch_params *p = (cuLaunch_params *)params; if (!is_exit) { instrument_function_if_needed(ctx, p->f); nvbit_enable_instrumented(ctx, p->f, true); } } }