Skip to content

Make memory hooks work with static libcudart, by using bistro hooks for cuda driver API - #6038

Merged
yosefe merged 2 commits into
openucx:masterfrom
yosefe:topic/cuda-bistro-hooks
Jan 4, 2021
Merged

yosefe merged 2 commits into
openucx:masterfrom
yosefe:topic/cuda-bistro-hooks

Conversation

@yosefe

@yosefe yosefe commented Dec 16, 2020 •

Copy link
Copy Markdown
Member

Why

Support Cuda memory allocate/release hooks also for applications which are linked to libcudart statically, or use directly the driver API.
Fixed #3210

How

Use binary instrumentation (bistro) hooks for Cuda Driver APIs, which work regardless of how a function is called (from .plt or by function pointer).

  • Before this PR, if an application was statically linked to libcudart, we could not install hooks on Cuda memory events: cuda runtime calls (e.g cudaFree) are not going through .plt, and libcudart is calling driver API (e.g cuMemFree) by function pointer.
  • After this PR, the calls from cudart to cuda driver API can be intercepter.

If any other library is intercepting the same function by .plt hooks, it can work alongside with bistro hooks. However, if the other library is also using binary patching, in will probably be incompatible with the UCX hooks.

Binary instrumentation hook method:

  1. Copy minimal number of bytes of target function prologue to a newly allocated buffer with execute permissions.
  2. After these code bytes, place an indirect jump instruction to the rest of the target function code.

    If there are not enough position independent instructions, such as push <reg> or sub <imm>, <reg>, to accommodate
    for the indirect jump (3), this method will fail.

  3. Place an indirect jump in the target function start to the UCM replacement
  4. UCM replacement will treat the executable buffer (1) as the "original" function which should be used from the replacement (2)
memory allocate/release
in the application
      +
      |
      |       target function
      |       (e.g cuMemAlloc)               UCM replacement
      +----->-----------------+              (e.g ucm_cuMemAlloc)
             |jmp to UCM hook +-------------->+-----------+
             |                | (3)           | dispatch  +-------------> [ rcache ]
             |                |               | UCM event |
             +----------------+               |           |
             | unmodified     +<---+          +-----------+
             | code of target |    |          | call orig | (4)
             | function       |    |      +---+ function  |
             |                |    |      |   +-----------+
             |                |    |      |
             |                |    |      |   executable buffer
             |                |    |      |   (e.g ucm_orig_cuMemAlloc)
             |                |    |      +-->+-----------+
             |                |    |          | copy of   | (1)
             |                |    |          | function  |
             |                |    |          | prologue  |
             |                |    |          +-----------+
             |                |    +----------+ jump back | (2)
             +----------------+               +-----------+

@yosefe yosefe added the WIP-DNM Work in progress / Do not review label Dec 16, 2020
}

/* check the opcode */
if (((rex == 0) || rex == 0x41) && ((opcode & 0xF0) == 0x50)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

((rex == 0) || rex == 0x41) is this correct?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rex == 0 is pushing IA32 register and rex==0x41 is pushing rN register

}
}

/* unsupported instruction - bail */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bail? not fail?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, "bail" means exit

Comment thread src/ucm/bistro/bistro_x86_64.c Outdated

pthread_mutex_lock(&mutex);

if (page == MAP_FAILED) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this condition is always true

Comment thread src/ucm/bistro/bistro_x86_64.c Outdated
}
}

if (size + offset > ucm_get_page_size()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offset is 0 here... and wrap size + offset by ()

Comment thread src/ucm/bistro/bistro_x86_64.c Outdated

/* allocate space in the page */
result = UCS_PTR_BYTE_OFFSET(page, offset);
offset += size;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems body of this function was in loop :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgot to add 'static'

@yosefe
yosefe force-pushed the topic/cuda-bistro-hooks branch from 76d2862 to ee105b1 Compare December 16, 2020 22:05
Comment thread src/ucm/bistro/bistro.h Outdated
* Set library function call hook using Binary Instrumentation
* method (BISTRO): replace function body by user defined call
*
* @param symbol function name to replace

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing @param func_ptr

Comment thread src/ucm/cuda/cudamem.c Outdated
ucm_event_enter();

ucm_trace("ucm_cuMemFree(dptr=%p)",(void*)dptr);
ucm_diag("ucm_cuMemFree(dptr=%p)",(void*)dptr);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after ,

Comment thread src/ucm/cuda/cudamem.c Outdated

ucm_event_enter();

ucm_diag("ucm_cuMemFree_v2(dptr=%p)",(void*)dptr);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after ,

Comment thread src/ucm/cuda/cudamem.c Outdated
ret = ucm_orig_cuMemAlloc(dptr, size);
if (ret == CUDA_SUCCESS) {
ucm_trace("ucm_cuMemAlloc(dptr=%p size:%lu)",(void *)*dptr, size);
ucm_diag("ucm_cuMemAlloc(dptr=%p size:%lu)",(void *)*dptr, size);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would not diag be too flooding?

Comment thread src/ucm/cuda/cudamem.c Outdated
ret = ucm_orig_cuMemAlloc(dptr, size);
if (ret == CUDA_SUCCESS) {
ucm_trace("ucm_cuMemAlloc(dptr=%p size:%lu)",(void *)*dptr, size);
ucm_diag("ucm_cuMemAlloc(dptr=%p size:%lu)",(void *)*dptr, size);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after ,

Comment thread src/ucm/cuda/cudamem.c Outdated

ret = ucm_orig_cuMemAlloc_v2(dptr, size);
if (ret == CUDA_SUCCESS) {
ucm_diag("ucm_cuMemAlloc_v2(dptr=%p size:%lu)",(void *)*dptr, size);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after ,

Comment thread src/ucm/cuda/cudamem.c Outdated
ElementSizeBytes);
if (ret == CUDA_SUCCESS) {
ucm_trace("ucm_cuMemHostGetDevicePointer(pdptr=%p p=%p)",(void *)*pdptr, p);
ucm_trace("ucm_cuMemAllocPitch_v2(dptr=%p size:%lu)",(void *)*dptr,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space after ,

Comment thread src/ucm/cuda/cudamem.c Outdated
continue;
}

ucm_debug("failed to install bistro hook for '%s', fallback to reloc",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better print status here than in previous ucs_debug, where it is always OK

@yosefe
yosefe force-pushed the topic/cuda-bistro-hooks branch from ee105b1 to 79c12b7 Compare December 19, 2020 12:16
@yosefe yosefe removed the WIP-DNM Work in progress / Do not review label Dec 19, 2020
@yosefe
yosefe force-pushed the topic/cuda-bistro-hooks branch from 79c12b7 to d87b139 Compare December 19, 2020 12:47
@yosefe
yosefe force-pushed the topic/cuda-bistro-hooks branch 3 times, most recently from 8595659 to 1b69f10 Compare December 28, 2020 11:19
@yosefe

yosefe commented Dec 28, 2020

Copy link
Copy Markdown
Member Author

bot:pipe:retest

@yosefe

yosefe commented Dec 29, 2020

Copy link
Copy Markdown
Member Author

/azp run

@yosefe

yosefe commented Dec 29, 2020

Copy link
Copy Markdown
Member Author

bot:pipe:retest

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@yosefe

yosefe commented Dec 30, 2020

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@yosefe
yosefe force-pushed the topic/cuda-bistro-hooks branch from 1b69f10 to 80693f4 Compare December 30, 2020 12:18
@yosefe

yosefe commented Dec 30, 2020

Copy link
Copy Markdown
Member Author

@Akshay-Venkatesh @bureddy can you pls take a look?

Comment thread src/ucm/bistro/bistro_aarch64.h Outdated
* optional, may be NULL
* @param func_ptr Pointer to function to patch.
* @param hook User-defined function-replacer.
* @param symbol Function name to replace.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we we add @PARAM orig_func_p documentation here?

Comment thread src/ucm/cuda/cudamem.c Outdated
UCM_CUDA_FREE_FUNC(cuMemFree, CUresult, CUdeviceptr)
UCM_CUDA_FREE_FUNC(cuMemFree_v2, CUresult, CUdeviceptr)
UCM_CUDA_FREE_FUNC(cuMemFreeHost, CUresult, void*)
UCM_CUDA_FREE_FUNC(cuMemFreeHost_v2, CUresult, void*)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cuMemFreeHost is also dispatching UCS_MEMORY_TYPE_CUDA memtype free event. it is not an issue today as we don't care if it not find in the mem type cache. but, should we differentiate cuMemFree and cuMemFreeHost?

Comment thread src/ucm/cuda/cudamem.c Outdated
if (status == UCS_OK) {
ucm_debug("installed cuda memory bistro hooks on driver API");
ucm_cudamem_installed = 1;
} else {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also have to install reloc for driver funcs if bistro fails. application might be using driver API directly.
can we update test_cuda_hook.c with driver api test as well?

@yosefe

yosefe commented Jan 1, 2021

Copy link
Copy Markdown
Member Author

@bureddy fixed

@Akshay-Venkatesh

Copy link
Copy Markdown
Contributor

@Akshay-Venkatesh @bureddy can you pls take a look?

Some high-level questions/requests:

  • Can you please explain how/why this solves the static linking issue? "How" section in the PR seems to describe the low-level mechanism of binary instrumentation. Specifying current limitations with cudart static linking (such as use of function pointers in cudart), how current hooks in ucm/cuda fails to intercept when cudart static is used, and how this PR addresses issues could be useful for future reference.
  • Should we document any limitations with current PR (such as memory allocated before ucp_init)?
  • Does this PR address the case when there are other libraries intercept cu* functions?

@yosefe yosefe changed the title UCM/CUDA: Add bistro hooks for Cuda Make memory hooks work with static libcudart, by using bistro hooks for cuda driver API Jan 2, 2021
@yosefe

yosefe commented Jan 2, 2021

Copy link
Copy Markdown
Member Author
  • Can you please explain how/why this solves the static linking issue? "How" section in the PR seems to describe the low-level mechanism of binary instrumentation. Specifying current limitations with cudart static linking (such as use of function pointers in cudart), how current hooks in ucm/cuda fails to intercept when cudart static is used, and how this PR addresses issues could be useful for future reference.

Added

  • Should we document any limitations with current PR (such as memory allocated before ucp_init)?

Memory before ucp_init is not a problem: we set suspicious regions to UNKNOWN and force detection during runtime

  • Does this PR address the case when there are other libraries intercept cu* functions?

Added note: if other library is hooking .plt it would work, if using bistro - no

Comment thread src/ucm/cuda/cudamem.c Outdated
return ret;
event.mem_type.address = (void*)ptr;
event.mem_type.size = length;
event.mem_type.mem_type = UCS_MEMORY_TYPE_CUDA;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be "event.mem_type.mem_type = mem_type" ?

Comment thread src/ucm/cuda/cudamem.c Outdated
cudaError_t ucm_cudaHostGetDevicePointer(void **pDevice, void *pHost, unsigned int flags)
{
cudaError_t ret;
if (ucm_mmap_hook_mode() == UCM_MMAP_HOOK_BISTRO) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yosefe should we detach cuda hooks from ucm_mmap_hook_mode() flag? may be we have a case where we need to to disable malloc bistro hooks but not cuda hooks ( for ex: on summit with spectrum mpi).
how about controlling cuda hooks with new flag "ucm_cuda_hook_mode: and "remove ucm_global_opts.enable_cuda_reloc" ?

Comment thread src/ucm/cuda/cudamem.c Outdated

if (!ucm_global_opts.enable_cuda_reloc) {
if (ucm_cuda_hook_mode() == UCM_MMAP_HOOK_NONE) {
ucm_debug("cudamem relocations are disabled by configuration");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/relocations/hooks?

Comment thread src/ucs/config/ucm_opts.c Outdated
" reloc - Use ELF relocation table to set hooks.\n"
#if UCM_BISTRO_HOOKS
" bistro - Use binary instrumentation to set hooks. In this mode, calls to\n"
" statically a statically Cuda runtime can be hooked on the the\n"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems wording is not clear

Comment thread src/ucm/cuda/cudamem.c Outdated
status = ucm_cuda_install_hooks(ucm_cuda_runtime_funcs, &used_reloc);
if (status == UCS_OK) {
ucm_debug("installed cuda memory %s hooks on runtime API",
used_reloc ? "reloc" : "bistro");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems "ucm_cuda_install_hooks" can do bistro for some funcs if it is possible and reloc for others. is this debug msg accurate?

yosefe added 2 commits January 4, 2021 10:25
In order to catch cuda memory events even when Cuda is statically
linked, use bistro.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UCX memory hooks with static linking

5 participants