diff --git a/configure.ac b/configure.ac
index 8e11328..6d3042f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@
 # You should have received a copy of the GNU General Public License along with
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 
-AC_INIT([GNU make],[4.0.90],[bug-make@gnu.org])
+AC_INIT([GNU make],[4.0.90-profile],[bug-make@gnu.org])
 
 AC_PREREQ([2.62])
 
diff --git a/file.c b/file.c
index 92c4052..e26cd76 100644
--- a/file.c
+++ b/file.c
@@ -1049,6 +1049,128 @@ print_file_data_base (void)
   hash_print_stats (&files, stdout);
 }
 
+
+static double
+invoke_time(const struct file *f)
+{
+  double invoked_time_in_mill = (f->t_invoked.tv_sec) * 1000 +
+            (f->t_invoked.tv_usec) / 1000;
+  return invoked_time_in_mill;
+}
+
+static double
+finish_time(const struct file *f)
+{
+  double finished_time_in_mill = (f->t_finished.tv_sec) * 1000 +
+            (f->t_finished.tv_usec) / 1000;
+  return finished_time_in_mill;
+}
+
+static double
+diff_time (const struct file *f)
+{
+  double itime = invoke_time(f);
+  double ftime = finish_time(f);
+  return ftime-itime;
+}
+
+/* %N:%S:%D */
+void
+print_profile_simple (const struct file *f)
+{
+  fprintf (stderr, "%2$s" "%1$c"
+      "%3$s" "%1$c"
+      "%4$.0f" "%1$c"
+      "%5$.0f\n",
+      (int)profile_sep, profile_prefix, f->name,
+      invoke_time(f), diff_time(f));
+}
+
+/* %N:%S:%E */
+void
+print_profile_startend (const struct file *f)
+{
+  fprintf (stderr, "%2$s" "%1$c"
+      "%3$s" "%1$c"
+      "%4$.0f" "%1$c"
+      "%5$.0f\n",
+      profile_sep, profile_prefix, f->name,
+      invoke_time(f), finish_time(f));
+}
+
+/* %N:%L:%P:%S:%E:%D */
+void
+print_profile_short (const struct file *f)
+{
+  fprintf (stderr, "%2$s" "%1$c"
+      "%3$s" "%1$c"
+      "%4$d" "%1$c"
+      "%5$d" "%1$c"
+      "%6$.0f" "%1$c"
+      "%7$.0f" "%1$c"
+      "%8$.0f\n",
+      profile_sep, profile_prefix, f->name,
+      makelevel, (unsigned int)getpid(),
+      invoke_time(f), finish_time(f), diff_time(f) );
+}
+
+/* target=%N : level=%L : pid=%P : start=%S : end=%E : duration=%D */
+void
+print_profile_long (const struct file *f)
+{
+  fprintf (stderr, "%2$s" " %1$c "
+      "target=%3$s" " %1$c "
+      "level=%4$d" " %1$c "
+      "pid=%5$d" " %1$c "
+      "start=%6$.0f" " %1$c "
+      "end=%7$.0f" " %1$c "
+      "duration=%8$.0f\n",
+      profile_sep, profile_prefix, f->name,
+      makelevel, (unsigned int)getpid(),
+      invoke_time(f), finish_time(f), diff_time(f) );
+}
+
+
+static void
+print_target_update_time (const void *item)
+{
+  const struct file *f = item;
+
+  int built_in_special_target=(
+       (0==strcmp(f->name,".PHONY"))
+    || (0==strcmp(f->name,".SUFFIXES"))
+    || (0==strcmp(f->name,".DEFAULT"))
+    || (0==strcmp(f->name,".PRECIOUS"))
+    || (0==strcmp(f->name,".INTERMEDIATE"))
+    || (0==strcmp(f->name,".SECONDARY"))
+    || (0==strcmp(f->name,".SECONDEXPANSION"))
+    || (0==strcmp(f->name,".DELETE_ON_ERROR"))
+    || (0==strcmp(f->name,".IGNORE"))
+    || (0==strcmp(f->name,".LOW_RESOLUTION_TIME"))
+    || (0==strcmp(f->name,".SILENT"))
+    || (0==strcmp(f->name,".EXPORT_ALL_VARIABLES"))
+    || (0==strcmp(f->name,".NOTPARALLEL"))
+    || (0==strcmp(f->name,".ONESHELL"))
+    || (0==strcmp(f->name,".POSIX"))
+    );
+  if ((f->is_target) && (!built_in_special_target))
+    {
+
+      double itime = invoke_time(f);
+      if (!print_profile_func)
+        O( fatal, NILF, _("internal error: profile option active, "
+            "but no profile print format selected"));
+      if (itime)
+          print_profile_func(f);
+    }
+}
+
+void
+print_targets_update_time (void)
+{
+  hash_map (&files, print_target_update_time);
+}
+
 /* Verify the integrity of the data base of files.  */
 
 #define VERIFY_CACHED(_p,_n) \
diff --git a/filedef.h b/filedef.h
index f458551..c85d7bd 100644
--- a/filedef.h
+++ b/filedef.h
@@ -20,6 +20,7 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
    All of these are chained together through 'next'.  */
 
 #include "hash.h"
+#include <sys/time.h>
 
 struct file
   {
@@ -74,6 +75,9 @@ struct file
         cs_finished             /* Commands finished.  */
       } command_state ENUM_BITFIELD (2);
 
+    struct timeval t_invoked;
+    struct timeval t_finished;
+
     unsigned int builtin:1;     /* True if the file is a builtin rule. */
     unsigned int precious:1;    /* Non-0 means don't delete file on quit */
     unsigned int loaded:1;      /* True if the file is a loaded object. */
@@ -105,6 +109,18 @@ struct file
 
 extern struct file *suffix_file, *default_file;
 
+typedef void (*profile_print_func_t) (const struct file *target);
+typedef void (*prof_info_func_t) (const struct file *, char *);
+typedef struct prof_info_st
+  {
+    const char *fmt; /* format string for fprintf */
+    prof_info_func_t info_func;
+    struct prof_info_st *next;
+  } prof_info;
+
+extern char profile_sep;
+extern char *profile_prefix;
+extern profile_print_func_t print_profile_func;
 
 struct file *lookup_file (const char *name);
 struct file *enter_file (const char *name);
@@ -120,6 +136,25 @@ void init_hash_files (void);
 char *build_target_list (char *old_list);
 void print_prereqs (const struct dep *deps);
 void print_file_data_base (void);
+void print_targets_update_time (void);
+
+void prof_print_str (const struct file *f, char *fmt);
+void prof_print_name (const struct file *f, char *fmt);
+void prof_print_level (const struct file *f, char *fmt);
+void prof_print_pid (const struct file *f, char *fmt);
+void prof_print_invokets (const struct file *f, char *fmt);
+void prof_print_finishts (const struct file *f, char *fmt);
+void prof_print_diff (const struct file *f, char *fmt);
+
+/* %N:%S:%D */
+void print_profile_simple (const struct file *);
+/* %N:%S:%E */
+void print_profile_startend (const struct file *);
+/* %N:%L:%P:%S:%E:%D */
+void print_profile_short (const struct file *);
+/* target=%N : level=%L : pid=%P : start=%S : end=%E : duration=%D */
+void print_profile_long (const struct file *);
+
 
 #if FILE_TIMESTAMP_HI_RES
 # define FILE_TIMESTAMP_STAT_MODTIME(fname, st) \
diff --git a/main.c b/main.c
index fa0498f..74b5637 100644
--- a/main.c
+++ b/main.c
@@ -180,6 +180,17 @@ int ignore_errors_flag = 0;
 
 int print_data_base_flag = 0;
 
+/* Nonzero means print time stamps for start and finish for each of the
+   targets in the makefile. */
+
+static char *profile_option = 0;
+
+char profile_sep = ':';
+char *profile_prefix = 0;
+
+/* The actual function doing the profile information printing */
+profile_print_func_t print_profile_func = 0;
+
 /* Nonzero means don't remake anything; just return a nonzero status
    if the specified targets are not up to date (-q).  */
 
@@ -358,6 +369,12 @@ static const char *const usage[] =
     N_("\
   -p, --print-data-base       Print make's internal database.\n"),
     N_("\
+  -P[[[PREFIX]:[SEP]]format],\n\
+  --profile[=[[PREFIX]:[SEP]]format],\n\
+  --profile-format[=[[PREFIX]:[SEP]]format]\n\
+                              Print profiling information for each target\n\
+                              using the format 'FMT'.\n"),
+    N_("\
   -q, --question              Run no recipe; exit status says if up to date.\n"),
     N_("\
   -r, --no-builtin-rules      Disable the built-in implicit rules.\n"),
@@ -434,6 +451,7 @@ static const struct command_switch switches[] =
 #endif
     { 'o', filename, &old_files, 0, 0, 0, 0, 0, "old-file" },
     { 'O', string, &output_sync_option, 1, 1, 0, "target", 0, "output-sync" },
+    { 'P', string, &profile_option, 1, 1, 0, "PROFILE:short", 0, "profile" },
     { 'W', filename, &new_files, 0, 0, 0, 0, 0, "what-if" },
 
     /* These are long-style options.  */
@@ -461,6 +479,7 @@ static struct option long_option_aliases[] =
     { "max-load",       optional_argument,      0, 'l' },
     { "dry-run",        no_argument,            0, 'n' },
     { "recon",          no_argument,            0, 'n' },
+    { "profile-format", optional_argument,      0, 'P' },
     { "makefile",       required_argument,      0, 'f' },
   };
 
@@ -777,6 +796,57 @@ decode_output_sync_flags (void)
     RECORD_SYNC_MUTEX (sync_mutex);
 }
 
+static void
+decode_profile_format (void)
+{
+  char *p = profile_option;
+  char *prefix_str_src = profile_option;
+  char default_profile_prefix[] = "PROFILE";
+  size_t len = 0;
+
+  if (!profile_option)
+    return;
+
+  /* If the profile option contains ':' the string up to it is a prefix to be
+     printed before the profile information; If not, the default prefix
+     is chosen: 'PROFILE'; The rest is the possible profile format selector */
+  p = index (profile_option, ':');
+  if (p)
+    {
+      len = (size_t)(p - profile_option);
+      p++;
+    }
+  else
+    {
+      prefix_str_src = default_profile_prefix;
+      len = strlen (default_profile_prefix);
+      p = profile_option;
+    }
+  /* we delay setting profile_prefix to prevent useless memory allocation */
+
+  /* an optional filed separator can be chosen from the predefined set */
+  if ( (*p) == '\0' )
+    O (fatal, NILF, _("profile type can't be empty"));
+  else
+    if ( strpbrk(p," %#@!`&:;,.|_+=^<>/\t") == p ) profile_sep = *p++;
+
+  if (!strcmp (p, "simple"))   print_profile_func = print_profile_simple;
+  if (!strcmp (p, "startend")) print_profile_func = print_profile_startend;
+  if (!strcmp (p, "short"))    print_profile_func = print_profile_short;
+  if (!strcmp (p, "long"))     print_profile_func = print_profile_long;
+  if (!print_profile_func)
+      OS (fatal, NILF, _("unknown profile type '%s'"), p);
+
+  profile_prefix = xstrndup(prefix_str_src, len);
+
+}
+
+void
+clean_profile(void)
+{
+  if (profile_prefix) free(profile_prefix);
+}
+
 #ifdef WINDOWS32
 
 #ifndef NO_OUTPUT_SYNC
@@ -2893,6 +2963,7 @@ decode_switches (int argc, char **argv, int env)
   /* If there are any options that need to be decoded do it now.  */
   decode_debug_flags ();
   decode_output_sync_flags ();
+  decode_profile_format ();
 }
 
 /* Decode switches from environment variable ENVAR (which is LEN chars long).
@@ -3399,9 +3470,14 @@ die (int status)
       if (print_data_base_flag)
         print_data_base ();
 
+      if (profile_option)
+        print_targets_update_time ();
+
       if (verify_flag)
         verify_file_data_base ();
 
+      if (profile_option)
+        clean_profile();
       clean_jobserver (status);
 
       if (output_context)
diff --git a/make-j_profile.png b/make-j_profile.png
new file mode 100644
index 0000000..6e4e144
Binary files /dev/null and b/make-j_profile.png differ
diff --git a/remake.c b/remake.c
index ff22277..34f8104 100644
--- a/remake.c
+++ b/remake.c
@@ -860,6 +860,7 @@ notice_finished_file (struct file *file)
   int touched = 0;
 
   file->command_state = cs_finished;
+  gettimeofday (&file->t_finished, NULL);
   file->updated = 1;
 
   if (touch_flag
@@ -1184,6 +1185,7 @@ touch_file (struct file *file)
 static void
 remake_file (struct file *file)
 {
+  gettimeofday (&file->t_invoked, NULL);
   if (file->cmds == 0)
     {
       if (file->phony)
