The time spent executing your code (user time). If your program is CPU intensive then this is the time you will be trying to minimize.
The time spent by the system executing on behalf of your code (system time). It essentially measures how long the kernel spent executing services for your program (printf, open, write, etc).
The total time elapsed during execution (wall clock time). Ultimately, this is the time that most matters to you, but it is an amalgamation of several other factors, so it can be misleading. The same application running under a loaded system will take a lot longer than in a mostly idle system. So, when analyzing your code for performance problems, wall clock time should rarely be the focus of your attention.
Measuring time
The simplest way to measure how long your program takes to run is the time utility:

time ./my-prog
If you are using bash as your shell, you will see something like this:

[ ... program output ... ]

real    0m8.233s
user    0m7.583s
sys     0m0.412s
This means that the program executed for a total of 8.23 seconds, out of which 7.58 were spent executing your code. The system spent 0.41 seconds executing system calls made inside my-prog. Notice that the numbers do not seem to add up. What's missing is all the other activities that go on behind the scenes when the system executes a program (loading time, context switching, memory management, etc).

This output tells you that my-prog is fairly CPU intensive, so if you are having performance problems, they will most certainly be in your code or in the libraries used by your code. On the other hand, a program that makes a lot of system calls may show something like this:

[ ... program output ... ]

real    0m20.081s
user    0m2.431s
sys     0m17.580s
This particular output came from a program that opens a file, writes to it, and closes it again 3,000,000 times. Notice how the program is now spending 87% of its time making system calls. If we now make the simple change of not opening and closing the file inside the loop.

[ ... program output ... ]

real    0m8.467s
user    0m1.127s
sys     0m7.257s
While we haven't changed the profile of the program (it still spends an inordinate amount of time in the system), we have reduced its runtime quite a bit. Granted, these are rather contrived examples, but they do happen.

Knowing how long the program takes to run is obviously not enough to pinpoint hot spots. You also need to know exactly where the problems occur. There are several mechanisms available, each will have its own innate advantages and shortcomings. It depends greatly on the nature of your application (size, use of external libraries, etc).

Inserting probes in your code
You can get timing information from inside your program by calling one of a family of timer functions: times, getrusage, and clock. They all return the amount of time used by the program so far, so the idea is to insert them as calipers in your code. The first one before entering the code you want to measure and the second one at the end. For instance, using times:


#include <sys/times.h>

compute ()
{
  struct tms t1, t2;
  clock_t ut_elapsed, st_elapsed;

  times (&t1);

  /* Do work */

  times (&t2);
  ut_elapsed = t2.tms_utime - t1.tms_utime;
  st_elapsed = t2.tms_stime - t1.tms_stime;
}

Both times and getrusage are fairly accurate, while clock returns only an approximation. Furthermore, with getrusage you can obtain more information besides timing. It also returns generic resource usage information like memory usage, swap activity, input/output operations, etc.

These functions are useful in situations where modifications to the source are manageable. Typically this means that you are familiar with the code and know which sections you are interested in monitoring. Not surprisingly, this is not often the case. Usually, all one knows is that the program is misbehaving. It would be impractical to add timer calls haphazardly in a large program just to guess where the problem may be. For this, we have profiling tools.

A profiler will automatically collect timing information as your program executes. At the end, all the collected information is aggregated and presented for analysis. There are three popular choices in this area: GProf, OProfile and Valgrind.

