diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2a8bfa5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*.elc
+*.fasl
+.#*
\ No newline at end of file
diff --git a/ChangeLog b/ChangeLog
index 3d57436..3df68c3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2013-11-20  João Távora <joaotavora@gmail.com>
+
+	* swank-backend.lisp (wrap, unwrap, wrapped-p): new wrapping
+	interface with default implementation.
+	* swank-allegro.lisp (wrap,unwrap,wrapped-p): implement wrapping interface
+	* swank-sbcl.lisp (wrap,unwrap,wrapped-p): implement wrapping interface
+	* swank.lisp (*after-toggle-trace-hook*): new exported variable,
+	defaults to nil.
+	* swank-loader.lisp (*contribs*): add swank-trace-dialog contrib
+
 2013-11-17  Helmut Eller  <heller@common-lisp.net>
 
 	* swank-sbcl.lisp (swank-compile-string): Load the fasl file even
diff --git a/contrib/ChangeLog b/contrib/ChangeLog
index b596372..1aaa483 100644
--- a/contrib/ChangeLog
+++ b/contrib/ChangeLog
@@ -1,3 +1,14 @@
+2013-11-20  JoÃ£o TÃ¡vora <joaotavora@gmail.com>
+
+	* swank-trace-dialog.lisp: New file, new :swank-trace-dialog
+	package, uses new wrapping interface in swank-backend.el to trace
+	fspecs. Report the trace tree to emacs.
+	* slime-trace-dialog.el: New file, collect and render a
+	interactive tree of traces collected from the Lisp
+	backend. Inspect trace arguments and return values.
+	* slime-fancy.el: Enable new contrib slime-trace-dialog. Call
+	slime-fancy trace init function.
+
 2013-05-11  Marco Baringer  <mb@bese.it>
 
 	* slime-repl.el (slime-repl-sexp-at-point): New function; similar
diff --git a/contrib/slime-fancy.el b/contrib/slime-fancy.el
index e7e5a22..c466526 100644
--- a/contrib/slime-fancy.el
+++ b/contrib/slime-fancy.el
@@ -15,13 +15,16 @@
                        slime-scratch
                        slime-references
                        slime-package-fu
-                       slime-fontifying-fu)
+                       slime-fontifying-fu
+                       slime-trace-dialog)
   (:on-load
+   (slime-trace-dialog-init)
    (slime-repl-init)
    (slime-autodoc-init)
    (slime-c-p-c-init)
    (slime-editing-commands-init)
    (slime-fancy-inspector-init)
+   (slime-fancy-trace-init)
    (slime-fuzzy-init)
    (slime-presentations-init)
    (slime-scratch-init)
diff --git a/contrib/slime-trace-dialog.el b/contrib/slime-trace-dialog.el
new file mode 100644
index 0000000..e7883d3
--- /dev/null
+++ b/contrib/slime-trace-dialog.el
@@ -0,0 +1,690 @@
+;;; -*- coding: utf-8; lexical-binding: t -*-
+;;;
+;;; slime-trace-dialog.el -- a navigable dialog of inspectable trace entries
+;;;
+;;; TODO: implement better wrap interface for sbcl, capable of tracing
+;;;       complex specs like allegro's.
+;;;
+
+(define-slime-contrib slime-trace-dialog
+  "Provide an interfactive trace dialog buffer for managing and
+inspecting details of traced functions. Invoke this dialog with C-c T."
+  (:authors "JoÃ£o TÃ¡vora <joaotavora@gmail.com>")
+  (:license "GPL")
+  (:swank-dependencies swank-trace-dialog)
+  (:on-load (define-key slime-prefix-map "T"  'slime-trace-dialog)
+            (define-key slime-prefix-map "\M-t"  'slime-trace-dialog-toggle-trace))
+  (:on-unload (define-key slime-prefix-map "T"   nil)))
+
+
+;;;; Variables
+;;;
+(defvar slime-trace-dialog-autofollow nil)
+
+(defvar slime-trace-dialog--brief-mode nil)
+
+(defvar slime-trace-dialog-flash       t)
+
+(defvar slime-trace-dialog--specs-overlay nil)
+
+(defvar slime-trace-dialog--progress-overlay nil)
+
+(defvar slime-trace-dialog--tree-overlay nil)
+
+(defvar slime-trace-dialog--local-control-overlay nil)
+
+(defvar slime-trace-dialog--collapse-chars (cons "-" "+"))
+
+
+;;;; Local trace entry model
+(defvar slime-trace-dialog--traces nil)
+
+(cl-defstruct (slime-trace-dialog--trace (:constructor slime-trace-dialog--make-trace))
+  id
+  parent
+  spec
+  args
+  retlist
+  depth
+  beg
+  end
+  collapse-button-marker
+  summary-beg
+  children-end
+  collapsed-p)
+
+(defun slime-trace-dialog--find-trace (id)
+  (gethash id slime-trace-dialog--traces))
+
+
+;;;; Modes and mode maps
+;;;
+(define-derived-mode slime-trace-dialog--mode slime-inspector-mode "Slime-Trace-Dialog-"
+  "Mode for controlling slime's trace dialog")
+
+(setq slime-trace-dialog--mode-map
+      (let ((map (make-sparse-keymap))
+            (remaps '((slime-inspector-operate-on-point            . nil)
+                      (slime-inspector-operate-on-click            . nil)
+                      (slime-inspector-reinspect                   . slime-trace-dialog-fetch-status)
+                      (slime-inspector-next-inspectable-object     . slime-trace-dialog-next-button)
+                      (slime-inspector-previous-inspectable-object . slime-trace-dialog-prev-button))))
+        (set-keymap-parent map slime-inspector-mode-map)
+        (loop for (old . new) in remaps
+              do (substitute-key-definition old new map))
+        (set-keymap-parent map slime-parent-map)
+        (define-key map (kbd "G") 'slime-trace-dialog-fetch-traces)
+
+        map))
+
+(define-derived-mode slime-trace-dialog--detail-mode slime-inspector-mode "Slime-Trace-Detail"
+  "Mode for viewing a particular trace from slime's trace dialog")
+
+(setq slime-trace-dialog--detail-mode-map
+      (let ((map (make-sparse-keymap))
+            (remaps '((slime-inspector-next-inspectable-object     . slime-trace-dialog-next-button)
+                      (slime-inspector-previous-inspectable-object . slime-trace-dialog-prev-button))))
+        (set-keymap-parent map slime-trace-dialog--mode-map)
+        (loop for (old . new) in remaps
+              do (substitute-key-definition old new map))
+        map))
+
+
+;;;; Helper functions
+;;;
+(defun slime-trace-dialog--call-refreshing (buffer overlay dont-erase recover-point-p fn)
+  (with-current-buffer buffer
+    (let ((inhibit-point-motion-hooks t)
+          (inhibit-read-only t)
+          (saved (point)))
+      (save-restriction
+        (when overlay
+          (narrow-to-region (overlay-start overlay)
+                            (overlay-end overlay)))
+        (unwind-protect
+            (if dont-erase
+                (goto-char (point-max))
+              (delete-region (point-min) (point-max)))
+          (funcall fn)
+          (when recover-point-p
+            (goto-char saved)))
+        (when slime-trace-dialog-flash
+          (slime-flash-region (point-min) (point-max)))))
+    buffer))
+
+(cl-defmacro slime-trace-dialog--refresh ((buffer &key overlay dont-erase recover-point-p) &rest body)
+  (declare (indent 1)
+           (debug (sexp &rest form)))
+  `(slime-trace-dialog--call-refreshing ,buffer ,overlay ,dont-erase ,recover-point-p #'(lambda () ,@body)))
+
+(defmacro slime-trace-dialog--insert-and-overlay (string overlay)
+  `(save-restriction
+     (let ((inhibit-read-only t))
+       (narrow-to-region (point) (point))
+       (insert ,string "\n")
+       (setq ,overlay
+             (let ((overlay (make-overlay (point-min) (point-max) (current-buffer) nil t)))
+               (move-overlay overlay (overlay-start overlay) (1- (overlay-end overlay)))
+               ;; (overlay-put overlay 'face '(:background "darkslategrey"))
+               overlay)))))
+
+(defun slime-trace-dialog--get-buffer ()
+  (or (get-buffer "*trace-dialog*")
+      (slime-with-popup-buffer ("*trace-dialog*" :mode 'slime-trace-dialog--mode)
+        (save-excursion
+          (buffer-disable-undo)
+          (slime-trace-dialog--insert-and-overlay "local control"
+                                                  slime-trace-dialog--local-control-overlay)
+          (slime-trace-dialog--insert-and-overlay "[waiting for the traced specs to be available]"
+                                                  slime-trace-dialog--specs-overlay)
+          (slime-trace-dialog--insert-and-overlay "[waiting for some info on trace download progress ]"
+                                                  slime-trace-dialog--progress-overlay)
+          (slime-trace-dialog--insert-and-overlay "[waiting for the actual traces to be available]"
+                                                  slime-trace-dialog--tree-overlay)
+          (setq slime-trace-dialog--brief-mode nil)
+          (current-buffer)))))
+
+(defun slime-trace-dialog--make-autofollow-fn (id)
+  (let ((requested nil))
+    #'(lambda (_before after)
+        (let ((inhibit-point-motion-hooks t)
+              (id-after (get-text-property after 'slime-trace-dialog--id)))
+          (when (and (= after (point))
+                     slime-trace-dialog-autofollow
+                     id-after
+                     (= id-after id)
+                     (not requested))
+            (setq requested t)
+            (slime-eval-async `(swank-trace-dialog:report-trace-detail ,id-after)
+                #'(lambda (detail)
+                    (setq requested nil)
+                    (when detail
+                      (let ((inhibit-point-motion-hooks t))
+                        (slime-trace-dialog--open-detail detail 'no-pop))))))))))
+
+(defun slime-trace-dialog--set-collapsed (collapsed-p trace button)
+  (save-excursion
+    (setf (slime-trace-dialog--trace-collapsed-p trace) collapsed-p)
+    (slime-trace-dialog--go-replace-char-at button (if collapsed-p
+                                                       (cdr slime-trace-dialog--collapse-chars)
+                                                     (car slime-trace-dialog--collapse-chars)))
+    (slime-trace-dialog--hide-unhide (slime-trace-dialog--trace-summary-beg trace)
+                                     (slime-trace-dialog--trace-end trace)
+                                     (if collapsed-p 1 -1))
+    (slime-trace-dialog--hide-unhide (slime-trace-dialog--trace-end trace)
+                                     (slime-trace-dialog--trace-children-end trace)
+                                     (if collapsed-p 1 -1))))
+
+(defun slime-trace-dialog--hide-unhide (start-pos end-pos delta)
+  (loop with inhibit-read-only = t
+        for pos = start-pos then next
+        for next = (next-single-property-change pos
+                                                'slime-trace-dialog--hidden-level
+                                                nil
+                                                end-pos)
+        for hidden-level = (+ (or (get-text-property pos
+                                                     'slime-trace-dialog--hidden-level)
+                                  0)
+                              delta)
+        do (add-text-properties pos next
+                                (list 'slime-trace-dialog--hidden-level
+                                      hidden-level
+                                      'invisible
+                                      (plusp hidden-level)))
+        while (< next end-pos)))
+
+(defun slime-trace-dialog--set-brief-mode ()
+  (loop for trace being the hash-values of slime-trace-dialog--traces
+        do (slime-trace-dialog--hide-unhide
+            (slime-trace-dialog--trace-summary-beg trace)
+            (slime-trace-dialog--trace-end trace)
+            (if slime-trace-dialog--brief-mode 1 -1))))
+
+(defun slime-trace-dialog--format-part (part-id part-text trace-id type)
+  (slime-trace-dialog--button (format "%s" part-text)
+                              #'(lambda (_button)
+                                  (slime-eval-async
+                                      `(swank-trace-dialog:inspect-trace-part ,trace-id ,part-id ,type)
+                                      #'slime-open-inspector))
+                              'mouse-face 'highlight
+                              'face 'slime-inspector-value-face))
+
+(defun slime-trace-dialog--format-trace-entry (id external)
+  (slime-trace-dialog--button (format "%s" external)
+                              #'(lambda (_button)
+                                  (slime-eval-async
+                                      `(swank::inspect-object (swank-trace-dialog::find-trace ,id))
+                                      #'slime-open-inspector))
+                              'face 'slime-inspector-value-face))
+
+(defun slime-trace-dialog--format (fmt-string &rest args)
+  (let* ((string (apply #'format fmt-string args))
+         (indent (make-string (max 2
+                                   (- 50 (length string))) ? )))
+    (format "%s%s" string indent)))
+
+(defun slime-trace-dialog--button (title lambda &rest props)
+  (let ((string (format "%s" title)))
+    (apply #'make-text-button string nil
+           'action     #'(lambda (button)
+                           (funcall lambda button))
+           'mouse-face 'highlight
+           'face       'slime-inspector-action-face
+           props)
+    string))
+
+(defun slime-trace-dialog--call-maintaining-properties (pos fn)
+  (save-excursion
+    (goto-char pos)
+    (let* ((saved-props (text-properties-at pos))
+           (saved-point (point))
+           (inhibit-read-only t)
+           (inhibit-point-motion-hooks t))
+      (funcall fn)
+      (add-text-properties saved-point (point) saved-props)
+      (if (markerp pos) (set-marker pos saved-point)))))
+
+(cl-defmacro slime-trace-dialog--maintaining-properties (pos
+                                                         &body body)
+  (declare (indent 1))
+  `(slime-trace-dialog--call-maintaining-properties ,pos #'(lambda () ,@body)))
+
+(defun slime-trace-dialog--go-replace-char-at (pos char)
+  (slime-trace-dialog--maintaining-properties pos
+    (delete-char 1)
+    (insert char)))
+
+
+;;;; Handlers for the *trace-dialog* and *trace-detail* buffers
+;;;
+(defun slime-trace-dialog--open-specs (traced-specs)
+  (cl-flet ((make-report-spec-fn
+             (&optional form)
+             #'(lambda (_button)
+                 (slime-eval-async
+                     `(cl:progn
+                       ,form
+                       (swank-trace-dialog:report-specs))
+                     #'(lambda (results)
+                         (slime-trace-dialog--open-specs results))))))
+    (slime-trace-dialog--refresh
+        ((slime-trace-dialog--get-buffer)
+         :overlay slime-trace-dialog--specs-overlay
+         :recover-point-p t)
+      (insert
+       (slime-trace-dialog--format "Traced specs (%s)" (length traced-specs))
+       (slime-trace-dialog--button "[refresh]"
+                                   (make-report-spec-fn))
+       "\n" (make-string 50 ? )
+       (slime-trace-dialog--button "[untrace all]"
+                                   (make-report-spec-fn `(swank-trace-dialog:dialog-untrace-all)))
+       "\n\n")
+      (loop for spec in traced-specs
+            do (insert
+                "  "
+                (slime-trace-dialog--button "[untrace]"
+                                            (make-report-spec-fn `(swank-trace-dialog:dialog-untrace ',spec)))
+                (format " %s" spec)
+                "\n")))))
+
+(defun slime-trace-dialog--open-local-control ()
+  (slime-trace-dialog--refresh
+      ((slime-trace-dialog--get-buffer)
+       :overlay slime-trace-dialog--local-control-overlay
+       :recover-point-p t)
+    (insert
+     (slime-trace-dialog--format "Autofollow is %s. "
+                                 (if slime-trace-dialog-autofollow "on" "off"))
+     (slime-trace-dialog--button "[toggle]"
+                                 #'(lambda (_button)
+                                     (setq slime-trace-dialog-autofollow
+                                           (not slime-trace-dialog-autofollow))
+                                     (slime-trace-dialog--open-local-control)))
+     "\n"
+     (slime-trace-dialog--format "Brief mode is %s. "
+                                 (if slime-trace-dialog--brief-mode "on" "off"))
+     (slime-trace-dialog--button "[toggle]"
+                                 #'(lambda (_button)
+                                     (setq slime-trace-dialog--brief-mode
+                                           (not slime-trace-dialog--brief-mode))
+                                     (slime-trace-dialog--set-brief-mode)
+                                     (slime-trace-dialog--open-local-control)))
+     "\n")))
+
+(defvar slime-trace-dialog--fetch-key nil)
+
+(defvar slime-trace-dialog--stop-fetching nil)
+
+(defun slime-trace-dialog--update-progress (done remaining &optional show-stop-p)
+  (slime-trace-dialog--refresh
+      ((slime-trace-dialog--get-buffer)
+       :overlay slime-trace-dialog--progress-overlay
+       :recover-point-p t)
+    (insert
+     (slime-trace-dialog--format "Trace collection status (%d/%s)"
+                                 done
+                                 (if remaining
+                                     (+ done remaining)
+                                   "?"))
+     (slime-trace-dialog--button "[refresh]"
+                                 #'(lambda (_button)
+                                     (slime-eval-async
+                                         '(swank-trace-dialog:report-total)
+                                         #'(lambda (total)
+                                             (slime-trace-dialog--update-progress
+                                              done
+                                              (- total done)))))))
+
+    (when (and remaining (plusp remaining))
+      (insert "\n" (make-string 50 ? )
+              (slime-trace-dialog--button "[fetch next batch]"
+                                          #'(lambda (_button)
+                                              (slime-trace-dialog-fetch-traces nil)))
+              "\n" (make-string 50 ? )
+              (slime-trace-dialog--button "[fetch all]"
+                                          #'(lambda (_button)
+                                              (slime-trace-dialog-fetch-traces t)))))
+    (when (and done remaining (plusp done))
+      (insert "\n" (make-string 50 ? )
+              (slime-trace-dialog--button "[clear]"
+                                          #'(lambda (_button)
+                                              (slime-eval-async
+                                                  '(swank-trace-dialog:clear-trace-tree)
+                                                  #'(lambda (_ignored)
+                                                      (slime-trace-dialog--clear-local-tree 0)))))))
+    (when show-stop-p
+      (insert "\n" (make-string 50 ? )
+              (slime-trace-dialog--button "[stop]"
+                                          #'(lambda (_button)
+                                              (setq slime-trace-dialog--stop-fetching t)))))
+    (insert "\n\n")))
+
+(defun slime-trace-dialog--open-detail (trace-tuple &optional no-pop)
+  (slime-trace-dialog--refresh
+      ((or (get-buffer "*trace-detail*")
+           (slime-with-popup-buffer ("*trace-detail*" :mode 'slime-trace-dialog--detail-mode))
+           (get-buffer "*trace-detail*")))
+    (if no-pop
+        (display-buffer (current-buffer))
+      (pop-to-buffer (current-buffer)))
+    (destructuring-bind (id _parent-id _spec args retlist backtrace external)
+        trace-tuple
+      (let ((headline (slime-trace-dialog--format-trace-entry id external)))
+        (setq headline (format "%s\n%s\n"
+                               headline
+                               (make-string (length headline) ?-)))
+        (insert headline))
+      (loop for (type objects label)
+            in `((:arg ,args   "Called with args:")
+                 (:retval ,retlist "Returned values:"))
+            do (insert (format "\n%s\n" label))
+            (insert (loop for object in objects
+                          for i from 0
+                          concat (format "   %s: %s\n" i (slime-trace-dialog--format-part
+                                                          (first object)
+                                                          (second object)
+                                                          id
+                                                          type)))))
+      (when backtrace
+        (insert "\nBacktrace:\n"
+                (loop for (i spec) in backtrace
+                      concat (format "   %s: %s\n" i spec))))
+      )))
+
+
+;;;; Rendering traces
+;;;
+
+(defun slime-trace-dialog--draw-tree-lines (start offset direction)
+  (save-excursion
+    (let ((inhibit-point-motion-hooks t))
+      (goto-char start)
+      (loop with replace-set = (if (eq direction 'down)
+                                   '(? )
+                                 '(?  ?`))
+            for line-beginning = (line-beginning-position (if (eq direction 'down)
+                                                              2 0))
+            for pos = (+ line-beginning offset)
+            while (and (< (point-min) line-beginning)
+                       (< line-beginning (point-max))
+                       (memq (char-after pos) replace-set))
+            do
+            (slime-trace-dialog--go-replace-char-at pos "|")
+            (goto-char pos)))))
+
+(defun slime-trace-dialog--make-indent (depth suffix)
+  (concat (make-string (* 3 (max 0 (1- depth))) ? )
+          (if (plusp depth) suffix)))
+
+(defun slime-trace-dialog--make-collapse-button (trace)
+  (slime-trace-dialog--button (if (slime-trace-dialog--trace-collapsed-p trace)
+                                  (cdr slime-trace-dialog--collapse-chars)
+                                (car slime-trace-dialog--collapse-chars))
+                              #'(lambda (button)
+                                  (slime-trace-dialog--set-collapsed
+                                   (not (slime-trace-dialog--trace-collapsed-p trace))
+                                   trace
+                                   button))))
+
+
+(defun slime-trace-dialog--insert-trace (trace)
+  (let* ((id (slime-trace-dialog--trace-id trace))
+         (parent (slime-trace-dialog--trace-parent trace))
+         (has-children-p (slime-trace-dialog--trace-children-end trace))
+         (indent-spec
+          (slime-trace-dialog--make-indent (slime-trace-dialog--trace-depth trace)
+                                           "`--"))
+         (indent-summary
+          (slime-trace-dialog--make-indent (slime-trace-dialog--trace-depth trace)
+                                           "   "))
+         (autofollow-fn (slime-trace-dialog--make-autofollow-fn id))
+         (id-string (slime-trace-dialog--button (format "%4s" id)
+                                                #'(lambda (_button)
+                                                    (slime-eval-async
+                                                        `(swank-trace-dialog:report-trace-detail
+                                                          ,id)
+                                                      #'slime-trace-dialog--open-detail))))
+         (spec (slime-trace-dialog--trace-spec trace))
+         (summary (loop for (type objects marker) in
+                        `((:arg    ,(slime-trace-dialog--trace-args trace)    " > ")
+                          (:retval ,(slime-trace-dialog--trace-retlist trace) " < "))
+                        concat (loop for object in objects
+                                     concat "      "
+                                     concat indent-summary
+                                     concat marker
+                                     concat (slime-trace-dialog--format-part (first object)
+                                                                             (second object)
+                                                                             id
+                                                                             type)
+                                     concat "\n"))))
+    (puthash id trace slime-trace-dialog--traces)
+    ;; insert and propertize the text
+    ;;
+    (setf (slime-trace-dialog--trace-beg trace) (point-marker))
+    (insert id-string " ")
+    (insert indent-spec)
+    (if has-children-p
+        (insert (slime-trace-dialog--make-collapse-button trace))
+      (setf (slime-trace-dialog--trace-collapse-button-marker trace) (point-marker)))
+    (insert (format " %s\n" spec))
+    (setf (slime-trace-dialog--trace-summary-beg trace) (point-marker))
+    (insert summary)
+    (setf (slime-trace-dialog--trace-end trace) (point-marker))
+
+    (add-text-properties (slime-trace-dialog--trace-beg trace)
+                         (slime-trace-dialog--trace-end trace)
+                         (list 'slime-trace-dialog--id id
+                               'point-entered autofollow-fn
+                               'point-left autofollow-fn))
+    ;; respect brief mode and collapsed state
+    ;;
+    (loop for condition in (list slime-trace-dialog--brief-mode
+                                 (slime-trace-dialog--trace-collapsed-p trace))
+          when condition
+          do (slime-trace-dialog--hide-unhide (slime-trace-dialog--trace-summary-beg trace)
+                                              (slime-trace-dialog--trace-end trace)
+                                              1))
+    (loop for tr = trace then parent
+          for parent = (slime-trace-dialog--trace-parent tr)
+          while parent
+          when (slime-trace-dialog--trace-collapsed-p parent)
+          do (slime-trace-dialog--hide-unhide (slime-trace-dialog--trace-beg trace)
+                                              (slime-trace-dialog--trace-end trace)
+                                              (+ 1
+                                                 (or (get-text-property (slime-trace-dialog--trace-beg parent)
+                                                                        'slime-trace-dialog--hidden-level)
+                                                     0)))
+          (return))
+    ;; maybe add the collapse-button to the parent in case it didn't
+    ;; have one already
+    ;;
+    (when (and parent
+               (slime-trace-dialog--trace-collapse-button-marker parent))
+      (slime-trace-dialog--maintaining-properties
+          (slime-trace-dialog--trace-collapse-button-marker parent)
+        (insert (slime-trace-dialog--make-collapse-button parent))
+        (setf (slime-trace-dialog--trace-collapse-button-marker parent)
+              nil)))
+    ;; draw the tree lines
+    ;;
+    (when parent
+      (slime-trace-dialog--draw-tree-lines (slime-trace-dialog--trace-beg trace)
+                                           (+ 2 (length indent-spec))
+                                           'up))
+    (when has-children-p
+      (slime-trace-dialog--draw-tree-lines (slime-trace-dialog--trace-beg trace)
+                                           (+ 5 (length indent-spec))
+                                           'down))
+    ;; set the "children-end" slot
+    ;;
+    (unless (slime-trace-dialog--trace-children-end trace)
+      (loop for parent = trace
+            then (slime-trace-dialog--trace-parent parent)
+            while parent
+            do
+            (setf (slime-trace-dialog--trace-children-end parent)
+                  (slime-trace-dialog--trace-end trace))))))
+
+(defun slime-trace-dialog--render-trace (trace)
+  ;; Render the trace entry in the appropriate place.
+  ;;
+  ;; A trace becomes a few lines of propertized text in the buffer,
+  ;; generated by `slime-trace-dialog--insert-trace', bound by point
+  ;; markers that we use here.
+  ;;
+  ;; The new trace might be replacing an existing one, or otherwise
+  ;; must be placed under its existing parent which might or might not
+  ;; be the last entry inserted.
+  ;;
+  (let ((existing (slime-trace-dialog--find-trace
+                   (slime-trace-dialog--trace-id trace)))
+        (parent (slime-trace-dialog--trace-parent trace)))
+    (cond (existing
+           ;; We need to maintain eqness with existing traces and the best way
+           ;; to do that is destructively modify `existing' with the new
+           ;; retlist...
+           ;;
+           (setf (slime-trace-dialog--trace-retlist existing)
+                 (slime-trace-dialog--trace-retlist trace))
+           ;; And set `trace' to be `existing'
+           ;;
+           (setq trace existing)
+           ;; Before deleting and re-inserting `trace' at an arbitrary point in
+           ;; the tree, we notice that `trace''s "children-end" marker is
+           ;; non-nil at this point. We want to 1. leave it alone if it's
+           ;; already a parent, or 2. set it to nil if it's a leaf, thus forcing
+           ;; the needed update of the parents' "children-end" marker.
+           ;;
+           (when (= (slime-trace-dialog--trace-children-end trace)
+                    (slime-trace-dialog--trace-end trace))
+             (setf (slime-trace-dialog--trace-children-end trace) nil))
+           (delete-region (slime-trace-dialog--trace-beg trace)
+                          (slime-trace-dialog--trace-end trace))
+           (goto-char (slime-trace-dialog--trace-end trace)))
+          (parent
+           (goto-char (1+ (slime-trace-dialog--trace-children-end parent))))
+          (;; top level trace
+           t
+           (goto-char (point-max))))
+    (goto-char (line-beginning-position))
+    (slime-trace-dialog--insert-trace trace)))
+
+(defun slime-trace-dialog--update-tree (tuples)
+  (save-excursion
+    (slime-trace-dialog--refresh
+        ((slime-trace-dialog--get-buffer)
+         :overlay slime-trace-dialog--tree-overlay
+         :dont-erase t)
+      (loop for tuple in tuples
+            for parent = (slime-trace-dialog--find-trace (second tuple))
+            for trace = (slime-trace-dialog--make-trace
+                         :id (first tuple)
+                         :parent parent
+                         :spec (third tuple)
+                         :args (fourth tuple)
+                         :retlist (fifth tuple)
+                         :depth (if parent
+                                    (1+ (slime-trace-dialog--trace-depth parent))
+                                  0))
+            do (slime-trace-dialog--render-trace trace)))))
+
+(defun slime-trace-dialog--clear-local-tree (&optional remaining)
+  (set (make-local-variable 'slime-trace-dialog--fetch-key)
+       (cl-gensym "slime-trace-dialog-fetch-key-"))
+  (set (make-local-variable 'slime-trace-dialog--traces)
+       (make-hash-table))
+  (slime-trace-dialog--refresh
+      ((slime-trace-dialog--get-buffer)
+       :overlay slime-trace-dialog--tree-overlay))
+  (slime-trace-dialog--update-progress 0 remaining))
+
+(defun slime-trace-dialog--on-new-results (results &optional recurse)
+  (destructuring-bind (tuples remaining reply-key)
+      results
+    (cond ((and slime-trace-dialog--fetch-key
+                (string= (symbol-name slime-trace-dialog--fetch-key)
+                         (symbol-name reply-key)))
+           (slime-trace-dialog--update-tree tuples)
+           (slime-trace-dialog--update-progress (hash-table-count slime-trace-dialog--traces)
+                                                remaining
+                                                (and recurse
+                                                     (plusp remaining)))
+           (when (and recurse
+                      (not (prog1 slime-trace-dialog--stop-fetching
+                             (setq slime-trace-dialog--stop-fetching nil)))
+                      (plusp remaining))
+             (slime-eval-async `(swank-trace-dialog:report-partial-tree ',reply-key)
+                 #'(lambda (results) (slime-trace-dialog--on-new-results
+                                      results
+                                      recurse))))))))
+
+
+;;;; Interactive functions
+;;;
+(defun slime-trace-dialog-fetch-status ()
+  "Refresh just the status part of the trace dialog"
+  (interactive)
+  (with-current-buffer
+      (pop-to-buffer (slime-trace-dialog--get-buffer))
+    (slime-trace-dialog--open-local-control)
+    (slime-eval-async `(swank-trace-dialog:report-specs)
+        #'slime-trace-dialog--open-specs)))
+
+(defun slime-trace-dialog-fetch-traces (&optional recurse)
+  (interactive "P")
+  (with-current-buffer
+      (pop-to-buffer (slime-trace-dialog--get-buffer))
+    (setq slime-trace-dialog--stop-fetching nil)
+    (slime-eval-async `(swank-trace-dialog:report-partial-tree
+                                      ',slime-trace-dialog--fetch-key)
+        #'(lambda (results) (slime-trace-dialog--on-new-results results
+                                                                recurse)))))
+
+(defun slime-trace-dialog-next-button (&optional goback)
+  (interactive)
+  (let ((finder (if goback
+                    #'previous-single-property-change
+                  #'next-single-property-change)))
+    (loop for pos = (funcall finder (point) 'action)
+          while pos
+          do (goto-char pos)
+          until (get-text-property pos 'action))))
+
+(defun slime-trace-dialog-prev-button ()
+  (interactive)
+  (slime-trace-dialog-next-button 'goback))
+
+(defun slime-trace-dialog-toggle-trace (&optional using-context-p)
+  "Toggle the dialog-trace of the spec at point.
+
+When USING-CONTEXT-P, attempt to decipher lambdas. methods and
+other complicated function specs."
+  (interactive "P")
+  ;; Notice the use of "spec strings" here as opposed to the
+  ;; proper cons specs we use on the swank side.
+  ;;
+  ;; Notice the conditional use of `slime-trace-query' found in
+  ;; swank-fancy-trace.el
+  ;;
+  (let* ((spec-string (if using-context-p
+                          (slime-extract-context)
+                        (slime-symbol-at-point)))
+         (spec-string (if (fboundp 'slime-trace-query)
+                          (slime-trace-query spec-string)
+                        spec-string)))
+    (message "%s" (slime-eval `(swank-trace-dialog:dialog-toggle-trace (swank::from-string ,spec-string))))))
+
+(defun slime-trace-dialog (&optional just-one-batch)
+  "Show trace dialog and fetch all traces from the beginning.
+
+With optional JUST-ONE-BATCH prefix arg, fetch just first batch of
+traces."
+  (interactive "P")
+  (with-current-buffer
+      (pop-to-buffer (slime-trace-dialog--get-buffer))
+    (slime-trace-dialog-fetch-status)
+    (overlay-put slime-trace-dialog--tree-overlay 'slime-trace-dialog--collected nil)
+    (slime-trace-dialog--clear-local-tree)
+    (slime-trace-dialog-fetch-traces (not just-one-batch))))
+
+(provide 'slime-trace-dialog)
diff --git a/contrib/swank-trace-dialog.lisp b/contrib/swank-trace-dialog.lisp
new file mode 100644
index 0000000..5ef9278
--- /dev/null
+++ b/contrib/swank-trace-dialog.lisp
@@ -0,0 +1,252 @@
+(defpackage :swank-trace-dialog
+  (:use :cl)
+  (:nicknames :std)
+  (:import-from :swank :defslimefun :from-string :to-string)
+  (:export #:clear-trace-tree
+           #:dialog-toggle-trace
+           #:dialog-trace
+           #:dialog-traced-p
+           #:dialog-untrace
+           #:dialog-untrace-all
+           #:inspect-trace-part
+           #:report-partial-tree
+           #:report-specs
+           #:report-total
+           #:report-trace-detail
+           #:report-specs
+           #:trace-format
+           #:still-inside
+           #:exited-non-locally
+           #:*record-backtrace*
+           #:*traces-per-report*
+           #:*dialog-trace-follows-trace*))
+
+(in-package :swank-trace-dialog)
+
+(defparameter *record-backtrace* nil
+  "Record a backtrace of the last 20 calls for each trace.
+
+Beware that this may have a drastic performance impact on your
+program.")
+
+(defparameter *traces-per-report* 150
+  "Number of traces to report to emacs in each batch.")
+
+
+;;;; `trace-entry' model
+;;;;
+(defvar *traces* (make-array 1000 :fill-pointer 0
+                                  :adjustable t))
+
+(defvar *trace-lock* (swank-backend:make-lock :name "swank-trace-dialog lock"))
+
+(defvar *current-trace-by-thread* (make-hash-table))
+
+(defclass trace-entry ()
+  ((id                              :reader   id-of)
+   (spec       :initarg  :spec      :accessor spec-of
+               :initform (error "must provide a spec"))
+   (args       :initarg  :args      :accessor args-of)
+   (parent     :initarg  :parent    :reader   parent-of
+               :initform (error "must provide a parent, even if nil"))
+   (retlist    :initarg  :retlist   :accessor retlist-of    :initform 'still-inside)
+   (children                        :accessor children-of   :initform nil)
+   (backtrace                       :accessor backtrace-of
+                                    :initform (when *record-backtrace*
+                                                (useful-backtrace)))))
+
+(defmethod initialize-instance :after ((entry trace-entry) &rest initargs)
+  (declare (ignore initargs))
+  (if (parent-of entry)
+      (nconc (children-of (parent-of entry)) (list entry)))
+  (swank-backend:call-with-lock-held
+   *trace-lock*
+   #'(lambda ()
+       (setf (slot-value entry 'id) (fill-pointer *traces*))
+       (vector-push-extend entry *traces*))))
+
+(defmethod print-object ((entry trace-entry) stream)
+  (print-unreadable-object (entry stream)
+    (format stream "~a: ~a" (id-of entry) (spec-of entry))))
+
+(defun completed-p (trace) (not (eq (retlist-of trace) 'still-inside)))
+
+(defun find-trace (id)
+  (when (<= 0 id (1- (length *traces*)))
+    (aref *traces* id)))
+
+(defun useful-backtrace ()
+  (swank-backend:call-with-debugging-environment
+   #'(lambda ()
+       (loop for i from 0
+             for frame in (swank-backend:compute-backtrace 0 20)
+             collect (list i (swank::frame-to-string frame))))))
+
+(defun current-trace ()
+  (gethash (swank-backend:current-thread) *current-trace-by-thread*))
+
+(defun (setf current-trace) (trace)
+  (setf (gethash (swank-backend:current-thread) *current-trace-by-thread*)
+        trace))
+
+
+;;;; Control of traced specs
+;;;
+(defvar *traced-specs* '())
+
+(defslimefun dialog-trace (spec)
+  (flet ((before-hook (args)
+           (setf (current-trace) (make-instance 'trace-entry :spec      spec
+                                                             :args      args
+                                                             :parent    (current-trace))))
+         (after-hook (retlist)
+           (let ((trace (current-trace)))
+             (when trace
+               ;; the current trace might have been wiped away if the
+               ;; user cleared the tree in the meantime. no biggie,
+               ;; don't do anything.
+               ;;
+               (setf (retlist-of trace) (or retlist
+                                            'exited-non-locally)
+                     (current-trace) (parent-of trace))))))
+    (when (dialog-traced-p spec)
+      (warn "~a is apparently already traced! Untracing and retracing." spec)
+      (dialog-untrace spec))
+    (swank-backend:wrap spec 'trace-dialog
+                        :before #'before-hook
+                        :after #'after-hook)
+    (pushnew spec *traced-specs*)
+    (format nil "~a is now traced for trace dialog" spec)))
+
+(defslimefun dialog-untrace (spec)
+  (swank-backend:unwrap spec 'trace-dialog)
+  (setq *traced-specs* (remove spec *traced-specs* :test #'equal))
+  (format nil "~a is now untraced for trace dialog" spec))
+
+(defslimefun dialog-toggle-trace (spec)
+  (if (dialog-traced-p spec)
+      (dialog-untrace spec)
+      (dialog-trace spec)))
+
+(defslimefun dialog-traced-p (spec)
+  (find spec *traced-specs* :test #'equal))
+
+(defslimefun dialog-untrace-all ()
+  (untrace)
+  (mapcar #'dialog-untrace *traced-specs*))
+
+(defparameter *dialog-trace-follows-trace* nil)
+
+(setq swank:*after-toggle-trace-hook*
+      #'(lambda (spec traced-p)
+          (when *dialog-trace-follows-trace*
+            (cond (traced-p
+                   (dialog-trace spec)
+                   "traced for trace dialog as well")
+                  (t
+                   (dialog-untrace spec)
+                   "untraced for the trace dialog as well")))))
+
+
+;;;; A special kind of trace call
+;;;
+(defun trace-format (format-spec &rest format-args)
+  "Make a string from FORMAT-SPEC and FORMAT-ARGS and as a trace."
+  (let* ((line (apply #'format nil format-spec format-args)))
+    (make-instance 'trace-entry :spec line
+                                :args format-args
+                                :parent (current-trace)
+                                :retlist nil)))
+
+
+;;;; Reporting to emacs
+;;;
+(defparameter *visitor-idx* 0)
+
+(defparameter *visitor-key* nil)
+
+(defvar *unfinished-traces* '())
+
+(defun describe-trace-for-emacs (trace)
+  `(,(id-of trace)
+    ,(and (parent-of trace) (id-of (parent-of trace)))
+    ,(spec-of trace)
+    ,(loop for arg in (args-of trace)
+           for i from 0
+           collect (list i (swank::to-line arg)))
+    ,(loop for retval in (swank::ensure-list (retlist-of trace))
+           for i from 0
+           collect (list i (swank::to-line retval)))))
+
+(defslimefun report-partial-tree (key)
+  (unless (equal key *visitor-key*)
+    (setq *visitor-idx* 0
+          *visitor-key* key))
+  (let* ((recently-finished
+           (loop with i = 0
+                 for trace in *unfinished-traces*
+                 while (< i *traces-per-report*)
+                 when (completed-p trace)
+                   collect trace
+                   and do
+                     (incf i)
+                     (setq *unfinished-traces*
+                           (remove trace *unfinished-traces*))))
+         (new (loop for i
+                      from (length recently-finished)
+                        below *traces-per-report*
+                    while (< *visitor-idx* (length *traces*))
+                    for trace = (aref *traces* *visitor-idx*)
+                    collect trace
+                    unless (completed-p trace)
+                      do (push trace *unfinished-traces*)
+                    do (incf *visitor-idx*))))
+    (list
+     (mapcar #'describe-trace-for-emacs
+             (append recently-finished new))
+     (- (length *traces*) *visitor-idx*)
+    key)))
+
+(defslimefun report-trace-detail (trace-id)
+  (swank::call-with-bindings
+   swank::*inspector-printer-bindings*
+   #'(lambda ()
+       (let ((trace (find-trace trace-id)))
+         (when trace
+           (append
+            (describe-trace-for-emacs trace)
+            (list (backtrace-of trace)
+                  (swank::to-line trace))))))))
+
+(defslimefun report-specs ()
+  (sort (copy-list *traced-specs*)
+        #'string<
+        :key #'princ-to-string))
+
+(defslimefun report-total ()
+  (length *traces*))
+
+(defslimefun clear-trace-tree ()
+  (setf *current-trace-by-thread* (clrhash *current-trace-by-thread*)
+        *visitor-key* nil
+        *unfinished-traces* nil)
+  (swank-backend:call-with-lock-held
+   *trace-lock*
+   #'(lambda () (setf (fill-pointer *traces*) 0)))
+  nil)
+
+;; HACK: `swank::*inspector-history*' is unbound by default and needs
+;; a reset in that case so that it won't error `swank::inspect-object'
+;; before any other object is inspected in the slime session.
+;;
+(unless (boundp 'swank::*inspector-history*)
+  (swank::reset-inspector))
+
+(defslimefun inspect-trace-part (trace-id part-id type)
+  (let* ((trace (find-trace trace-id))
+         (list (ecase type
+                 (:arg (args-of trace))
+                 (:retval (swank::ensure-list (retlist-of trace))))))
+    (swank::inspect-object (nth part-id list))))
+
+(provide :swank-trace-dialog)
diff --git a/swank-allegro.lisp b/swank-allegro.lisp
index cb6ffe0..b09c793 100644
--- a/swank-allegro.lisp
+++ b/swank-allegro.lisp
@@ -969,3 +969,35 @@
   (loop for name being the hash-keys of excl::*name-to-char-table*
        when (funcall matchp prefix name)
        collect (string-capitalize name)))
+
+
+;;;; Wrap
+
+(defimplementation wrap (spec indicator &key before after replace)
+  (let ((allegro-spec (process-fspec-for-allegro spec)))
+    (excl:fwrap allegro-spec
+                indicator
+                (excl:def-fwrapper allegro-wrapper (&rest args)
+                  (let (retlist completed)
+                    (unwind-protect
+                         (progn
+                           (when before
+                             (funcall before args))
+                           (setq retlist (multiple-value-list (if replace
+                                                                  (funcall replace args)
+                                                                  (excl:call-next-fwrapper))))
+                           (setq completed t)
+                           (values-list retlist))
+                      (when after
+                        (funcall after (if completed
+                                           retlist
+                                           :exited-non-locally)))))))
+    allegro-spec))
+
+(defimplementation unwrap (spec indicator)
+  (let ((allegro-spec (process-fspec-for-allegro spec)))
+    (excl:funwrap allegro-spec indicator)
+    allegro-spec))
+
+(defimplementation wrapped-p (spec indicator)
+  (getf (excl:fwrap-order (process-fspec-for-allegro spec)) indicator))
diff --git a/swank-backend.lisp b/swank-backend.lisp
index ca315b3..4130905 100644
--- a/swank-backend.lisp
+++ b/swank-backend.lisp
@@ -1511,3 +1511,65 @@ COMPLETION-FUNCTION, if non-nil, should be called after saving the image.")
   ;; Can't hang on to an fd-stream from a previous session.
   (setf (symbol-value (find-symbol "*LOG-OUTPUT*" 'swank))
         nil))
+
+;;;; Wrapping
+(definterface wrap (spec indicator &key before after replace)
+  "Intercept future calls to SPEC and surround them in callbacks.
+
+INDICATOR is a symbol identifying a particular wrapping, and is used
+to differentiate between multiple wrappings.
+
+Implementations intercept calls to SPEC and call, in this order:
+
+* the BEFORE callback, if it's provided, with a single argument set to
+  the list of arguments passed to the intercepted call;
+
+* the original definition of SPEC recursively honouring any wrappings
+  previously established under different values of INDICATOR. If the
+  compatible function REPLACE is provided, call that instead.
+
+* the AFTER callback, if it's provided, with a single set to the list
+  of values returned by the previous call, or, if that call exited
+  non-locally, a single descriptive symbol, like :EXITED-NON-LOCALLY."
+  (declare (ignore indicator))
+  (assert (symbolp spec) nil "The default implementation for WRAP allows only simple names")
+  (assert (null (get spec 'slime-wrap)) nil "The default implementation for WRAP allows a single wrapping")
+  (let* ((saved (symbol-function spec))
+         (replacement (lambda (&rest args)
+                        (let (retlist completed)
+                          (unwind-protect
+                              (progn
+                                (when before
+                                  (funcall before args))
+                                (setq retlist (multiple-value-list (apply (or replace
+                                                                              saved) args)))
+                                (setq completed t)
+                                (values-list retlist))
+                            (when after
+                              (funcall after (if completed
+                                                 retlist
+                                               :exited-non-locally))))))))
+    (setf (get spec 'slime-wrap) (list saved replacement))
+    (setf (symbol-function spec) replacement))
+  spec)
+
+(definterface unwrap (spec indicator)
+  "Remove from SPEC any wrappings tagged with INDICATOR."
+  (if (wrapped-p spec indicator)
+      (setf (symbol-function spec) (first (get spec 'slime-wrap)))
+      (cerror "All right, so I did" "Hmmm, ~a is not correctly wrapped, you probably redefined it" spec))
+  (setf (get spec 'slime-wrap) nil)
+  spec)
+
+(definterface wrapped-p (spec indicator)
+  "Returns true if SPEC is wrapped with INDICATOR."
+  (declare (ignore indicator))
+  (and (symbolp spec)
+       (let ((prop-value (get spec 'slime-wrap)))
+         (cond ((and prop-value
+                     (not (eq (second prop-value)
+                              (symbol-function spec))))
+                (warn "~a appears to be incorrectly wrapped" spec)
+                nil)
+               (prop-value t)
+               (t nil)))))
diff --git a/swank-loader.lisp b/swank-loader.lisp
index 72a74a9..eadb7fd 100644
--- a/swank-loader.lisp
+++ b/swank-loader.lisp
@@ -228,7 +228,7 @@ If LOAD is true, load the fasl file."
     swank-hyperdoc
     #+sbcl swank-sbcl-exts
     swank-mrepl
-    )
+    swank-trace-dialog)
   "List of names for contrib modules.")
 
 (defun append-dir (absolute name)
diff --git a/swank-sbcl.lisp b/swank-sbcl.lisp
index 9669dac..012b402 100644
--- a/swank-sbcl.lisp
+++ b/swank-sbcl.lisp
@@ -1882,3 +1882,34 @@ stack."
                              (zerop (sb-posix:wexitstatus status))))))))))))
 
 (pushnew 'deinit-log-output sb-ext:*save-hooks*)
+
+;;; slime-trace-dialog
+;;;
+(defimplementation wrap (spec indicator &key before after replace)
+  (when (wrapped-p spec indicator)
+    (warn "~a already wrapped with indicator ~a, unwrapping first" spec indicator)
+    (sb-int:unencapsulate spec indicator))
+   (sb-int:encapsulate spec indicator `(sbcl-wrap ',spec ,before ,after ,replace)))
+
+(defimplementation unwrap (spec indicator)
+  (sb-int:unencapsulate spec indicator))
+
+(defimplementation wrapped-p (spec indicator)
+  (sb-int:encapsulated-p spec indicator))
+
+(in-package :sb-int)
+
+(defun swank-backend::sbcl-wrap (spec before after replace)
+  (declare (special sb-int:basic-definition sb-int:arg-list))
+  (let (retlist completed)
+    (unwind-protect
+         (progn
+           (when before
+             (funcall before sb-int:arg-list))
+           (setq retlist (multiple-value-list (if replace
+                                                  (funcall replace sb-int:arg-list)
+                                                  (apply sb-int:basic-definition sb-int:arg-list))))
+           (setq completed t)
+           (values-list retlist))
+      (when after
+        (funcall after (if completed retlist :exited-non-locally))))))
diff --git a/swank.lisp b/swank.lisp
index 3cfc6ea..cd24d39 100644
--- a/swank.lisp
+++ b/swank.lisp
@@ -65,7 +65,8 @@
            #:eval-in-emacs
            #:y-or-n-p-in-emacs
            #:*find-definitions-right-trim*
-           #:*find-definitions-left-trim*))
+           #:*find-definitions-left-trim*
+           #:*after-toggle-trace-hook*))
 
 (in-package :swank)
 
@@ -2845,16 +2846,32 @@ Include the nicknames if NICKNAMES is true."
 (defun tracedp (fspec)
   (member fspec (eval '(trace))))
 
+(defvar *after-toggle-trace-hook* nil
+  "Hook called whenever a SPEC is traced or untraced.
+
+If non-nil, called with two arguments SPEC and TRACED-P." )
 (defslimefun swank-toggle-trace (spec-string)
-  (let ((spec (from-string spec-string)))
-    (cond ((consp spec) ; handle complicated cases in the backend
+  (let* ((spec (from-string spec-string))
+         (retval (cond ((consp spec) ; handle complicated cases in the backend
                         (toggle-trace spec))
                        ((tracedp spec)
                         (eval `(untrace ,spec))
                         (format nil "~S is now untraced." spec))
                        (t
                         (eval `(trace ,spec))
-	   (format nil "~S is now traced." spec)))))
+                        (format nil "~S is now traced." spec))))
+         (traced-p (let* ((tosearch "is now traced.")
+                          (start (- (length retval)
+                                    (length tosearch)))
+                          (end (+ start (length tosearch))))
+                     (search tosearch (subseq retval start end))))
+         (hook-msg (when *after-toggle-trace-hook*
+                     (funcall *after-toggle-trace-hook*
+                              spec
+                              traced-p))))
+    (if hook-msg
+        (format nil "~a~%(also ~a)" retval hook-msg)
+        retval)))
 
 (defslimefun untrace-all ()
   (untrace))
