a5bd9f6
From 6136b9fb4811ee44ec16f3ad9f4306d0798419b1 Mon Sep 17 00:00:00 2001
a5bd9f6
From: Vladimir 'phcoder' Serbinenko <phcoder@gmail.com>
a5bd9f6
Date: Tue, 19 Mar 2013 20:25:09 +0100
a5bd9f6
Subject: [PATCH 212/364] 	Implement boot time analysis framework.
a5bd9f6
a5bd9f6
---
a5bd9f6
 ChangeLog                     |  6 +++-
a5bd9f6
 config.h.in                   |  1 +
a5bd9f6
 configure.ac                  | 19 +++++++++++++
a5bd9f6
 grub-core/Makefile.core.def   |  6 ++++
a5bd9f6
 grub-core/bus/usb/ehci.c      |  7 +++++
a5bd9f6
 grub-core/bus/usb/usb.c       |  9 ++++--
a5bd9f6
 grub-core/bus/usb/usbhub.c    | 35 +++++++++++++++++++----
a5bd9f6
 grub-core/commands/boottime.c | 66 +++++++++++++++++++++++++++++++++++++++++++
a5bd9f6
 grub-core/disk/usbms.c        |  4 +++
a5bd9f6
 grub-core/kern/dl.c           |  3 ++
a5bd9f6
 grub-core/kern/main.c         | 12 ++++++++
a5bd9f6
 grub-core/kern/misc.c         | 39 +++++++++++++++++++++++++
a5bd9f6
 grub-core/normal/main.c       | 13 +++++++++
a5bd9f6
 include/grub/misc.h           | 20 +++++++++++++
a5bd9f6
 14 files changed, 232 insertions(+), 8 deletions(-)
a5bd9f6
 create mode 100644 grub-core/commands/boottime.c
a5bd9f6
a5bd9f6
diff --git a/ChangeLog b/ChangeLog
a5bd9f6
index a544fbf..94dd5bb 100644
a5bd9f6
--- a/ChangeLog
a5bd9f6
+++ b/ChangeLog
a5bd9f6
@@ -1,6 +1,10 @@
a5bd9f6
 2013-03-19  Vladimir Serbinenko  <phcoder@gmail.com>
a5bd9f6
 
a5bd9f6
-	remove get_endpoint_descriptor and change all functions needing
a5bd9f6
+	Implement boot time analysis framework.
a5bd9f6
+
a5bd9f6
+2013-03-19  Vladimir Serbinenko  <phcoder@gmail.com>
a5bd9f6
+
a5bd9f6
+	Remove get_endpoint_descriptor and change all functions needing
a5bd9f6
 	descriptor to just receive it as argument rather than endpoint
a5bd9f6
 	address.
a5bd9f6
 
a5bd9f6
diff --git a/config.h.in b/config.h.in
a5bd9f6
index 621742c..2e1f459 100644
a5bd9f6
--- a/config.h.in
a5bd9f6
+++ b/config.h.in
a5bd9f6
@@ -8,6 +8,7 @@
a5bd9f6
 
a5bd9f6
 /* Define to 1 to enable disk cache statistics.  */
a5bd9f6
 #define DISK_CACHE_STATS @DISK_CACHE_STATS@
a5bd9f6
+#define BOOT_TIME_STATS @BOOT_TIME_STATS@
a5bd9f6
 
a5bd9f6
 #if defined (GRUB_UTIL) || !defined (GRUB_MACHINE)
a5bd9f6
 #include <config-util.h>
a5bd9f6
diff --git a/configure.ac b/configure.ac
a5bd9f6
index 038f429..a39a025 100644
a5bd9f6
--- a/configure.ac
a5bd9f6
+++ b/configure.ac
a5bd9f6
@@ -798,6 +798,17 @@ else
a5bd9f6
 fi
a5bd9f6
 AC_SUBST([DISK_CACHE_STATS])
a5bd9f6
 
a5bd9f6
+AC_ARG_ENABLE([boot-time],
a5bd9f6
+	      AS_HELP_STRING([--enable-boot-time],
a5bd9f6
+                             [enable boot time statistics collection]))
a5bd9f6
+
a5bd9f6
+if test x$enable_boot_time = xyes; then
a5bd9f6
+  BOOT_TIME_STATS=1
a5bd9f6
+else
a5bd9f6
+  BOOT_TIME_STATS=0
a5bd9f6
+fi
a5bd9f6
+AC_SUBST([BOOT_TIME_STATS])
a5bd9f6
+
a5bd9f6
 AC_ARG_ENABLE([grub-emu-usb],
a5bd9f6
 	      [AS_HELP_STRING([--enable-grub-emu-usb],
a5bd9f6
                              [build and install the `grub-emu' debugging utility with USB support (default=guessed)])])
a5bd9f6
@@ -1159,6 +1170,7 @@ AM_CONDITIONAL([COND_GRUB_PE2ELF], [test x$TARGET_OBJ2ELF != x])
a5bd9f6
 AM_CONDITIONAL([COND_APPLE_CC], [test x$TARGET_APPLE_CC = x1])
a5bd9f6
 AM_CONDITIONAL([COND_ENABLE_EFIEMU], [test x$enable_efiemu = xyes])
a5bd9f6
 AM_CONDITIONAL([COND_ENABLE_CACHE_STATS], [test x$DISK_CACHE_STATS = x1])
a5bd9f6
+AM_CONDITIONAL([COND_ENABLE_BOOT_TIME_STATS], [test x$BOOT_TIME_STATS = x1])
a5bd9f6
 
a5bd9f6
 AM_CONDITIONAL([COND_HAVE_ASM_USCORE], [test x$HAVE_ASM_USCORE = x1])
a5bd9f6
 AM_CONDITIONAL([COND_CYGWIN], [test x$host_os = xcygwin])
a5bd9f6
@@ -1231,6 +1243,13 @@ echo With disk cache statistics: Yes
a5bd9f6
 else
a5bd9f6
 echo With disk cache statistics: No
a5bd9f6
 fi
a5bd9f6
+
a5bd9f6
+if [ x"$enable_boot_time" = xyes ]; then
a5bd9f6
+echo With boot time statistics: Yes
a5bd9f6
+else
a5bd9f6
+echo With boot time statistics: No
a5bd9f6
+fi
a5bd9f6
+
a5bd9f6
 if [ x"$efiemu_excuse" = x ]; then
a5bd9f6
 echo efiemu runtime: Yes
a5bd9f6
 else
a5bd9f6
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
a5bd9f6
index 3bcf662..5c15f32 100644
a5bd9f6
--- a/grub-core/Makefile.core.def
a5bd9f6
+++ b/grub-core/Makefile.core.def
a5bd9f6
@@ -1869,6 +1869,12 @@ module = {
a5bd9f6
 };
a5bd9f6
 
a5bd9f6
 module = {
a5bd9f6
+  name = boottime;
a5bd9f6
+  common = commands/boottime.c;
a5bd9f6
+  condition = COND_ENABLE_BOOT_TIME_STATS;
a5bd9f6
+};
a5bd9f6
+
a5bd9f6
+module = {
a5bd9f6
   name = adler32;
a5bd9f6
   common = lib/adler32.c;
a5bd9f6
 };
a5bd9f6
diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c
a5bd9f6
index c60873d..a5a24af 100644
a5bd9f6
--- a/grub-core/bus/usb/ehci.c
a5bd9f6
+++ b/grub-core/bus/usb/ehci.c
a5bd9f6
@@ -715,6 +715,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
a5bd9f6
       usblegsup = grub_pci_read (pciaddr_eecp);
a5bd9f6
       if (usblegsup & GRUB_EHCI_BIOS_OWNED)
a5bd9f6
 	{
a5bd9f6
+	  grub_boot_time ("Taking ownership of EHCI port");
a5bd9f6
 	  grub_dprintf ("ehci",
a5bd9f6
 			"EHCI grub_ehci_pci_iter: EHCI owned by: BIOS\n");
a5bd9f6
 	  /* Ownership change - set OS_OWNED bit */
a5bd9f6
@@ -741,6 +742,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
a5bd9f6
 	      /* Ensure PCI register is written */
a5bd9f6
 	      grub_pci_read (pciaddr_eecp);
a5bd9f6
 	    }
a5bd9f6
+	  grub_boot_time ("Ownership of EHCI port taken");
a5bd9f6
 	}
a5bd9f6
       else if (usblegsup & GRUB_EHCI_OS_OWNED)
a5bd9f6
 	/* XXX: What to do in this case - nothing ? Can it happen ? */
a5bd9f6
@@ -1706,10 +1708,12 @@ grub_ehci_portstatus (grub_usb_controller_t dev,
a5bd9f6
   /* Reset RESET bit and wait for the end of reset */
a5bd9f6
   grub_ehci_port_resbits (e, port, GRUB_EHCI_PORT_RESET);
a5bd9f6
   endtime = grub_get_time_ms () + 1000;
a5bd9f6
+  grub_boot_time ("Resetting port %d", port);
a5bd9f6
   while (grub_ehci_port_read (e, port) & GRUB_EHCI_PORT_RESET)
a5bd9f6
     if (grub_get_time_ms () > endtime)
a5bd9f6
       return grub_error (GRUB_ERR_IO,
a5bd9f6
 			 "portstatus: EHCI Timed out - reset port");
a5bd9f6
+  grub_boot_time ("Port %d reset", port);
a5bd9f6
   /* Remember "we did the reset" - needed by detect_dev */
a5bd9f6
   e->reset |= (1 << port);
a5bd9f6
   /* Test if port enabled, i.e. HIGH speed device connected */
a5bd9f6
@@ -1911,8 +1915,11 @@ GRUB_MOD_INIT (ehci)
a5bd9f6
 {
a5bd9f6
   COMPILE_TIME_ASSERT (sizeof (struct grub_ehci_td) == 64);
a5bd9f6
   COMPILE_TIME_ASSERT (sizeof (struct grub_ehci_qh) == 96);
a5bd9f6
+  grub_boot_time ("Initing EHCI hardware");
a5bd9f6
   grub_ehci_inithw ();
a5bd9f6
+  grub_boot_time ("Registering EHCI driver");
a5bd9f6
   grub_usb_controller_dev_register (&usb_controller);
a5bd9f6
+  grub_boot_time ("EHCI driver registered");
a5bd9f6
   grub_loader_register_preboot_hook (grub_ehci_fini_hw, grub_ehci_restore_hw,
a5bd9f6
 				     GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
a5bd9f6
 }
a5bd9f6
diff --git a/grub-core/bus/usb/usb.c b/grub-core/bus/usb/usb.c
a5bd9f6
index 7a517f8..108c69b 100644
a5bd9f6
--- a/grub-core/bus/usb/usb.c
a5bd9f6
+++ b/grub-core/bus/usb/usb.c
a5bd9f6
@@ -262,8 +262,13 @@ void grub_usb_device_attach (grub_usb_device_t dev)
a5bd9f6
 	continue;
a5bd9f6
 
a5bd9f6
       for (desc = attach_hooks; desc; desc = desc->next)
a5bd9f6
-	if (interf->class == desc->class && desc->hook (dev, 0, i))
a5bd9f6
-	  dev->config[0].interf[i].attached = 1;
a5bd9f6
+	if (interf->class == desc->class)
a5bd9f6
+	  {
a5bd9f6
+	    grub_boot_time ("Probing USB device driver class %x", desc->class);
a5bd9f6
+	    if (desc->hook (dev, 0, i))
a5bd9f6
+	      dev->config[0].interf[i].attached = 1;
a5bd9f6
+	    grub_boot_time ("Probed USB device driver class %x", desc->class);
a5bd9f6
+	  }
a5bd9f6
 
a5bd9f6
       if (dev->config[0].interf[i].attached)
a5bd9f6
 	continue;
a5bd9f6
diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
a5bd9f6
index 7e7dc8c..f95a567 100644
a5bd9f6
--- a/grub-core/bus/usb/usbhub.c
a5bd9f6
+++ b/grub-core/bus/usb/usbhub.c
a5bd9f6
@@ -52,6 +52,8 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
a5bd9f6
   int i;
a5bd9f6
   grub_usb_err_t err;
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Attaching USB device");
a5bd9f6
+
a5bd9f6
   dev = grub_zalloc (sizeof (struct grub_usb_device));
a5bd9f6
   if (! dev)
a5bd9f6
     return NULL;
a5bd9f6
@@ -108,8 +110,12 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
a5bd9f6
 
a5bd9f6
   /* Wait "recovery interval", spec. says 2ms */
a5bd9f6
   grub_millisleep (2);
a5bd9f6
+
a5bd9f6
+  grub_boot_time ("Probing USB device driver");
a5bd9f6
   
a5bd9f6
   grub_usb_device_attach (dev);
a5bd9f6
+
a5bd9f6
+  grub_boot_time ("Attached USB device");
a5bd9f6
   
a5bd9f6
   return dev;
a5bd9f6
 }
a5bd9f6
@@ -194,6 +200,8 @@ attach_root_port (struct grub_usb_hub *hub, int portno,
a5bd9f6
   grub_usb_speed_t current_speed = GRUB_USB_SPEED_NONE;
a5bd9f6
   int changed=0;
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("detect_dev USB root portno=%d\n", portno);
a5bd9f6
+
a5bd9f6
 #if 0
a5bd9f6
 /* Specification does not say about disabling of port when device
a5bd9f6
  * connected. If disabling is really necessary for some devices,
a5bd9f6
@@ -203,6 +211,9 @@ attach_root_port (struct grub_usb_hub *hub, int portno,
a5bd9f6
   if (err)
a5bd9f6
     return;
a5bd9f6
 #endif
a5bd9f6
+
a5bd9f6
+  grub_boot_time ("Before the stable power wait portno=%d", portno);
a5bd9f6
+
a5bd9f6
   /* Wait for completion of insertion and stable power (USB spec.)
a5bd9f6
    * Should be at least 100ms, some devices requires more...
a5bd9f6
    * There is also another thing - some devices have worse contacts
a5bd9f6
@@ -239,6 +250,8 @@ attach_root_port (struct grub_usb_hub *hub, int portno,
a5bd9f6
   /* If the device is a Hub, scan it for more devices.  */
a5bd9f6
   if (dev->descdev.class == 0x09)
a5bd9f6
     grub_usb_add_hub (dev);
a5bd9f6
+
a5bd9f6
+  grub_boot_time ("Attached root port");
a5bd9f6
 }
a5bd9f6
 
a5bd9f6
 grub_usb_err_t
a5bd9f6
@@ -248,6 +261,8 @@ grub_usb_root_hub (grub_usb_controller_t controller)
a5bd9f6
   struct grub_usb_hub *hub;
a5bd9f6
   int changed=0;
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Registering USB root hub");
a5bd9f6
+
a5bd9f6
   hub = grub_malloc (sizeof (*hub));
a5bd9f6
   if (!hub)
a5bd9f6
     return GRUB_USB_ERR_INTERNAL;
a5bd9f6
@@ -287,6 +302,8 @@ grub_usb_root_hub (grub_usb_controller_t controller)
a5bd9f6
         }
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("USB root hub registered");
a5bd9f6
+
a5bd9f6
   return GRUB_USB_ERR_NONE;
a5bd9f6
 }
a5bd9f6
 
a5bd9f6
@@ -407,12 +424,13 @@ poll_nonroot_hub (grub_usb_device_t dev)
a5bd9f6
 	  /* Connected and status of connection changed ? */
a5bd9f6
 	  if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
a5bd9f6
 	    {
a5bd9f6
+	      grub_boot_time ("Before the stable power wait portno=%d", i);
a5bd9f6
 	      /* A device is actually connected to this port. */
a5bd9f6
-  /* Wait for completion of insertion and stable power (USB spec.)
a5bd9f6
-   * Should be at least 100ms, some devices requires more...
a5bd9f6
-   * There is also another thing - some devices have worse contacts
a5bd9f6
-   * and connected signal is unstable for some time - we should handle
a5bd9f6
-   * it - but prevent deadlock in case when device is too faulty... */
a5bd9f6
+	      /* Wait for completion of insertion and stable power (USB spec.)
a5bd9f6
+	       * Should be at least 100ms, some devices requires more...
a5bd9f6
+	       * There is also another thing - some devices have worse contacts
a5bd9f6
+	       * and connected signal is unstable for some time - we should handle
a5bd9f6
+	       * it - but prevent deadlock in case when device is too faulty... */
a5bd9f6
               for (total = j = 0; (j < 250) && (total < 2000); j++, total++)
a5bd9f6
                 {
a5bd9f6
                   grub_millisleep (1);
a5bd9f6
@@ -432,6 +450,9 @@ poll_nonroot_hub (grub_usb_device_t dev)
a5bd9f6
                   if (!(current_status & GRUB_USB_HUB_STATUS_PORT_CONNECTED))
a5bd9f6
                     j = 0;
a5bd9f6
                 }
a5bd9f6
+
a5bd9f6
+	      grub_boot_time ("After the stable power wait portno=%d", i);
a5bd9f6
+
a5bd9f6
               grub_dprintf ("usb", "(non-root) total=%d\n", total);
a5bd9f6
               if (total >= 2000)
a5bd9f6
                 continue;
a5bd9f6
@@ -443,6 +464,8 @@ poll_nonroot_hub (grub_usb_device_t dev)
a5bd9f6
 				    GRUB_USB_REQ_SET_FEATURE,
a5bd9f6
 				    GRUB_USB_HUB_FEATURE_PORT_RESET,
a5bd9f6
 				    i, 0, 0);
a5bd9f6
+	      grub_boot_time ("Resetting port %d", i);
a5bd9f6
+
a5bd9f6
 	      rescan = 1;
a5bd9f6
 	      /* We cannot reset more than one device at the same time !
a5bd9f6
 	       * Resetting more devices together results in very bad
a5bd9f6
@@ -464,6 +487,8 @@ poll_nonroot_hub (grub_usb_device_t dev)
a5bd9f6
 				GRUB_USB_REQ_CLEAR_FEATURE,
a5bd9f6
 				GRUB_USB_HUB_FEATURE_C_PORT_RESET, i, 0, 0);
a5bd9f6
 
a5bd9f6
+	  grub_boot_time ("Port %d reset", i);
a5bd9f6
+
a5bd9f6
 	  if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)
a5bd9f6
 	    {
a5bd9f6
 	      grub_usb_speed_t speed;
a5bd9f6
diff --git a/grub-core/commands/boottime.c b/grub-core/commands/boottime.c
a5bd9f6
new file mode 100644
a5bd9f6
index 0000000..cd7f70a
a5bd9f6
--- /dev/null
a5bd9f6
+++ b/grub-core/commands/boottime.c
a5bd9f6
@@ -0,0 +1,66 @@
a5bd9f6
+/* cacheinfo.c - disk cache statistics  */
a5bd9f6
+/*
a5bd9f6
+ *  GRUB  --  GRand Unified Bootloader
a5bd9f6
+ *  Copyright (C) 2008,2010  Free Software Foundation, Inc.
a5bd9f6
+ *
a5bd9f6
+ *  GRUB is free software: you can redistribute it and/or modify
a5bd9f6
+ *  it under the terms of the GNU General Public License as published by
a5bd9f6
+ *  the Free Software Foundation, either version 3 of the License, or
a5bd9f6
+ *  (at your option) any later version.
a5bd9f6
+ *
a5bd9f6
+ *  GRUB is distributed in the hope that it will be useful,
a5bd9f6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
a5bd9f6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
a5bd9f6
+ *  GNU General Public License for more details.
a5bd9f6
+ *
a5bd9f6
+ *  You should have received a copy of the GNU General Public License
a5bd9f6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
a5bd9f6
+ */
a5bd9f6
+
a5bd9f6
+#include <grub/dl.h>
a5bd9f6
+#include <grub/misc.h>
a5bd9f6
+#include <grub/command.h>
a5bd9f6
+#include <grub/i18n.h>
a5bd9f6
+
a5bd9f6
+GRUB_MOD_LICENSE ("GPLv3+");
a5bd9f6
+
a5bd9f6
+
a5bd9f6
+static grub_err_t
a5bd9f6
+grub_cmd_boottime (struct grub_command *cmd __attribute__ ((unused)),
a5bd9f6
+		   int argc __attribute__ ((unused)),
a5bd9f6
+		   char *argv[] __attribute__ ((unused)))
a5bd9f6
+{
a5bd9f6
+  struct grub_boot_time *cur;
a5bd9f6
+  grub_uint64_t last_time = 0, start_time = 0;
a5bd9f6
+  if (!grub_boot_time_head)
a5bd9f6
+    {
a5bd9f6
+      grub_puts_ (N_("No boot time statistics is available\n"));
a5bd9f6
+      return 0;
a5bd9f6
+    }
a5bd9f6
+  start_time = last_time = grub_boot_time_head->tp;
a5bd9f6
+  for (cur = grub_boot_time_head; cur; cur = cur->next)
a5bd9f6
+    {
a5bd9f6
+      grub_uint32_t tmabs = cur->tp - start_time;
a5bd9f6
+      grub_uint32_t tmrel = cur->tp - last_time;
a5bd9f6
+      last_time = cur->tp;
a5bd9f6
+
a5bd9f6
+      grub_printf ("%3d.%03ds %2d.%03ds %s:%d %s\n", 
a5bd9f6
+		   tmabs / 1000, tmabs % 1000, tmrel / 1000, tmrel % 1000, cur->file, cur->line,
a5bd9f6
+		   cur->msg);
a5bd9f6
+    }
a5bd9f6
+ return 0;
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+static grub_command_t cmd_boottime;
a5bd9f6
+
a5bd9f6
+GRUB_MOD_INIT(boottime)
a5bd9f6
+{
a5bd9f6
+  cmd_boottime =
a5bd9f6
+    grub_register_command ("boottime", grub_cmd_boottime,
a5bd9f6
+			   0, N_("Get boot time statistics."));
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+GRUB_MOD_FINI(boottime)
a5bd9f6
+{
a5bd9f6
+  grub_unregister_command (cmd_boottime);
a5bd9f6
+}
a5bd9f6
diff --git a/grub-core/disk/usbms.c b/grub-core/disk/usbms.c
a5bd9f6
index dd35bff..2cfc537 100644
a5bd9f6
--- a/grub-core/disk/usbms.c
a5bd9f6
+++ b/grub-core/disk/usbms.c
a5bd9f6
@@ -151,6 +151,8 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
a5bd9f6
   unsigned curnum;
a5bd9f6
   grub_usb_err_t err = GRUB_ERR_NONE;
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Attaching USB mass storage");
a5bd9f6
+
a5bd9f6
   if (first_available_slot == ARRAY_SIZE (grub_usbms_devices))
a5bd9f6
     return 0;
a5bd9f6
 
a5bd9f6
@@ -246,6 +248,8 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
a5bd9f6
 
a5bd9f6
   usbdev->config[configno].interf[interfno].detach_hook = grub_usbms_detach;
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Attached USB mass storage");
a5bd9f6
+
a5bd9f6
 #if 0 /* All this part should be probably deleted.
a5bd9f6
        * This make trouble on some devices if they are not in
a5bd9f6
        * Phase Error state - and there they should be not in such state...
a5bd9f6
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
a5bd9f6
index 5b0aa65..d06b6ae 100644
a5bd9f6
--- a/grub-core/kern/dl.c
a5bd9f6
+++ b/grub-core/kern/dl.c
a5bd9f6
@@ -648,7 +648,10 @@ grub_dl_load_core (void *addr, grub_size_t size)
a5bd9f6
 
a5bd9f6
   grub_dprintf ("modules", "module name: %s\n", mod->name);
a5bd9f6
   grub_dprintf ("modules", "init function: %p\n", mod->init);
a5bd9f6
+
a5bd9f6
+  grub_boot_time ("Initing module %s", mod->name);
a5bd9f6
   grub_dl_call_init (mod);
a5bd9f6
+  grub_boot_time ("Module %s inited", mod->name);
a5bd9f6
 
a5bd9f6
   if (grub_dl_add (mod))
a5bd9f6
     {
a5bd9f6
diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
a5bd9f6
index e1a2001..19dc988 100644
a5bd9f6
--- a/grub-core/kern/main.c
a5bd9f6
+++ b/grub-core/kern/main.c
a5bd9f6
@@ -254,6 +254,8 @@ grub_main (void)
a5bd9f6
   /* First of all, initialize the machine.  */
a5bd9f6
   grub_machine_init ();
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("After machine init.");
a5bd9f6
+
a5bd9f6
   /* Hello.  */
a5bd9f6
   grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
a5bd9f6
   grub_printf ("Welcome to GRUB!\n\n");
a5bd9f6
@@ -261,6 +263,8 @@ grub_main (void)
a5bd9f6
 
a5bd9f6
   grub_load_config ();
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Before loading embedded modules.");
a5bd9f6
+
a5bd9f6
   /* Load pre-loaded modules and free the space.  */
a5bd9f6
   grub_register_exported_symbols ();
a5bd9f6
 #ifdef GRUB_LINKER_HAVE_INIT
a5bd9f6
@@ -268,6 +272,8 @@ grub_main (void)
a5bd9f6
 #endif  
a5bd9f6
   grub_load_modules ();
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("After loading embedded modules.");
a5bd9f6
+
a5bd9f6
   /* It is better to set the root device as soon as possible,
a5bd9f6
      for convenience.  */
a5bd9f6
   grub_set_prefix_and_root ();
a5bd9f6
@@ -277,11 +283,17 @@ grub_main (void)
a5bd9f6
   /* Reclaim space used for modules.  */
a5bd9f6
   reclaim_module_space ();
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("After reclaiming module space.");
a5bd9f6
+
a5bd9f6
   grub_register_core_commands ();
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Before execution of embedded config.");
a5bd9f6
+
a5bd9f6
   if (load_config)
a5bd9f6
     grub_parser_execute (load_config);
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("After execution of embedded config. Attempt to go to normal mode");
a5bd9f6
+
a5bd9f6
   grub_load_normal_mode ();
a5bd9f6
   grub_rescue_run ();
a5bd9f6
 }
a5bd9f6
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
a5bd9f6
index 6cb8f0e..94b88a3 100644
a5bd9f6
--- a/grub-core/kern/misc.c
a5bd9f6
+++ b/grub-core/kern/misc.c
a5bd9f6
@@ -1130,3 +1130,42 @@ void __deregister_frame_info (void)
a5bd9f6
 }
a5bd9f6
 #endif
a5bd9f6
 
a5bd9f6
+#if BOOT_TIME_STATS
a5bd9f6
+
a5bd9f6
+#include <grub/time.h>
a5bd9f6
+
a5bd9f6
+struct grub_boot_time *grub_boot_time_head;
a5bd9f6
+static struct grub_boot_time **boot_time_last = &grub_boot_time_head;
a5bd9f6
+
a5bd9f6
+void
a5bd9f6
+grub_real_boot_time (const char *file,
a5bd9f6
+		     const int line,
a5bd9f6
+		     const char *fmt, ...)
a5bd9f6
+{
a5bd9f6
+  struct grub_boot_time *n;
a5bd9f6
+  va_list args;
a5bd9f6
+
a5bd9f6
+  grub_error_push ();
a5bd9f6
+  n = grub_malloc (sizeof (*n));
a5bd9f6
+  if (!n)
a5bd9f6
+    {
a5bd9f6
+      grub_errno = 0;
a5bd9f6
+      grub_error_pop ();
a5bd9f6
+      return;
a5bd9f6
+    }
a5bd9f6
+  n->file = file;
a5bd9f6
+  n->line = line;
a5bd9f6
+  n->tp = grub_get_time_ms ();
a5bd9f6
+  n->next = 0;
a5bd9f6
+
a5bd9f6
+  va_start (args, fmt);
a5bd9f6
+  n->msg = grub_xvasprintf (fmt, args);    
a5bd9f6
+  va_end (args);
a5bd9f6
+
a5bd9f6
+  *boot_time_last = n;
a5bd9f6
+  boot_time_last = &n->next;
a5bd9f6
+
a5bd9f6
+  grub_errno = 0;
a5bd9f6
+  grub_error_pop ();
a5bd9f6
+}
a5bd9f6
+#endif
a5bd9f6
diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c
a5bd9f6
index 07f337d..9aaa3b2 100644
a5bd9f6
--- a/grub-core/normal/main.c
a5bd9f6
+++ b/grub-core/normal/main.c
a5bd9f6
@@ -296,6 +296,8 @@ grub_normal_execute (const char *config, int nested, int batch)
a5bd9f6
       grub_register_variable_hook ("prefix", NULL, read_lists_hook);
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Executing config file");
a5bd9f6
+
a5bd9f6
   if (config)
a5bd9f6
     {
a5bd9f6
       menu = read_config_file (config);
a5bd9f6
@@ -304,10 +306,14 @@ grub_normal_execute (const char *config, int nested, int batch)
a5bd9f6
       grub_errno = GRUB_ERR_NONE;
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Executed config file");
a5bd9f6
+
a5bd9f6
   if (! batch)
a5bd9f6
     {
a5bd9f6
       if (menu && menu->size)
a5bd9f6
 	{
a5bd9f6
+
a5bd9f6
+	  grub_boot_time ("Entering menu");
a5bd9f6
 	  grub_show_menu (menu, nested, 0);
a5bd9f6
 	  if (nested)
a5bd9f6
 	    grub_normal_free_menu (menu);
a5bd9f6
@@ -319,12 +325,15 @@ grub_normal_execute (const char *config, int nested, int batch)
a5bd9f6
 void
a5bd9f6
 grub_enter_normal_mode (const char *config)
a5bd9f6
 {
a5bd9f6
+  grub_boot_time ("Entering normal mode");
a5bd9f6
   nested_level++;
a5bd9f6
   grub_normal_execute (config, 0, 0);
a5bd9f6
+  grub_boot_time ("Entering shell");
a5bd9f6
   grub_cmdline_run (0);
a5bd9f6
   nested_level--;
a5bd9f6
   if (grub_normal_exit_level)
a5bd9f6
     grub_normal_exit_level--;
a5bd9f6
+  grub_boot_time ("Exiting normal mode");
a5bd9f6
 }
a5bd9f6
 
a5bd9f6
 /* Enter normal mode from rescue mode.  */
a5bd9f6
@@ -504,6 +513,8 @@ GRUB_MOD_INIT(normal)
a5bd9f6
 {
a5bd9f6
   unsigned i;
a5bd9f6
 
a5bd9f6
+  grub_boot_time ("Preparing normal module");
a5bd9f6
+
a5bd9f6
   /* Previously many modules depended on gzio. Be nice to user and load it.  */
a5bd9f6
   grub_dl_load ("gzio");
a5bd9f6
   grub_errno = 0;
a5bd9f6
@@ -556,6 +567,8 @@ GRUB_MOD_INIT(normal)
a5bd9f6
   grub_env_export ("grub_cpu");
a5bd9f6
   grub_env_set ("grub_platform", GRUB_PLATFORM);
a5bd9f6
   grub_env_export ("grub_platform");
a5bd9f6
+
a5bd9f6
+  grub_boot_time ("Normal module prepared");
a5bd9f6
 }
a5bd9f6
 
a5bd9f6
 GRUB_MOD_FINI(normal)
a5bd9f6
diff --git a/include/grub/misc.h b/include/grub/misc.h
a5bd9f6
index 11eeb22..f0ecaec 100644
a5bd9f6
--- a/include/grub/misc.h
a5bd9f6
+++ b/include/grub/misc.h
a5bd9f6
@@ -458,4 +458,24 @@ grub_error_load (const struct grub_error_saved *save)
a5bd9f6
   grub_errno = save->grub_errno;
a5bd9f6
 }
a5bd9f6
 
a5bd9f6
+#if BOOT_TIME_STATS
a5bd9f6
+struct grub_boot_time
a5bd9f6
+{
a5bd9f6
+  struct grub_boot_time *next;
a5bd9f6
+  grub_uint64_t tp;
a5bd9f6
+  const char *file;
a5bd9f6
+  int line;
a5bd9f6
+  char *msg;
a5bd9f6
+};
a5bd9f6
+
a5bd9f6
+extern struct grub_boot_time *EXPORT_VAR(grub_boot_time_head);
a5bd9f6
+
a5bd9f6
+void EXPORT_FUNC(grub_real_boot_time) (const char *file,
a5bd9f6
+				       const int line,
a5bd9f6
+				       const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
a5bd9f6
+#define grub_boot_time(fmt, args...) grub_real_boot_time(GRUB_FILE, __LINE__, fmt, ## args)
a5bd9f6
+#else
a5bd9f6
+#define grub_boot_time(fmt, args...) 
a5bd9f6
+#endif
a5bd9f6
+
a5bd9f6
 #endif /* ! GRUB_MISC_HEADER */
a5bd9f6
-- 
a5bd9f6
1.8.1.4
a5bd9f6