/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nsISupports.idl"

%{C++
#include <stdio.h>

class nsCycleCollectorLogger;
%}

[ptr] native FILE(FILE);
[ptr] native nsCycleCollectorLoggerPtr(nsCycleCollectorLogger);
interface nsIFile;

/**
 * A set of interfaces for recording the cycle collector's work. An instance
 * of nsICycleCollectorListener can be configured to enable various
 * options, then passed to the cycle collector when it runs.
 * Note that additional logging options are available by setting environment
 * variables, as described at the top of nsCycleCollector.cpp.
 */

/**
 * nsICycleCollectorHandler is the interface JS code should implement to
 * receive the results logged by an nsICycleCollectorListener
 * instance. Pass an instance of this to the logger's 'processNext' method
 * after the collection has run. This will describe the objects the cycle
 * collector visited, the edges it found, and the conclusions it reached
 * about the liveness of objects.
 *
 * In more detail:
 * - For each node in the graph:
 *   - a call is made to either |noteRefCountedObject| or |noteGCedObject|, to
 *     describe the node itself; and
 *   - for each edge starting at that node, a call is made to |noteEdge|.
 *
 * - For each weak map entry, a call is made to |noteWeakMapEntry|. Each of the
 *   values passed to noteWeakMapEntry can be null, which indicates that it is
 *   something that is not part of a garbage cycle.
 *
 * - Then, a series of calls are made to:
 *   - |describeRoot|, for reference-counted nodes that the CC has identified as
 *     being alive because there are unknown references to those nodes.
 *   - |describeGarbage|, for nodes the cycle collector has identified as garbage.
 *
 *   Any node not mentioned in a call to |describeRoot| or |describeGarbage| is
 *   neither a root nor garbage. The cycle collector was able to find all of the
 *   edges implied by the node's reference count.
 */
[scriptable, uuid(7f093367-1492-4b89-87af-c01dbc831246)]
interface nsICycleCollectorHandler : nsISupports
{
    void noteRefCountedObject(in ACString aAddress,
                              in unsigned long aRefCount,
                              in ACString aObjectDescription);
    void noteGCedObject(in ACString aAddress,
                        in boolean aMarked,
                        in ACString aObjectDescription,
                        in ACString aCompartmentAddress);
    void noteEdge(in ACString aFromAddress,
                  in ACString aToAddress,
                  in ACString aEdgeName);
    void noteWeakMapEntry(in ACString aMap,
                          in ACString aKey,
                          in ACString aKeyDelegate,
                          in ACString aValue);
    void describeRoot(in ACString aAddress,
                      in unsigned long aKnownEdges);
    void describeGarbage(in ACString aAddress);
};


/**
 * This interface allows replacing the log-writing backend for an
 * nsICycleCollectorListener.  As this interface is also called while
 * the cycle collector is running, it cannot be implemented in JS.
 */
[scriptable, builtinclass, uuid(3ad9875f-d0e4-4ac2-87e3-f127f6c02ce1)]
interface nsICycleCollectorLogSink : nsISupports
{
  // aGCLog can be null, if the sink was not configured to create a GC log.
  [noscript] void open(out FILE aGCLog, out FILE aCCLog);

  void closeGCLog();

  void closeCCLog();

  // This string will appear somewhere in the log's filename.
  attribute AString filenameIdentifier;

  // This is the process ID; it can be changed if logging is on behalf
  // of another process.
  attribute int32_t processIdentifier;

  // The GC log file, if logging to files.
  readonly attribute nsIFile gcLog;

  // The CC log file, if logging to files.
  readonly attribute nsIFile ccLog;
};


/**
 * This interface is used to configure some reporting options for the cycle
 * collector. This interface cannot be implemented by JavaScript code, as it
 * is called while the cycle collector is running.
 *
 * To analyze cycle collection data in JS:
 *
 * - Create an instance of nsICycleCollectorListener, which implements this
 *   interface. In C++, this can be done by calling
 *   nsCycleCollector_createLogger(). In JS, this can be done by calling
 *   Components.utils.createCCLogger().
 *
 * - Set its |disableLog| property to true. This prevents the logger from
 *   printing messages about each method call to a temporary log file.
 *
 * - Set its |wantAfterProcessing| property to true. This tells the logger
 *   to record calls to its methods in memory. The |processNext| method
 *   returns events from this record.
 *
 * - Perform a collection using the logger. For example, call
 *   |nsIDOMWindowUtils|'s |garbageCollect| method, passing the logger as
 *   the |aListener| argument.
 *
 * - When the collection is complete, loop calling the logger's
 *   |processNext| method, passing a JavaScript object that implements
 *   nsICycleCollectorHandler. This JS code is free to allocate and operate
 *   on objects however it pleases: the cycle collector has finished its
 *   work, and the JS code is simply consuming recorded data.
 */
[scriptable, builtinclass, uuid(703b53b6-24f6-40c6-9ea9-aeb2dc53d170)]
interface nsICycleCollectorListener : nsISupports
{
    // Return a listener that directs the cycle collector to traverse
    // objects that it knows won't be collectable.
    //
    // Note that even this listener will not visit every node in the heap;
    // the cycle collector can't see the entire heap. But while this
    // listener is in use, the collector disables some optimizations it
    // normally uses to avoid certain classes of objects that are certainly
    // alive. So, if your purpose is to get a view of the portion of the
    // heap that is of interest to the cycle collector, and not simply find
    // garbage, then you should use the listener this returns.
    //
    // Note that this does not necessarily return a new listener; rather, it may
    // simply set a flag on this listener (a side effect!) and return it.
    nsICycleCollectorListener allTraces();

    // True if this listener will behave like one returned by allTraces().
    readonly attribute boolean wantAllTraces;

    // If true, do not log each method call to a temporary file.
    // Initially false.
    attribute boolean disableLog;

    // If |disableLog| is false, this object will be sent the log text.
    attribute nsICycleCollectorLogSink logSink;

    // If true, record all method calls in memory, to be retrieved later
    // using |processNext|. Initially false.
    attribute boolean wantAfterProcessing;

    // Report the next recorded event to |aHandler|, and remove it from the
    // record. Return false if there isn't anything more to process.
    //
    // Note that we only record events to report here if our
    // |wantAfterProcessing| property is true.
    boolean processNext(in nsICycleCollectorHandler aHandler);

    // Return the current object as an nsCycleCollectorLogger*, which is the
    // only class that should be implementing this interface. We need the
    // concrete implementation type to help the GC rooting analysis.
    [noscript] nsCycleCollectorLoggerPtr asLogger();
};
