sim: cris/frv/h8300/iq2000/lm32/m32r/sh64: standardize cpu state
[deliverable/binutils-gdb.git] / gdb / python / py-inferior.c
index afc3eb0c995d22ce732bf39173cc7f19131d9e9c..fe8a705cd7618613b9d68fdf0793d95b0664695b 100644 (file)
@@ -1,6 +1,6 @@
 /* Python interface to inferiors.
 
-   Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2009-2015 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -18,7 +18,6 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
-#include "exceptions.h"
 #include "gdbcore.h"
 #include "gdbthread.h"
 #include "inferior.h"
@@ -51,7 +50,8 @@ typedef struct
   int nthreads;
 } inferior_object;
 
-static PyTypeObject inferior_object_type;
+extern PyTypeObject inferior_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("inferior_object");
 
 static const struct inferior_data *infpy_inf_data_key;
 
@@ -64,7 +64,8 @@ typedef struct {
   CORE_ADDR length;
 } membuf_object;
 
-static PyTypeObject membuf_object_type;
+extern PyTypeObject membuf_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("membuf_object");
 
 /* Require that INFERIOR be a valid inferior ID.  */
 #define INFPY_REQUIRE_VALID(Inferior)                          \
@@ -81,7 +82,10 @@ static void
 python_on_normal_stop (struct bpstats *bs, int print_frame)
 {
   struct cleanup *cleanup;
-  enum target_signal stop_signal;
+  enum gdb_signal stop_signal;
+
+  if (!gdb_python_initialized)
+    return;
 
   if (!find_thread_ptid (inferior_ptid))
       return;
@@ -101,7 +105,10 @@ python_on_resume (ptid_t ptid)
 {
   struct cleanup *cleanup;
 
-  cleanup = ensure_python_env (target_gdbarch, current_language);
+  if (!gdb_python_initialized)
+    return;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
 
   if (emit_continue_event (ptid) < 0)
     gdbpy_print_stack ();
@@ -109,13 +116,82 @@ python_on_resume (ptid_t ptid)
   do_cleanups (cleanup);
 }
 
+/* Callback, registered as an observer, that notifies Python listeners
+   when an inferior function call is about to be made. */
+
+static void
+python_on_inferior_call_pre (ptid_t thread, CORE_ADDR address)
+{
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
+
+  if (emit_inferior_call_event (INFERIOR_CALL_PRE, thread, address) < 0)
+    gdbpy_print_stack ();
+
+  do_cleanups (cleanup);
+}
+
+/* Callback, registered as an observer, that notifies Python listeners
+   when an inferior function call has completed. */
+
+static void
+python_on_inferior_call_post (ptid_t thread, CORE_ADDR address)
+{
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
+
+  if (emit_inferior_call_event (INFERIOR_CALL_POST, thread, address) < 0)
+    gdbpy_print_stack ();
+
+  do_cleanups (cleanup);
+}
+
+/* Callback, registered as an observer, that notifies Python listeners
+   when a part of memory has been modified by user action (eg via a
+   'set' command). */
+
+static void
+python_on_memory_change (struct inferior *inferior, CORE_ADDR addr, ssize_t len, const bfd_byte *data)
+{
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
+
+  if (emit_memory_changed_event (addr, len) < 0)
+    gdbpy_print_stack ();
+
+  do_cleanups (cleanup);
+}
+
+/* Callback, registered as an observer, that notifies Python listeners
+   when a register has been modified by user action (eg via a 'set'
+   command). */
+
+static void
+python_on_register_change (struct frame_info *frame, int regnum)
+{
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
+
+  if (emit_register_changed_event (frame, regnum) < 0)
+    gdbpy_print_stack ();
+
+  do_cleanups (cleanup);
+}
+
 static void
 python_inferior_exit (struct inferior *inf)
 {
   struct cleanup *cleanup;
   const LONGEST *exit_code = NULL;
 
-  cleanup = ensure_python_env (target_gdbarch, current_language);
+  if (!gdb_python_initialized)
+    return;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
 
   if (inf->has_exit_code)
     exit_code = &inf->exit_code;
@@ -127,20 +203,32 @@ python_inferior_exit (struct inferior *inf)
 }
 
 /* Callback used to notify Python listeners about new objfiles loaded in the
-   inferior.  */
+   inferior.  OBJFILE may be NULL which means that the objfile list has been
+   cleared (emptied).  */
 
 static void
 python_new_objfile (struct objfile *objfile)
 {
   struct cleanup *cleanup;
 
-  if (objfile == NULL)
+  if (!gdb_python_initialized)
     return;
 
-  cleanup = ensure_python_env (get_objfile_arch (objfile), current_language);
+  cleanup = ensure_python_env (objfile != NULL
+                              ? get_objfile_arch (objfile)
+                              : target_gdbarch (),
+                              current_language);
 
-  if (emit_new_objfile_event (objfile) < 0)
-    gdbpy_print_stack ();
+  if (objfile == NULL)
+    {
+      if (emit_clear_objfiles_event () < 0)
+       gdbpy_print_stack ();
+    }
+  else
+    {
+      if (emit_new_objfile_event (objfile) < 0)
+       gdbpy_print_stack ();
+    }
 
   do_cleanups (cleanup);
 }
@@ -157,15 +245,9 @@ inferior_to_inferior_object (struct inferior *inferior)
   inf_obj = inferior_data (inferior, infpy_inf_data_key);
   if (!inf_obj)
     {
-      struct cleanup *cleanup;
-      cleanup = ensure_python_env (python_gdbarch, python_language);
-
       inf_obj = PyObject_New (inferior_object, &inferior_object_type);
       if (!inf_obj)
-       {
-         do_cleanups (cleanup);
          return NULL;
-       }
 
       inf_obj->inferior = inferior;
       inf_obj->threads = NULL;
@@ -173,7 +255,6 @@ inferior_to_inferior_object (struct inferior *inferior)
 
       set_inferior_data (inferior, infpy_inf_data_key, inf_obj);
 
-      do_cleanups (cleanup);
     }
   else
     Py_INCREF ((PyObject *)inf_obj);
@@ -187,7 +268,6 @@ inferior_to_inferior_object (struct inferior *inferior)
 PyObject *
 find_inferior_object (int pid)
 {
-  struct inflist_entry *p;
   struct inferior *inf = find_inferior_pid (pid);
 
   if (inf)
@@ -204,7 +284,7 @@ find_thread_object (ptid_t ptid)
   PyObject *inf_obj;
   thread_object *found = NULL;
 
-  pid = PIDGET (ptid);
+  pid = ptid_get_pid (ptid);
   if (pid == 0)
     return NULL;
 
@@ -237,6 +317,9 @@ add_thread_object (struct thread_info *tp)
   inferior_object *inf_obj;
   struct threadlist_entry *entry;
 
+  if (!gdb_python_initialized)
+    return;
+
   cleanup = ensure_python_env (python_gdbarch, python_language);
 
   thread_obj = create_thread_object (tp);
@@ -264,13 +347,21 @@ delete_thread_object (struct thread_info *tp, int ignore)
 {
   struct cleanup *cleanup;
   inferior_object *inf_obj;
-  thread_object *thread_obj;
   struct threadlist_entry **entry, *tmp;
 
-  inf_obj = (inferior_object *) find_inferior_object (PIDGET(tp->ptid));
-  if (!inf_obj)
+  if (!gdb_python_initialized)
     return;
 
+  cleanup = ensure_python_env (python_gdbarch, python_language);
+
+  inf_obj
+    = (inferior_object *) find_inferior_object (ptid_get_pid (tp->ptid));
+  if (!inf_obj)
+    {
+      do_cleanups (cleanup);
+      return;
+    }
+
   /* Find thread entry in its inferior's thread_list.  */
   for (entry = &inf_obj->threads; *entry != NULL; entry =
         &(*entry)->next)
@@ -280,11 +371,10 @@ delete_thread_object (struct thread_info *tp, int ignore)
   if (!*entry)
     {
       Py_DECREF (inf_obj);
+      do_cleanups (cleanup);
       return;
     }
 
-  cleanup = ensure_python_env (python_gdbarch, python_language);
-
   tmp = *entry;
   tmp->thread_obj->thread = NULL;
 
@@ -308,6 +398,16 @@ infpy_threads (PyObject *self, PyObject *args)
 
   INFPY_REQUIRE_VALID (inf_obj);
 
+  TRY
+    {
+      update_thread_list ();
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
   tuple = PyTuple_New (inf_obj->nthreads);
   if (!tuple)
     return NULL;
@@ -377,9 +477,7 @@ build_inferior_list (struct inferior *inf, void *arg)
 PyObject *
 gdbpy_inferiors (PyObject *unused, PyObject *unused2)
 {
-  int i = 0;
-  PyObject *list, *inferior;
-  struct inferior *inf;
+  PyObject *list, *tuple;
 
   list = PyList_New (0);
   if (!list)
@@ -391,79 +489,71 @@ gdbpy_inferiors (PyObject *unused, PyObject *unused2)
       return NULL;
     }
 
-  return PyList_AsTuple (list);
+  tuple = PyList_AsTuple (list);
+  Py_DECREF (list);
+
+  return tuple;
 }
 
 /* Membuf and memory manipulation.  */
 
-/* Implementation of gdb.read_memory (address, length).
+/* Implementation of Inferior.read_memory (address, length).
    Returns a Python buffer object with LENGTH bytes of the inferior's
    memory at ADDRESS.  Both arguments are integers.  Returns NULL on error,
    with a python exception set.  */
 static PyObject *
 infpy_read_memory (PyObject *self, PyObject *args, PyObject *kw)
 {
-  int error = 0;
   CORE_ADDR addr, length;
   void *buffer = NULL;
   membuf_object *membuf_obj;
-  PyObject *addr_obj, *length_obj;
-  struct cleanup *cleanups;
-  volatile struct gdb_exception except;
+  PyObject *addr_obj, *length_obj, *result;
   static char *keywords[] = { "address", "length", NULL };
 
   if (! PyArg_ParseTupleAndKeywords (args, kw, "OO", keywords,
                                     &addr_obj, &length_obj))
     return NULL;
 
-  cleanups = make_cleanup (null_cleanup, NULL);
+  if (get_addr_from_python (addr_obj, &addr) < 0
+      || get_addr_from_python (length_obj, &length) < 0)
+    return NULL;
 
-  TRY_CATCH (except, RETURN_MASK_ALL)
+  TRY
     {
-      if (!get_addr_from_python (addr_obj, &addr)
-         || !get_addr_from_python (length_obj, &length))
-       {
-         error = 1;
-         break;
-       }
-
       buffer = xmalloc (length);
-      make_cleanup (xfree, buffer);
 
       read_memory (addr, buffer, length);
     }
-  if (except.reason < 0)
+  CATCH (except, RETURN_MASK_ALL)
     {
-      do_cleanups (cleanups);
+      xfree (buffer);
       GDB_PY_HANDLE_EXCEPTION (except);
     }
-
-  if (error)
-    {
-      do_cleanups (cleanups);
-      return NULL;
-    }
+  END_CATCH
 
   membuf_obj = PyObject_New (membuf_object, &membuf_object_type);
   if (membuf_obj == NULL)
     {
-      PyErr_SetString (PyExc_MemoryError,
-                      _("Could not allocate memory buffer object."));
-      do_cleanups (cleanups);
+      xfree (buffer);
       return NULL;
     }
 
-  discard_cleanups (cleanups);
-
   membuf_obj->buffer = buffer;
   membuf_obj->addr = addr;
   membuf_obj->length = length;
 
-  return PyBuffer_FromReadWriteObject ((PyObject *) membuf_obj, 0,
-                                      Py_END_OF_BUFFER);
+#ifdef IS_PY3K
+  result = PyMemoryView_FromObject ((PyObject *) membuf_obj);
+#else
+  result = PyBuffer_FromReadWriteObject ((PyObject *) membuf_obj, 0,
+                                        Py_END_OF_BUFFER);
+#endif
+  Py_DECREF (membuf_obj);
+
+  return result;
 }
 
-/* Implementation of gdb.write_memory (address, buffer [, length]).
+/* Implementation of Inferior.write_memory (address, buffer [, length]).
    Writes the contents of BUFFER (a Python object supporting the read
    buffer protocol) at ADDRESS in the inferior's memory.  Write LENGTH
    bytes from BUFFER, or its entire contents if the argument is not
@@ -472,43 +562,59 @@ infpy_read_memory (PyObject *self, PyObject *args, PyObject *kw)
 static PyObject *
 infpy_write_memory (PyObject *self, PyObject *args, PyObject *kw)
 {
+  struct gdb_exception except = exception_none;
   Py_ssize_t buf_len;
-  int error = 0;
   const char *buffer;
   CORE_ADDR addr, length;
   PyObject *addr_obj, *length_obj = NULL;
-  volatile struct gdb_exception except;
   static char *keywords[] = { "address", "buffer", "length", NULL };
+#ifdef IS_PY3K
+  Py_buffer pybuf;
 
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "Os*|O", keywords,
+                                    &addr_obj, &pybuf,
+                                    &length_obj))
+    return NULL;
 
+  buffer = pybuf.buf;
+  buf_len = pybuf.len;
+#else
   if (! PyArg_ParseTupleAndKeywords (args, kw, "Os#|O", keywords,
                                     &addr_obj, &buffer, &buf_len,
                                     &length_obj))
     return NULL;
+#endif
+
+  if (get_addr_from_python (addr_obj, &addr) < 0)
+    goto fail;
 
-  TRY_CATCH (except, RETURN_MASK_ALL)
+  if (!length_obj)
+    length = buf_len;
+  else if (get_addr_from_python (length_obj, &length) < 0)
+    goto fail;
+
+  TRY
     {
-      if (!get_addr_from_python (addr_obj, &addr))
-       {
-         error = 1;
-         break;
-       }
-
-      if (!length_obj)
-       length = buf_len;
-      else if (!get_addr_from_python (length_obj, &length))
-       {
-         error = 1;
-         break;
-       }
-      write_memory (addr, buffer, length);
+      write_memory_with_notification (addr, (gdb_byte *) buffer, length);
     }
-  GDB_PY_HANDLE_EXCEPTION (except);
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+      except = ex;
+    }
+  END_CATCH
 
-  if (error)
-    return NULL;
+#ifdef IS_PY3K
+  PyBuffer_Release (&pybuf);
+#endif
+  GDB_PY_HANDLE_EXCEPTION (except);
 
   Py_RETURN_NONE;
+
+ fail:
+#ifdef IS_PY3K
+  PyBuffer_Release (&pybuf);
+#endif
+  return NULL;
 }
 
 /* Destructor of Membuf objects.  */
@@ -516,7 +622,7 @@ static void
 mbpy_dealloc (PyObject *self)
 {
   xfree (((membuf_object *) self)->buffer);
-  self->ob_type->tp_free (self);
+  Py_TYPE (self)->tp_free (self);
 }
 
 /* Return a description of the Membuf object.  */
@@ -531,6 +637,24 @@ which is %s bytes long."),
                              pulongest (membuf_obj->length));
 }
 
+#ifdef IS_PY3K
+
+static int
+get_buffer (PyObject *self, Py_buffer *buf, int flags)
+{
+  membuf_object *membuf_obj = (membuf_object *) self;
+  int ret;
+
+  ret = PyBuffer_FillInfo (buf, self, membuf_obj->buffer,
+                          membuf_obj->length, 0,
+                          PyBUF_CONTIG);
+  buf->format = "c";
+
+  return ret;
+}
+
+#else
+
 static Py_ssize_t
 get_read_buffer (PyObject *self, Py_ssize_t segment, void **ptrptr)
 {
@@ -575,6 +699,8 @@ get_char_buffer (PyObject *self, Py_ssize_t segment, char **ptrptr)
   return ret;
 }
 
+#endif /* IS_PY3K */
+
 /* Implementation of
    gdb.search_memory (address, length, pattern).  ADDRESS is the
    address to start the search.  LENGTH specifies the scope of the
@@ -586,41 +712,31 @@ get_char_buffer (PyObject *self, Py_ssize_t segment, char **ptrptr)
 static PyObject *
 infpy_search_memory (PyObject *self, PyObject *args, PyObject *kw)
 {
+  struct gdb_exception except = exception_none;
   CORE_ADDR start_addr, length;
   static char *keywords[] = { "address", "length", "pattern", NULL };
-  PyObject *pattern, *start_addr_obj, *length_obj;
-  volatile struct gdb_exception except;
+  PyObject *start_addr_obj, *length_obj;
   Py_ssize_t pattern_size;
   const void *buffer;
   CORE_ADDR found_addr;
   int found = 0;
+#ifdef IS_PY3K
+  Py_buffer pybuf;
 
-  if (! PyArg_ParseTupleAndKeywords (args, kw, "OOO", keywords,
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "OOs*", keywords,
                                     &start_addr_obj, &length_obj,
-                                    &pattern))
+                                    &pybuf))
     return NULL;
 
-  if (get_addr_from_python (start_addr_obj, &start_addr)
-      && get_addr_from_python (length_obj, &length))
-    {
-      if (!length)
-       {
-         PyErr_SetString (PyExc_ValueError,
-                          _("Search range is empty."));
-         return NULL;
-       }
-      /* Watch for overflows.  */
-      else if (length > CORE_ADDR_MAX
-              || (start_addr + length - 1) < start_addr)
-       {
-         PyErr_SetString (PyExc_ValueError,
-                          _("The search range is too large."));
+  buffer = pybuf.buf;
+  pattern_size = pybuf.len;
+#else
+  PyObject *pattern;
 
-         return NULL;
-       }
-    }
-  else
-    return NULL;
+  if (! PyArg_ParseTupleAndKeywords (args, kw, "OOO", keywords,
+                                    &start_addr_obj, &length_obj,
+                                    &pattern))
+     return NULL;
 
   if (!PyObject_CheckReadBuffer (pattern))
     {
@@ -632,19 +748,56 @@ infpy_search_memory (PyObject *self, PyObject *args, PyObject *kw)
 
   if (PyObject_AsReadBuffer (pattern, &buffer, &pattern_size) == -1)
     return NULL;
+#endif
+
+  if (get_addr_from_python (start_addr_obj, &start_addr) < 0)
+    goto fail;
+
+  if (get_addr_from_python (length_obj, &length) < 0)
+    goto fail;
+
+  if (!length)
+    {
+      PyErr_SetString (PyExc_ValueError,
+                      _("Search range is empty."));
+      goto fail;
+    }
+  /* Watch for overflows.  */
+  else if (length > CORE_ADDR_MAX
+          || (start_addr + length - 1) < start_addr)
+    {
+      PyErr_SetString (PyExc_ValueError,
+                      _("The search range is too large."));
+      goto fail;
+    }
 
-  TRY_CATCH (except, RETURN_MASK_ALL)
+  TRY
     {
       found = target_search_memory (start_addr, length,
                                    buffer, pattern_size,
                                    &found_addr);
     }
+  CATCH (ex, RETURN_MASK_ALL)
+    {
+      except = ex;
+    }
+  END_CATCH
+
+#ifdef IS_PY3K
+  PyBuffer_Release (&pybuf);
+#endif
   GDB_PY_HANDLE_EXCEPTION (except);
 
   if (found)
     return PyLong_FromLong (found_addr);
   else
     Py_RETURN_NONE;
+
+ fail:
+#ifdef IS_PY3K
+  PyBuffer_Release (&pybuf);
+#endif
+  return NULL;
 }
 
 /* Implementation of gdb.Inferior.is_valid (self) -> Boolean.
@@ -683,6 +836,9 @@ py_free_inferior (struct inferior *inf, void *datum)
   inferior_object *inf_obj = datum;
   struct threadlist_entry *th_entry, *th_tmp;
 
+  if (!gdb_python_initialized)
+    return;
+
   cleanup = ensure_python_env (python_gdbarch, python_language);
 
   inf_obj->inferior = NULL;
@@ -709,41 +865,39 @@ py_free_inferior (struct inferior *inf, void *datum)
 PyObject *
 gdbpy_selected_inferior (PyObject *self, PyObject *args)
 {
-  PyObject *inf_obj;
-
-  inf_obj = inferior_to_inferior_object (current_inferior ());
-  Py_INCREF (inf_obj);
-
-  return inf_obj;
+  return inferior_to_inferior_object (current_inferior ());
 }
 
-void
+int
 gdbpy_initialize_inferior (void)
 {
   if (PyType_Ready (&inferior_object_type) < 0)
-    return;
+    return -1;
 
-  Py_INCREF (&inferior_object_type);
-  PyModule_AddObject (gdb_module, "Inferior",
-                     (PyObject *) &inferior_object_type);
+  if (gdb_pymodule_addobject (gdb_module, "Inferior",
+                             (PyObject *) &inferior_object_type) < 0)
+    return -1;
 
   infpy_inf_data_key =
-    register_inferior_data_with_cleanup (py_free_inferior);
+    register_inferior_data_with_cleanup (NULL, py_free_inferior);
 
   observer_attach_new_thread (add_thread_object);
   observer_attach_thread_exit (delete_thread_object);
   observer_attach_normal_stop (python_on_normal_stop);
   observer_attach_target_resumed (python_on_resume);
+  observer_attach_inferior_call_pre (python_on_inferior_call_pre);
+  observer_attach_inferior_call_post (python_on_inferior_call_post);
+  observer_attach_memory_changed (python_on_memory_change);
+  observer_attach_register_changed (python_on_register_change);
   observer_attach_inferior_exit (python_inferior_exit);
   observer_attach_new_objfile (python_new_objfile);
 
   membuf_object_type.tp_new = PyType_GenericNew;
   if (PyType_Ready (&membuf_object_type) < 0)
-    return;
+    return -1;
 
-  Py_INCREF (&membuf_object_type);
-  PyModule_AddObject (gdb_module, "Membuf", (PyObject *)
-                     &membuf_object_type);
+  return gdb_pymodule_addobject (gdb_module, "Membuf", (PyObject *)
+                                &membuf_object_type);
 }
 
 static PyGetSetDef inferior_object_getset[] =
@@ -778,10 +932,9 @@ Return a long with the address of a match, or None." },
   { NULL }
 };
 
-static PyTypeObject inferior_object_type =
+PyTypeObject inferior_object_type =
 {
-  PyObject_HEAD_INIT (NULL)
-  0,                             /* ob_size */
+  PyVarObject_HEAD_INIT (NULL, 0)
   "gdb.Inferior",                /* tp_name */
   sizeof (inferior_object),      /* tp_basicsize */
   0,                             /* tp_itemsize */
@@ -820,6 +973,15 @@ static PyTypeObject inferior_object_type =
   0                              /* tp_alloc */
 };
 
+#ifdef IS_PY3K
+
+static PyBufferProcs buffer_procs =
+{
+  get_buffer
+};
+
+#else
+
 /* Python doesn't provide a decent way to get compatibility here.  */
 #if HAVE_LIBPYTHON2_4
 #define CHARBUFFERPROC_NAME getcharbufferproc
@@ -835,10 +997,10 @@ static PyBufferProcs buffer_procs = {
      Python 2.5.  */
   (CHARBUFFERPROC_NAME) get_char_buffer
 };
+#endif /* IS_PY3K */
 
-static PyTypeObject membuf_object_type = {
-  PyObject_HEAD_INIT (NULL)
-  0,                             /*ob_size*/
+PyTypeObject membuf_object_type = {
+  PyVarObject_HEAD_INIT (NULL, 0)
   "gdb.Membuf",                          /*tp_name*/
   sizeof (membuf_object),        /*tp_basicsize*/
   0,                             /*tp_itemsize*/
This page took 0.031115 seconds and 4 git commands to generate.