{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input",
     "active-ipynb",
     "remove-output"
    ]
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    from openmdao.utils.notebook_utils import notebook_mode  # noqa: F401\n",
    "except ImportError:\n",
    "    !python -m pip install openmdao[notebooks]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Instance-Based Call Tracing\n",
    "\n",
    "The `openmdao trace` command can be used to print a trace of each instance method call. For example:\n",
    "\n",
    "``` bash\n",
    "    openmdao trace <your_python_script_here>\n",
    "```\n",
    "\n",
    "Whenever a method is called that matches the search criteria, the following will be written to the console, indented based on its location in the call stack. :\n",
    "\n",
    "1. Pathname of the object instance (if available)\n",
    "2. Class\n",
    "3. Instance ID\n",
    "4. Method name\n",
    "\n",
    "For example:\n",
    "```\n",
    " Problem#1.Problem.__init__\n",
    "     Group#1.Group.__init__\n",
    "         Group#1.System.__init__\n",
    "             DictionaryJacobian#1.Jacobian.__init__\n",
    "             Group#1().System.initialize\n",
    "         NonlinearRunOnce#1.Solver.__init__\n",
    "             NonlinearRunOnce#1.Solver._declare_options\n",
    "         LinearRunOnce#1.LinearSolver.__init__\n",
    "             LinearRunOnce#1.Solver.__init__\n",
    "                 LinearRunOnce#1.Solver._declare_options\n",
    "     Driver#1.Driver.__init__\n",
    " IndepVarComp#1.IndepVarComp.__init__\n",
    "     IndepVarComp#1.ExplicitComponent.__init__\n",
    "         IndepVarComp#1.Component.__init__\n",
    "             IndepVarComp#1.System.__init__\n",
    "                 DictionaryJacobian#2.Jacobian.__init__\n",
    "                 IndepVarComp#1().System.initialize\n",
    " Propulsor#1.Group.__init__\n",
    "     Propulsor#1.System.__init__\n",
    "         DictionaryJacobian#3.Jacobian.__init__\n",
    "         Propulsor#1().System.initialize\n",
    "     NonlinearRunOnce#2.Solver.__init__\n",
    "         NonlinearRunOnce#2.Solver._declare_options\n",
    "     LinearRunOnce#2.LinearSolver.__init__\n",
    "         LinearRunOnce#2.Solver.__init__\n",
    "             LinearRunOnce#2.Solver._declare_options\n",
    " Problem#1.Problem.set_solver_print\n",
    "     Group#1().System.set_solver_print\n",
    "         LinearRunOnce#2.Solver._set_solver_print\n",
    "         NonlinearRunOnce#2.Solver._set_solver_print\n",
    " Problem#1.Problem.setup\n",
    "     Group#1().System._setup\n",
    "         Group#1().System._get_initial_procs\n",
    "         Group#1().Group._setup_procs\n",
    "             IndepVarComp#1().System._setup_procs\n",
    "             Propulsor#1().Group._setup_procs\n",
    "                 FlightConditions#1.Group.__init__\n",
    "                     FlightConditions#1.System.__init__\n",
    "                         DictionaryJacobian#4.Jacobian.__init__\n",
    "                         FlightConditions#1().FlightConditions.initialize\n",
    "                     NonlinearRunOnce#3.Solver.__init__\n",
    "                         NonlinearRunOnce#3.Solver._declare_options\n",
    "                     LinearRunOnce#3.LinearSolver.__init__\n",
    "                         LinearRunOnce#3.Solver.__init__\n",
    "                             LinearRunOnce#3.Solver._declare_options\n",
    "\n",
    "...\n",
    "```\n",
    "\n",
    "\n",
    "Note that we must always include the class name and instance ID, even when the instance has a pathname attribute, because there are times early in execution where either the pathname attribute doesn’t exist yet, as in the beginning of `__init__` method, or pathname exists but still has the default value of “” instead of its eventual value, as in the `_setup_procs` method.\n",
    "\n",
    "For more verbose output, which includes values of function locals and return values, as well as the number of times a function has been called, use the -*v* arg. For example:\n",
    "\n",
    "``` bash\n",
    "    openmdao trace -v <your_python_script_here>\n",
    "```\n",
    "\n",
    "Which will result in output that looks like this:\n",
    "\n",
    "```\n",
    "Problem#1.Problem.__init__ (1)\n",
    "  comm=None\n",
    "  model=None\n",
    "  root=None\n",
    "  self=<openmdao.core.problem.Problem object>\n",
    "    Group#1.Group.__init__ (1)\n",
    "      kwargs={}\n",
    "      self=<openmdao.core.group.Group object>\n",
    "        Group#1.System.__init__ (1)\n",
    "          kwargs={}\n",
    "          self=<openmdao.core.group.Group object>\n",
    "            DictionaryJacobian#1.Jacobian.__init__ (1)\n",
    "              kwargs={}\n",
    "              self=<openmdao.jacobians.dictionary_jacobian.DictionaryJacobian object>\n",
    "            <-- DictionaryJacobian#1.Jacobian.__init__\n",
    "            Group#1().System.initialize (1)\n",
    "              self=<openmdao.core.group.Group object>\n",
    "            <-- Group#1().System.initialize\n",
    "        <-- Group#1().System.__init__\n",
    "        NonlinearRunOnce#1.Solver.__init__ (1)\n",
    "          kwargs={}\n",
    "          self=NL: RUNONCE\n",
    "            NonlinearRunOnce#1.Solver._declare_options (1)\n",
    "              self=NL: RUNONCE\n",
    "            <-- NonlinearRunOnce#1.Solver._declare_options\n",
    "        <-- NonlinearRunOnce#1.Solver.__init__\n",
    "        LinearRunOnce#1.LinearSolver.__init__ (1)\n",
    "          kwargs={}\n",
    "          self=LN: RUNONCE\n",
    "            LinearRunOnce#1.Solver.__init__ (1)\n",
    "              kwargs={}\n",
    "              self=LN: RUNONCE\n",
    "                LinearRunOnce#1.Solver._declare_options (1)\n",
    "                  self=LN: RUNONCE\n",
    "                <-- LinearRunOnce#1.Solver._declare_options\n",
    "            <-- LinearRunOnce#1.Solver.__init__\n",
    "        <-- LinearRunOnce#1.LinearSolver.__init__\n",
    "    <-- Group#1().Group.__init__\n",
    "    Driver#1.Driver.__init__ (1)\n",
    "      self=<openmdao.core.driver.Driver object>\n",
    "    <-- Driver#1.Driver.__init__\n",
    "<-- Problem#1.Problem.__init__\n",
    "\n",
    "...\n",
    "```\n",
    "\n",
    "By default, a pre-defined set of general OpenMDAO functions will be included in the trace, but that can be changed using the -*g* option. For example, in order to trace only `setup`-related functions, do the following:\n",
    "\n",
    "``` bash\n",
    "    openmdao trace -v <your_python_script_here> -g setup\n",
    "```\n",
    "\n",
    "The tracer can also display the change in memory usage from the time a function is called to the time it returns. To show memory usage, use the -*m* option, for example:\n",
    "\n",
    "``` bash\n",
    "    openmdao trace -m <your_python_script_here>\n",
    "```    \n",
    "\n",
    "will result in output like this:\n",
    "\n",
    "``` \n",
    "...\n",
    "\n",
    "Group#1().Group._setup_procs\n",
    "    DistribOuptutImplicit#0().System._setup_procs\n",
    "        PETScKrylov#0.PETScKrylov.__init__\n",
    "            PETScKrylov#0.LinearSolver.__init__\n",
    "                PETScKrylov#0.Solver.__init__\n",
    "                    PETScKrylov#0.PETScKrylov._declare_options\n",
    "                    <-- PETScKrylov#0.PETScKrylov._declare_options (time:  0.06384) (total: 75.445 MB)\n",
    "                <-- PETScKrylov#0.Solver.__init__ (time:  0.06391) (total: 75.445 MB)\n",
    "            <-- PETScKrylov#0.LinearSolver.__init__ (time:  0.06397) (total: 75.445 MB)\n",
    "        <-- PETScKrylov#0.PETScKrylov.__init__ (time:  0.06402) (total: 75.445 MB)\n",
    "    <-- DistribOuptutImplicit#0(aero.icomp).System._setup_procs (time:  0.06519) (total: 77.371 MB) (diff: +4772 KB)\n",
    "    DistribInputExplicit#0().System._setup_procs\n",
    "    <-- DistribInputExplicit#0(aero.ecomp).System._setup_procs (time:  0.06738) (total: 79.281 MB) (diff: +1956 KB)\n",
    "<-- Group#1(aero).Group._setup_procs (time:  0.06746) (total: 79.281 MB)\n",
    "\n",
    "...\n",
    "```\n",
    "\n",
    "Note that total memory usage and elapsed time is shown on each function return line. Those function returns where a difference in memory was found will display the difference at the end of the line.\n",
    "\n",
    "The tracer can also be used to help track down memory leaks. Using the -*l* option, it will display a list of object types and their counts that were created since the function was called and not garbage collected after the function returned. For example:\n",
    "\n",
    "``` bash\n",
    "    openmdao trace -l <your_python_script_here> -g solver\n",
    "```\n",
    "\n",
    "will result in output like this:\n",
    "\n",
    "```\n",
    "...\n",
    "\n",
    "LinearRunOnce#0.LinearRunOnce.solve\n",
    "    LinearRunOnce#0.LinearBlockGS._iter_execute\n",
    "        LinearRunOnce#1.LinearRunOnce.solve\n",
    "            LinearRunOnce#1.LinearBlockGS._iter_execute\n",
    "                PETScKrylov#0.PETScKrylov.solve\n",
    "                <-- PETScKrylov#0.PETScKrylov.solve\n",
    "            <-- LinearRunOnce#1.LinearBlockGS._iter_execute\n",
    "        <-- LinearRunOnce#1.LinearRunOnce.solve\n",
    "           Recording +1\n",
    "    <-- LinearRunOnce#0.LinearBlockGS._iter_execute\n",
    "<-- LinearRunOnce#0.LinearRunOnce.solve\n",
    "   Recording +1\n",
    "\n",
    "...\n",
    "```\n",
    "\n",
    "This output shows that in LinearRunOnce\\#1.LinearRunOnce.solve, a Recording object was created and not garbage collected. Note that this does not always indicate a memory leak, as there are some functions that intentionally create new objects that are intended to last beyond the life of the function. This tool merely gives you a place to look in the code where a memory leak *might* exist.\n",
    "\n",
    "To see a list of the available pre-defined sets of functions to trace, look at the usage info for the -*g* command that can be obtained as follows:\n",
    "\n",
    "``` bash\n",
    "    openmdao trace -h\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "remove-input"
    ]
   },
   "outputs": [],
   "source": [
    "!openmdao trace -h"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  },
  "orphan": true
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
