changeset 104426:7b59e774c522

cedet/semantic/elp.el: New file.
author Chong Yidong <cyd@stupidchicken.com>
date Sat, 29 Aug 2009 22:15:12 +0000
parents 4d608806c00b
children 9d5ed9214322
files lisp/cedet/semantic/elp.el
diffstat 1 files changed, 772 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/cedet/semantic/elp.el	Sat Aug 29 22:15:12 2009 +0000
@@ -0,0 +1,772 @@
+;;; semantic-elp.el --- Bind ELP to measure Semantic
+
+;; Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+
+;; Author: Eric M. Ludlam <eric@siege-engine.com>
+;; X-RCS: $Id: semantic-elp.el,v 1.16 2009/04/02 01:18:33 zappo Exp $
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Provide fast ways to profile various (often slow) Semantic processes.
+
+(require 'elp)
+(require 'data-debug)
+(require 'semantic/adebug)
+(require 'semantic/tag-ls)
+(require 'semantic/tag-file)
+(require 'semantic/db)
+(require 'semantic/db-find)
+(require 'semantic/db-typecache)
+(require 'semantic/scope)
+(require 'semantic/analyze/fcn)
+(require 'semantic/analyze)
+(require 'semantic/analyze/complete)
+
+;;; Code:
+(defvar semantic-elp-emacs-core-list
+  '(
+    append
+    copy-sequence
+    expand-file-name
+    file-exists-p
+    file-name-directory
+    file-name-nondirectory
+    file-attributes
+    file-truename
+    find-buffer-visiting
+    length
+    locate-file
+    nconc
+    nreverse
+    sort
+    string<
+    string=
+    )
+  "List of Emacs functions for profiling.")
+
+(defvar semantic-elp-eieio-core-list
+  '(
+    eieio-generic-call
+    eieio-generic-call-primary-only
+    eieiomt-method-list
+    eieio-generic-form
+    eieio-oref
+    eieio-oset
+    obj-of-class-p
+    )
+  "List of EIEIO functions for profiling.")
+
+(defvar semantic-elp-ede-core-list
+  '(
+    ede-current-project
+    ede-directory-get-open-project
+    ede-expand-filename
+    ede-expand-filename-impl
+    ede-locate-file-in-project
+    ede-locate-file-in-project-impl
+    ede-system-include-path
+    ede-toplevel
+    ede-toplevel-project
+    ede-directory-project-p
+    )
+  "List of EDE functions to watch out for.")
+
+(defvar semantic-elp-semantic-core-list
+  '(
+    semantic-ctxt-current-argument
+    semantic-ctxt-current-assignment
+    semantic-ctxt-current-class-list
+    semantic-ctxt-current-function
+    semantic-ctxt-current-symbol-and-bounds
+    semantic-current-tag
+    semantic-dependency-tag-file
+    semantic-equivalent-tag-p
+    semantic-fetch-tags
+    semantic-fetch-tags-fast
+    semantic-find-tag-by-overlay
+    semantic-sort-tags-by-name-decreasing
+    semantic-sort-tags-by-name-increasing
+    semantic-sort-tags-by-name-then-type-increasing
+    semantic-sort-tags-by-type-decreasing
+    semantic-sort-tags-by-type-increasing
+    semantic-tag-clone
+    semantic-tag-components
+    semantic-tag-copy
+    semantic-tag-external-member-children
+    semantic-tag-file-name
+    semantic-tag-function-arguments
+    semantic-tag-function-parent
+    semantic-tag-get-attribute
+    semantic-tag-in-buffer-p
+    semantic-tag-include-filename
+    ;;semantic-tag-lessp-name-then-type
+    semantic-tag-name
+    semantic-tag-new-type
+    semantic-tag-of-class-p
+    semantic-tag-of-type-p
+    semantic-tag-of-type-p
+    semantic-tag-p
+    semantic-tag-prototype-p
+    semantic-tag-set-faux
+    semantic-tag-type
+    semantic-tag-type-members
+    semantic-tag-type-superclasses
+    semantic-tag-with-position-p
+    )
+  "List of core Semantic functions for profiling.")
+(defvar semantic-elp-semantic-find-core-list
+  '(
+    semantic-find-tags-by-class
+    semantic-find-tags-by-name
+    semantic-find-tags-by-name-regexp
+    semantic-find-tags-by-scope-protection
+    semantic-find-tags-by-type
+    semantic-find-tags-for-completion
+    semantic-find-tags-included
+    semantic-find-tags-of-compound-type
+    )
+  "List of semantic-find routines for profiling.")
+
+(defvar semantic-elp-semanticdb-core-list
+  '(
+    semanticdb-cache-get
+    semanticdb-current-database-list
+    semanticdb-file-table
+    semanticdb-file-table-object
+    semanticdb-full-filename
+    semanticdb-get-buffer
+    semanticdb-get-table-index
+    semanticdb-refresh-references
+    semanticdb-refresh-table
+    semanticdb-needs-refresh-p
+    semanticdb-directory-loaded-p
+    semanticdb-full-filename
+    semanticdb-create-table-for-file
+    )
+  "List of core Semanticdb functions for profiling.")
+
+(defvar semantic-elp-include-path-list
+  '(
+    semanticdb-find-incomplete-cache-entries-p
+    semanticdb-find-load-unloaded
+    semanticdb-find-table-for-include
+    semanticdb-find-throttle-active-p
+    semanticdb-find-translate-path-default
+    semanticdb-find-translate-path-brutish-default
+    semanticdb-find-translate-path-includes--internal
+    semanticdb-find-translate-path-includes-default
+    )
+  "List of include path calculation functions for profiling.")
+
+(defvar semantic-elp-semanticdb-find-list
+  '(
+    semanticdb-fast-strip-find-results
+    semanticdb-find-results-p
+    semanticdb-find-tags-by-class
+    semanticdb-find-tags-by-name
+    semanticdb-find-tags-by-name-regexp
+    semanticdb-find-tags-collector
+    semanticdb-find-tags-external-children-of-type
+    semanticdb-find-tags-for-completion
+    semanticdb-strip-find-results
+    )
+  "List of semanticdb find functions to profile.
+You may also need `semantic-elp-include-path-list'.")
+
+(defun semantic-elp-core-enable ()
+  "Do an ELP reset, and enable profiling of the core system."
+  (elp-reset-all)
+  (elp-instrument-list semantic-elp-emacs-core-list)
+  (elp-instrument-list semantic-elp-eieio-core-list)
+  (elp-instrument-list semantic-elp-ede-core-list)
+  (elp-instrument-list semantic-elp-semantic-core-list)
+  (elp-instrument-list semantic-elp-semanticdb-core-list)
+  (elp-instrument-list semantic-elp-semanticdb-find-list)
+  (elp-instrument-list semantic-elp-include-path-list)
+  )
+
+
+(defun semantic-elp-include-path-enable ()
+  "Enable profiling for `semanticdb-find-translate-path'."
+  (semantic-elp-core-enable)
+  (elp-set-master 'semanticdb-find-translate-path-default)
+  )
+
+(defvar semantic-elp-typecache-list
+  '(
+    semantic-analyze-split-name
+    semanticdb-get-typecache
+    semanticdb-typecache-merge-streams
+    semanticdb-typecache-safe-tag-members
+    semanticdb-typecache-apply-filename
+    semanticdb-typecache-file-tags
+    semanticdb-typecache-include-tags
+    )
+  "List of typecaching functions for profiling.")
+
+(defun semantic-elp-profile-typecache (tab)
+  "Profile the typecache.  Start with table TAB."
+  (let ((tc (semanticdb-get-typecache tab)))
+    (semanticdb-typecache-file-tags tab)
+    (semanticdb-typecache-include-tags tab)
+    tc))
+
+(defun semantic-elp-typecache-enable ()
+  "Enable profiling for `semanticdb-get-typecache'."
+  (semantic-elp-include-path-enable)
+  (elp-instrument-list semantic-elp-typecache-list)
+  (elp-set-master 'semantic-elp-profile-typecache)
+  )
+
+(defvar semantic-elp-scope-list
+  '(
+    semantic-analyze-find-tag
+    semantic-analyze-scope-nested-tags
+    semantic-analyze-scoped-types
+    semantic-analyze-scoped-types
+    semantic-analyze-tag-prototype-p
+    semantic-analyze-scoped-type-parts
+    semantic-calculate-scope
+    semantic-ctxt-scoped-types
+    semantic-get-all-local-variables
+    semantic-scope-find
+    semanticdb-typecache-find
+    semanticdb-typecache-merge-streams
+    )
+  "List of scope calculation functions for profiling.")
+
+(defun semantic-elp-scope-enable ()
+  "Enable profiling for `semanticdb-calculate-scope'."
+  (semantic-elp-core-enable)
+  (elp-instrument-list semantic-elp-typecache-list)
+  (elp-instrument-list semantic-elp-scope-list)
+  (elp-set-master 'semantic-calculate-scope)
+  )
+
+(defvar semantic-elp-analyze-list
+  '(
+    semantic-analyze-current-symbol
+    semantic-analyze-current-context
+    semantic-analyze-dereference-metatype
+    semantic-analyze-find-tag-sequence
+    semantic-analyze-interesting-tag
+    semantic-analyze-pop-to-context
+    semantic-analyze-select-best-tag
+    semantic-analyze-tag-type
+    semantic-analyze-type-to-name
+    semantic-analyze-type-constraint
+    semantic-analyze-scoped-type-parts
+    semantic-cache-data-to-buffer
+    )
+  "List of analyzer calculation functions for profiling.")
+
+(defun semantic-elp-analyze-enable ()
+  "Enable profiling for `semanticdb-analyze-current-context'."
+  (semantic-elp-scope-enable)
+  (elp-instrument-list semantic-elp-analyze-list)
+  (elp-set-master 'semantic-analyze-current-context)
+  )
+
+(defvar semantic-elp-symref-list
+  '(
+    semantic-symref-hits-in-region
+    semantic-symref-test-count-hits-in-tag
+    )
+  "List of symref functions for profiling.")
+
+(defun semantic-elp-analyze-symref-hits ()
+  "Enable profiling for `semanticdb-analyze-current-context'."
+  (semantic-elp-analyze-enable)
+  (elp-instrument-list semantic-elp-symref-list)
+  (elp-set-master 'semantic-symref-test-count-hits-in-tag)
+  )
+
+(defvar semantic-elp-complete-list
+  '(
+    semantic-analyze-possible-completions
+    semantic-analyze-possible-completions-default
+    semantic-analyze-tags-of-class-list
+    semantic-analyze-type-constants
+    semantic-unique-tag-table-by-name
+    )
+  "List of smart completion functions for profiling.")
+
+(defun semantic-elp-complete-enable ()
+  "Enable profiling for `semanticdb-analyze-current-context'."
+  (semantic-elp-analyze-enable)
+  (elp-instrument-list semantic-elp-complete-list)
+  (elp-set-master 'semantic-analyze-possible-completions)
+  )
+
+;;; Storage Classes
+;;
+;;
+(defclass semantic-elp-data ()
+  ((raw :initarg :raw
+	:type list
+	:documentation
+	"The raw ELP data.")
+   (sort :initform time
+	 :documentation
+	 "Which column do we sort our data by during various dumps.")
+   (sorted :initform nil
+	   :documentation
+	   "The sorted and filtered version of this data.")
+   (total :initarg :total
+	  :initform nil
+	  :documentation
+	  "The total time spent in the operation.
+Recorded outside of ELP.")
+   )
+  "Class for managing ELP data.")
+
+(defmethod semantic-elp-change-sort ((data semantic-elp-data) &optional newsort)
+  "Change the sort in DATA object to NEWSORT."
+  (cond ((eq newsort 'rotate)
+	 (let* ((arot '((time . avg)
+			(avg . calls)
+			(calls . name)
+			(name . time)))
+		(next (cdr (assoc (oref data sort) arot)))
+		)
+	   (oset data sort next)))
+	((null newsort)
+	 nil)
+	(t
+	 (oset data sort newsort)))
+  (let ((r (copy-sequence (oref data raw)))
+	(s (oref data sort)))
+    (cond ((eq s 'time)
+	   (oset data sorted (sort r (lambda (a b)
+				       (> (aref a 1) (aref b 1))
+				       )))
+	   )
+	  ((eq s 'avg)
+	   (oset data sorted (sort r (lambda (a b)
+				       (> (aref a 2) (aref b 2))
+				       )))
+	   )
+	  ((eq s 'calls)
+	   (oset data sorted (sort r (lambda (a b)
+				       (> (aref a 0) (aref b 0))
+				       )))
+	   )
+	  ((eq s 'name)
+	   (oset data sorted (sort r (lambda (a b)
+				       (string< (aref a 3) (aref b 3))
+				       )))
+	   )
+	  (t (message "Don't know how to resort with %s" s)
+	     ))))
+
+(defun semantic-elp-goto-function (point)
+  "Goto the function from the ELP data.
+Argument POINT is where to get the data from."
+  (let* ((data (get-text-property point 'ddebug))
+	 )
+    (find-function (intern-soft (aref data 3)))
+    ))
+
+(defmethod semantic-elp-dump-table ((data semantic-elp-data)
+				    prefix)
+  "dump out the current DATA table using PREFIX before each line."
+  (let* ((elpd (oref data sorted))
+	 (spaces (make-string (- (length prefix) 2) ? ))
+	 )
+    (data-debug-insert-simple-thing
+     "Calls\t Total Time\t Avg Time/Call\tName"
+     spaces " " 'underline)
+    (dolist (d elpd)
+      (when (> (aref d 0) 0) ;; We had some calls
+	(let ((start (point))
+	      (end nil))
+	  (data-debug-insert-simple-thing
+	   (format " % 4d\t% 2.7f\t% 2.7f\t%s"
+		   (aref d 0) (aref d 1) (aref d 2) (aref d 3))
+	   spaces " " nil)
+	  (setq end (1- (point)))
+	  (put-text-property start end 'ddebug d)
+	  (put-text-property start end 'ddebug-noexpand t)
+	  (put-text-property start end 'ddebug-function
+			     'semantic-elp-goto-function)
+	  )
+	))
+    )
+  )
+
+(defmethod data-debug/eieio-insert-slots ((data semantic-elp-data)
+					   prefix)
+  "Show the fields of ELP data in an adebug buffer.
+Ignore the usual, and format a nice table."
+  (data-debug-insert-thing (object-name-string data)
+			   prefix
+			   "Name: ")
+  (let* ((cl (object-class data))
+	 (cv (class-v cl)))
+    (data-debug-insert-thing (class-constructor cl)
+			     prefix
+			     "Class: ")
+    )
+
+  (data-debug-insert-thing (oref data :total)
+			   prefix
+			   "Total Time Spent: ")
+
+  (let ((s (oref data sort))
+	)
+    ;; Show how it's sorted:
+    (let ((start (point))
+	  (end nil)
+	  )
+      (insert prefix "Sort Method: " (symbol-name s))
+      (setq end (point))
+      ;; (data-debug-insert-thing s prefix "Sort Method: ")
+      (put-text-property start end 'ddebug data)
+      (put-text-property start end 'ddebug-noexpand t)
+      (put-text-property start end 'ddebug-indent(length prefix))
+      (put-text-property start end 'ddebug-prefix prefix)
+      (put-text-property start end 'ddebug-function
+			 'semantic-elp-change-sort-adebug)
+      (put-text-property start end 'help-echo
+			 "Change the Sort by selecting twice.")
+      (insert "\n"))
+
+    ;; How to sort the raw data
+    (semantic-elp-change-sort data)
+    )
+  ;; Display
+  (semantic-elp-dump-table data prefix)
+  )
+
+(defun semantic-elp-change-sort-adebug (point)
+  "Change the sort function here.  Redisplay.
+Argument POINT is where the text is."
+  (let* ((data (get-text-property point 'ddebug))
+	 (prefix (get-text-property point 'ddebug-prefix))
+	 )
+    ;; Get rid of the old table.
+    (data-debug-contract-current-line)
+    ;; Change it
+    (semantic-elp-change-sort data 'rotate)
+    (end-of-line)
+    (forward-word -1)
+    (delete-region (point) (point-at-eol))
+    (insert (symbol-name (oref data sort)))
+    ;; Redraw it.
+    (save-excursion
+      (end-of-line)
+      (forward-char 1)
+      (semantic-elp-dump-table data prefix))
+    ))
+
+(defclass semantic-elp-object-base (eieio-persistent)
+  ((file-header-line :initform ";; SEMANTIC ELP Profiling Save File")
+   (total :initarg :total
+	  :type number
+	  :documentation
+	  "Amount of time spent during the entire collection.")
+   )
+  "Base elp object.")
+
+(defclass semantic-elp-object (semantic-elp-object-base)
+  ((time :initarg :time
+	 :type semantic-elp-data
+	 :documentation
+	 "Times for calculating something.")
+   (answer :initarg :answer
+	   :documentation
+	   "Any answer that might be useful."))
+  "Simple elp object for remembering one analysis run.")
+
+(defclass semantic-elp-object-analyze (semantic-elp-object-base)
+  ((pathtime :initarg :pathtime
+	     :type semantic-elp-data
+	     :documentation
+	     "Times for calculating the include path.")
+   (typecachetime :initarg :typecachetime
+		  :type semantic-elp-data
+		  :documentation
+		  "Times for calculating the typecache.")
+   (scopetime :initarg :scopetime
+	      :type semantic-elp-data
+	      :documentation
+	      "Times for calculating the typecache")
+   (ctxttime :initarg :ctxttime
+	     :type semantic-elp-data
+	     :documentation
+	     "Times for calculating the context.")
+   (completiontime :initarg :completiontime
+		   :type semantic-elp-data
+		   :documentation
+		   "Times for calculating the completions.")
+   )
+  "Results from a profile run.")
+
+;;; ELP hackery.
+;;
+
+(defvar semantic-elp-last-results nil
+  "Save the last results from an ELP run for more post processing.")
+
+(defun semantic-elp-results (name)
+  "Fetch results from the last run, and display.
+Copied out of elp.el and modified only slightly.
+Argument NAME is the name to give the ELP data object."
+  (let ((resvec
+	 (mapcar
+	  (function
+	   (lambda (funsym)
+	     (let* ((info (get funsym elp-timer-info-property))
+		    (symname (format "%s" funsym))
+		    (cc (aref info 0))
+		    (tt (aref info 1)))
+	       (if (not info)
+		   (insert "No profiling information found for: "
+			   symname)
+		 ;;(setq longest (max longest (length symname)))
+		 (vector cc tt (if (zerop cc)
+				   0.0 ;avoid arithmetic div-by-zero errors
+				 (/ (float tt) (float cc)))
+			 symname)))))
+	  elp-all-instrumented-list))
+	)				; end let
+    (setq semantic-elp-last-results (semantic-elp-data name :raw resvec))
+    (elp-reset-all))
+  )
+
+;;; The big analyze and timer function!
+;;
+;;
+
+(defvar semantic-elp-last-run nil
+  "The results from the last elp run.")
+
+(defun semantic-elp-analyze ()
+  "Run the analyzer, using ELP to measure performance."
+  (interactive)
+  (let ((elp-recycle-buffers-p nil)
+	(totalstart (current-time))
+	(totalstop nil)
+	start stop
+	path pathtime
+	typecache typecachetime
+	scope scopetime
+	ctxt ctxttime
+	completion completiontime)
+    ;; Force tag table to be up to date.
+    (semantic-clear-toplevel-cache)
+    (semantic-fetch-tags)
+    ;; Path translation
+    (semantic-elp-include-path-enable)
+    (progn
+      (setq start (current-time))
+      (setq path (semanticdb-find-translate-path nil nil))
+      (setq stop (current-time)))
+    (semantic-elp-results "translate-path")
+    (setq pathtime semantic-elp-last-results)
+    (oset pathtime :total (semantic-elapsed-time start stop))
+    ;; typecache
+    (let* ((tab semanticdb-current-table)
+	   (idx (semanticdb-get-table-index tab))
+	   (tc nil)
+	   )
+      (semantic-elp-typecache-enable)
+      (progn
+	(setq start (current-time))
+	(setq tc (semantic-elp-profile-typecache tab))
+	(setq stop (current-time)))
+      (setq typecache tc))
+    (semantic-elp-results "typecache")
+    (setq typecachetime semantic-elp-last-results)
+    (oset typecachetime :total (semantic-elapsed-time start stop))
+    ;; Scope
+    (semantic-elp-scope-enable)
+    (progn
+      (setq start (current-time))
+      (setq scope (semantic-calculate-scope))
+      (setq stop (current-time)))
+    (semantic-elp-results "scope")
+    (setq scopetime semantic-elp-last-results)
+    (oset scopetime :total (semantic-elapsed-time start stop))
+    ;; Analyze!
+    (semantic-elp-analyze-enable)
+    (progn
+      (setq start (current-time))
+      (setq ctxt (semantic-analyze-current-context)) ; skip caching
+      (setq stop (current-time)))
+    (semantic-elp-results "analyze")
+    (setq ctxttime semantic-elp-last-results)
+    (oset ctxttime :total (semantic-elapsed-time start stop))
+    ;; Complete!
+    (semantic-elp-complete-enable)
+    (progn
+      (setq start (current-time))
+      (setq completion (semantic-analyze-possible-completions ctxt))
+      (setq stop (current-time)))
+    (semantic-elp-results "complete")
+    (setq completiontime semantic-elp-last-results)
+    (oset completiontime :total (semantic-elapsed-time start stop))
+    ;; Finish it
+    (setq totalstop (current-time))
+    ;; build it
+    (let ((elpobj (semantic-elp-object-analyze
+		   "ELP"
+		   :total          (semantic-elapsed-time totalstart totalstop)
+		   :pathtime	   pathtime
+		   :typecachetime  typecachetime
+		   :scopetime	   scopetime
+		   :ctxttime	   ctxttime
+		   :completiontime completiontime
+		   )))
+      (data-debug-show elpobj)
+      (setq semantic-elp-last-run elpobj)
+      (let ((saveas (read-file-name "Save Profile to: " (expand-file-name "~/")
+				    "semantic.elp" nil "semantic.elp")))
+	(oset elpobj :file saveas)
+	(eieio-persistent-save elpobj)
+	)
+      )))
+
+(defun semantic-elp-idle-work ()
+  "Run the idle work scheduler, using ELP to measure performance."
+  (interactive)
+  (let ((elp-recycle-buffers-p nil)
+	(totalstart nil)
+	(totalstop nil)
+	ans time
+	)
+    ;; Path translation
+    (semantic-elp-core-enable)
+    (setq totalstart (current-time))
+    (semantic-idle-scheduler-work-parse-neighboring-files)
+    (setq totalstop (current-time))
+    (semantic-elp-results "")
+    (setq time semantic-elp-last-results)
+    (oset time :total (semantic-elapsed-time totalstart totalstop))
+    ;; build it
+    (let ((elpobj (semantic-elp-object
+		   "ELP"
+		   :total          (semantic-elapsed-time totalstart totalstop)
+		   :time	   time)))
+      (data-debug-show elpobj)
+      (setq semantic-elp-last-run elpobj)
+      (let ((saveas (read-file-name "Save Profile to: " (expand-file-name "~/")
+				    "semantic.elp" nil "semantic.elp")))
+	(oset elpobj :file saveas)
+	(eieio-persistent-save elpobj)
+	)
+      )))
+
+(defun semantic-elp-searchdb ()
+  "Run a semanticdb search routine with the profiler.
+The expectation is that you will edit this fcn with different
+`semanticdb-find-' routines."
+  (interactive)
+  (let ((elp-recycle-buffers-p nil)
+	(totalstart nil)
+	(totalstop nil)
+	ans time
+	)
+    ;; reset
+    (semantic-clear-toplevel-cache)
+    (semantic-fetch-tags)
+
+    ;; Path translation
+    (semantic-elp-include-path-enable)
+    (setq totalstart (current-time))
+
+    (setq ans (semanticdb-find-tags-by-name-regexp "task" nil))
+
+    (setq totalstop (current-time))
+    (semantic-elp-results "")
+    (setq time semantic-elp-last-results)
+    (oset time :total (semantic-elapsed-time totalstart totalstop))
+    ;; build it
+    (let ((elpobj (semantic-elp-object
+		   "ELP"
+		   :total          (semantic-elapsed-time totalstart totalstop)
+		   :time	   time
+		   :answer         ans)))
+      (data-debug-show elpobj)
+      (setq semantic-elp-last-run elpobj)
+      (let ((saveas (read-file-name "Save Profile to: " (expand-file-name "~/")
+				    "semantic.elp" nil "semantic.elp")))
+	(oset elpobj :file saveas)
+	(eieio-persistent-save elpobj)
+	)
+      )))
+
+(defun semantic-elp-symref-hit-count ()
+  "Run a `semantic-symref-test-count-hits-in-tag' with elp on."
+  (interactive)
+  (let ((elp-recycle-buffers-p nil)
+	(totalstart nil)
+	(totalstop nil)
+	ans time
+	)
+    ;; reset
+    (semantic-clear-toplevel-cache)
+    (semantic-fetch-tags)
+
+    ;; Build up caches so we get user use timings.
+    (semantic-analyze-current-context)
+
+    ;; Enable everything for analysis.
+    (semantic-elp-analyze-symref-hits)
+
+    ;; Do the analysis
+    (setq totalstart (current-time))
+
+    (setq ans (semantic-symref-test-count-hits-in-tag))
+
+    (setq totalstop (current-time))
+
+    (semantic-elp-results "")
+    (setq time semantic-elp-last-results)
+    (oset time :total (semantic-elapsed-time totalstart totalstop))
+    ;; build it
+    (let ((elpobj (semantic-elp-object
+		   "ELP"
+		   :total          (semantic-elapsed-time totalstart totalstop)
+		   :time	   time
+		   :answer         ans)))
+      (data-debug-show elpobj)
+      (setq semantic-elp-last-run elpobj)
+;;(let ((saveas (read-file-name "Save Profile to: " (expand-file-name "~/")
+;;				    "semantic.elp" nil "semantic.elp")))
+;;	(oset elpobj :file saveas)
+;;	(eieio-persistent-save elpobj)
+;;	)
+      )))
+
+(defun semantic-elp-show-last-run ()
+  "Show the last elp run."
+  (interactive)
+  (when (not semantic-elp-last-run)
+    (error "No last run to show"))
+  (data-debug-show semantic-elp-last-run))
+
+(defun semantic-elp-load-old-run (file)
+  "Load an old run from FILE, and show it."
+  (interactive "fLast Run File: ")
+  (setq semantic-elp-last-run
+	(eieio-persistent-read file))
+  (data-debug-show semantic-elp-last-run))
+
+(provide 'semantic/elp)
+;;; semantic-elp.el ends here