Kernel flow graphing (was Re: Take a deep breath...)

Keith Owens (kaos@ocs.com.au)
Wed, 16 Jul 1997 21:40:11 +1000


On 15 Jul 1997 19:44:43 +0300,
Kohtala Marko <Marko.Kohtala@ntc.nokia.com> wrote:
>I have been playing with an idea of adding some LClint directives into
>linux source and perhaps extending LClint to provide some additional
>information about the source code (call graphs, uses of structures,
>uses of structure members, ranges of values used).

FWIW, some time ago I wrote perl code that took the output from "cflow
-axg <filename(s)>" and generated commands to a compiler visulation
tool (xvcg). The aim was to do part of what you suggested, determine
the call graphs. Unfortunately even something as "small" as
net/ipv4/*.[ch] generated a very wide, very flat graph. It was not
more than 6 levels deep but was about 50 functions wide. Took up a lot
of room on the wall :).

Add to that the fact that so many of the higher level functions are not
called directly but instead are called via pointers from structures and
it becomes very difficult to automatically graph the function flow.

Take function ipfw_device_event in ipv4/ip_fw.c (2.1.44). The only
reference to that is in structure ipfw_dev_notifier in the same source.
The structure is passed to register_netdevice_notifier in core/dev.c
which in turn calls notifier_chain_register. notifier_chain_register
is an inline macro in linux/notifier.h, with the parameters from
register_netdevice_notifier it adds ipfw_device_event to the
netdev_chain. Functions on the netdev_chain are invoked via
notifier_call_chain (another inline macro) from 5 places in core/dev.c
and 3 places in ipv4/devinet.c.

To a human, all the above means that ipfw_device_event is called when
any network device comes up, goes down, changes address, changes MTU or
changes some interface flags. However no tool like lint or cflow has
any hope of tracking that depth of function flow with all the
indirection that goes on. Flow within a module can be done but not
across modules.

I suspect the real answer is the equivalent of short man pages for each
high level function. Something like :-

int register_netdevice_notifier(struct notifier_block *nb)

Adds the function in field nb->notifier_call to the netdev_chain.
Functions on this chain will be called for events NETDEV_UP,
NETDEV_DOWN, NETDEV_CHANGE, NETDEV_CHANGEMTU, NETDEV_CHANGEADDR.
See function notifier_call_chain for return code handling of called
functions.

register_netdevice_notifier return code: Always 0, see
notifier_chain_register.

int ipfw_device_event(struct notifier_block *this, unsigned long event, void *ptr)

Called via notifier_call_chain on netdev_chain (see
register_netdevice_notifier). Entered in any state. Disables
interrupts to update the device information in the firewall chains
for NETDEV_UP and NETDEV_DOWN. Restores initial state on exit.

ipfw_device_event return code: Always NOTIFY_DONE.

Best place is probably in the source, even though that will increase
the tar ball size. Some sort of standard format that can be extracted
and printed or converted to HTML.