Hi,
You can check the following snippet:
import time
import torch
from torch.autograd import Variable, Function, gradcheck
from torch import nn


current = 0
def hook_fn(msg):
    def tmp(*args):
        global current
        new = time.time()
        diff = new - current
        current = new
        print("Hello {} at {}".format(msg, diff))
    return tmp

a = Variable(torch.rand(10, 10000), requires_grad=True)
fn = nn.Linear(10000, 50000)

b = fn(a)

a.register_hook(hook_fn("var a"))
b.grad_fn.register_hook(hook_fn("grad fn"))
b.register_hook(hook_fn("var b"))

current = time.time()
b.sum().backward()

As you can see with the runtimes,
Basically, the hook on b is one of the hooks executed just before the linear function backward is called. And the one on b.grad_fn is one of the hooks executed just after the linear function backward is executed. And the one in a is also a hook just after the linear function backward. You can change the size of the linear output and see which part of the timing change.
tl:dr: You can access pre_hook/post_hook with the variable register_hook and variable.grad_fn register_hook.