Surfing with ctags
By: . Published: . Categories: tools tips vim.Big code, little time, and you’ve work to do.
Where’s that function at? What’s in that struct?
There’s an app for that: ctags.
In Brief
The short version:
- Install exuberant ctags.
ctags -R .
in the project’s top-level directory to generate the tags file.vim $file
from within the same directory as your tags file.- Alternatively:
vim -t $tagname
to jump straight to that tag.
- Alternatively:
CTRL-]
while cursor in a tag (roughly fn/var/other name) pushes your current location then jumps to definition of the tag you were on.CTRL-t
pops back up the tag stack, so you can continue where you left off.:help tags
for more, but that’s about all you need.
(The astute vimmer will now note that the ^]/^t pair is the same one you use to jump around in vim help files. Handy, that.)
If you forget this, just man ctags
, /HOW TO USE
, and read that very brief section.
Ctags!
You don’t want just any ctags. You want the exuberant kind. Don’t ask why, just trust me: brew install ctags
and get yourself some exuberant ctags.
Caramel Apple Tarball
To demonstrate, nab yourself delicious Apple-flavored libc. This particular variety is the one included with 10.8.3. tar xzf Libc-825.26.tar.gz
, cd Libc-825.26
into the resulting directory, and wow that’s a good chunk of stuff.
Surfing Ctags
Now it’s time for ctags. First, we generate the tags file by walking the entire directory hierarchy we’re sitting at the root of. Don’t worry, it’s not so bad as all that, just a -R
ecurse flag and a .
directory to kick things off:
Libc-825.26% ctags -R .
Libc-825.26% wc -l tags
12832 tags
Thassa lotta tags.
Let’s jump into the thick of it. Fire up vim
at the definition of pthread_cond_signal
:
Libc-825.26% vi -t pthread_cond_signal
We find ourselves with cursor blinking right on the p
at the start of pthread_cond_signal
:
/*
* Signal a condition variable, waking only one thread.
*/
int
pthread_cond_signal(pthread_cond_t *cond)
{
return pthread_cond_signal_thread_np(cond, NULL);
}
This is where things would start to suck if we didn’t have ctags. The entire function is just a call to yet another function. The _np
non-portable suffix makes me think maybe it will be somewhere else. Maybe they exiled all the non-portable functions to a different file? Who knows. Even better: we don’t care.
/_np
, RET
, and hit CTRL-]
to jump right to that function:
/*
* Signal a condition variable, waking a specified thread.
*/
int
pthread_cond_signal_thread_np(pthread_cond_t *ocond, pthread_t thread)
{
npthread_cond_t * cond = (npthread_cond_t *)ocond;
int sig = cond->sig;
Ooh, fun, we transition from an old-cond to a new-style struct. (Maybe that _np
suffix was new pthreads
this time.) What’s the difference between old and new? Let’s take a look-see and find out.
/np
, n
, RET
, CTRL-]
. Looks like the definition of npthread_cond_t
lives right below the original pthread_cond_t
struct definition, so comparing the two is easy. The new version looks to have swapped out most of the old guts:
/*
* Condition variables
*/
#define _PTHREAD_COND_T
typedef struct _pthread_cond
{
long sig; /* Unique signature for this structure */
pthread_lock_t lock; /* Used for internal mutex on structure */
uint32_t waiters:15, /* Number of threads waiting */
sigspending:15, /* Number of outstanding signals */
pshared:2;
struct _pthread_cond *next, *prev; /* List of condition variables using mutex */
struct _pthread_mutex *busy; /* mutex associated with variable */
semaphore_t sem; /* Kernel semaphore */
} pthread_cond_t;
for free space and a different approach to getting things done:
typedef struct _npthread_cond
{
long sig; /* Unique signature for this structure */
pthread_lock_t lock; /* Used for internal mutex on structure */
uint32_t rfu:29, /* not in use*/
misalign: 1, /* structure is not aligned to 8 byte boundary */
pshared:2;
struct _npthread_mutex *busy; /* mutex associated with variable */
uint32_t c_seq[3];
#if defined(__LP64__)
uint32_t reserved[3];
#endif /* __LP64__ */
} npthread_cond_t;
There’s also LP64 support and some alignment games. Fun times.
Well, that’s enough struct-gazing, we can pop back on up to where we were with CTRL-T
and resume looking at the implementation of pthread_cond_signal_thread_np
, right where we left off.
int
pthread_cond_signal_thread_np(pthread_cond_t *ocond, pthread_t thread)
I bet it’d be interesting to see what happens when that thread
argument is NULL
, as it is in a standard pthread_cond_signal
call.
And when might it be called with a non-NULL
value?
Curious and curiouser.
I leave you to it, dear reader, with exuberant ctags frolicking and wagging tail at your side.
Added 2013-07-26T15:03:08Z-0400: This post is an introductory walkthrough. Once you’ve got the hang of the basics, check out the discussion at /r/vim for further pointers.