I think the idea of depth is not well defined in this question as stated. But I'll assume that what is wanted is some sort of dependency-based depth that targets only certain symbols (which would be h, g, f in the given example). We can inspect the DownValues for a given symbol and extract the symbols it depends on. If we do this recursively, we can build a dependency graph. Once we have that, we can do the normal graph operations, including GraphDistance (which would be analogous to depth according to this definition that I'm working with).
We'll build up to the whole dependency graph in stages. First, it's easy to get primary degree dependencies. From these, we'll create "links", i.e. rules that indicate directional dependency. Once we have links, we can recursively add new links by finding the next degree of dependencies. We'll continue until nothing new is discovered. At that point, we'll have links that we can use as edges in a graph.
Someone with more experience with inspecting DownValues and carefully managing execution could probably clean this up, but here's what I've got:
Dependencies[symbolSpace : {___String}][symbol : (_Symbol | _String)] :=
Block[
{$hold},
SetAttributes[$hold, HoldAllComplete];
Intersection[
symbolSpace,
DeleteCases[
Cases[
MapAt[$hold, DownValues[symbol], {All, 2}][[All, 2]],
s_Symbol :> ToString[Unevaluated[s]],
Infinity,
Heads -> True],
ToString[$hold]]]];
Dependencies[symbolSpace : {___String}][symbols : {(_Symbol | _String) ...}] :=
DeleteDuplicates[Flatten[Dependencies[symbolSpace] /@ symbols]];
DependencyLinks[symbolSpace : {___String}][symbol : (_Symbol | _String)] :=
Thread[symbol -> Dependencies[symbolSpace][symbol]];
DependencyLinks[symbolSpace : {___String}][symbols : {(_Symbol | _String) ...}] :=
DeleteDuplicates[Flatten[DependencyLinks[symbolSpace] /@ symbols]];
DependencyGraphStep[symbolSpace : {___String}][links : {___Rule}] :=
Union[links, DependencyLinks[symbolSpace][Values@links]];
DependencyGraph[symbolSpace : {___String}][symbol : (_Symbol | _String)] :=
DependencyGraph[symbolSpace][{ToString[symbol]}];
DependencyGraph[symbolSpace : {___String}][symbols : {(_Symbol | _String) ...}] :=
Graph[FixedPoint[DependencyGraphStep[symbolSpace], DependencyLinks[symbolSpace][ToString /@ symbols], 4]]
We can try it out:
f[x_] := f[x, 1];
f[x_, y_] := x + y;
g[x_] := f[x]^2;
g[x_, y_] := f[x, y]^y;
h[x_] := Cos[g[x]];
test = DependencyGraph[{"h", "g", "f"}][{h}];
Graph[test, VertexLabels -> Automatic]

And
GraphDistance[test, SymbolName[h], SymbolName[f]]
2
Something a bit more interesting maybe:
test2 = DependencyGraph[Names["Global`*"]][{h}];
Graph[test2, VertexLabels -> Automatic]

Caveat: this depends on DownValues only, so SubValues and OwnValues would need to be added if that degree of completeness were required.
Stack
. $\endgroup$f[x_] := Block[{stack = Stack[]}, Print[stack]; x^2]
(but you'll still have to determine what depth means for that). You might also be interested in Trace. It also depends on what you mean by "systematically". Do you want a mechanism that injects the Stack for a specified symbol or something? $\endgroup$