js/ipc/JavaScriptLogging.h
author Bill McCloskey <wmccloskey@mozilla.com>
Thu, 18 Dec 2014 17:34:24 -0800
changeset 246354 c68a69cdae8a582d922ea2a15d242007e602cfb0
parent 233243 ca047d5fafbaa39595026bb73e011e69610736aa
child 247733 2fbdaa8a5ea77c689fe7003217866f1674a0292d
permissions -rw-r--r--
Bug 1102637 - [e10s] Log the address of objects sent using CPOWs (r=mrbkap)

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sw=4 et tw=80:
 *
 * 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/. */

#ifndef mozilla_jsipc_JavaScriptLogging__
#define mozilla_jsipc_JavaScriptLogging__

#include "nsString.h"
#include "nsPrintfCString.h"
#include "jsfriendapi.h"
#include "jswrapper.h"

namespace mozilla {
namespace jsipc {

#define LOG(...)						               \
    PR_BEGIN_MACRO                                                             \
    if (LoggingEnabled()) {                                                    \
	Logging log(this, cx);					               \
	log.print(__VA_ARGS__);					               \
    }                                                                          \
    PR_END_MACRO

#define LOG_STACK()		                                               \
    PR_BEGIN_MACRO                                                             \
    if (StackLoggingEnabled()) {                                               \
        js_DumpBacktrace(cx);	                                               \
    }                                                                          \
    PR_END_MACRO

struct ReceiverObj
{
    ObjectId id;
    explicit ReceiverObj(ObjectId id) : id(id) {}
};

struct InVariant
{
    JSVariant variant;
    explicit InVariant(const JSVariant &variant) : variant(variant) {}
};

struct OutVariant
{
    JSVariant variant;
    explicit OutVariant(const JSVariant &variant) : variant(variant) {}
};

struct Identifier
{
    JSIDVariant variant;
    explicit Identifier(const JSIDVariant &variant) : variant(variant) {}
};

class Logging
{
  public:
    Logging(JavaScriptShared *shared, JSContext *cx) : shared(shared), cx(cx) {}

    void print(const nsCString &str) {
        const char *side = shared->isParent() ? "from child" : "from parent";
        printf("CPOW %s: %s\n", side, str.get());
    }

    void print(const char *str) {
        print(nsCString(str));
    }
    template<typename T1>
    void print(const char *fmt, const T1 &a1) {
        nsAutoCString tmp1;
        format(a1, tmp1);
        print(nsPrintfCString(fmt, tmp1.get()));
    }
    template<typename T1, typename T2>
    void print(const char *fmt, const T1 &a1, const T2 &a2) {
        nsAutoCString tmp1;
        nsAutoCString tmp2;
        format(a1, tmp1);
        format(a2, tmp2);
        print(nsPrintfCString(fmt, tmp1.get(), tmp2.get()));
    }
    template<typename T1, typename T2, typename T3>
    void print(const char *fmt, const T1 &a1, const T2 &a2, const T3 &a3) {
        nsAutoCString tmp1;
        nsAutoCString tmp2;
        nsAutoCString tmp3;
        format(a1, tmp1);
        format(a2, tmp2);
        format(a3, tmp3);
        print(nsPrintfCString(fmt, tmp1.get(), tmp2.get(), tmp3.get()));
    }

    void format(const nsString &str, nsCString &out) {
        out = NS_ConvertUTF16toUTF8(str);
    }

    void formatObject(bool incoming, bool local, ObjectId id, nsCString &out) {
        const char *side, *objDesc;
        void *ptr;

        if (local == incoming) {
            JS::RootedObject obj(cx);
            obj = shared->objects_.find(id);
            if (obj) {
                JSAutoCompartment ac(cx, obj);
                objDesc = js_ObjectClassName(cx, obj);
            } else {
                objDesc = "<dead object>";
            }

            side = shared->isParent() ? "parent" : "child";
            ptr = js::UncheckedUnwrap(obj, true);
        } else {
            objDesc = "<cpow>";
            side = shared->isParent() ? "child" : "parent";
            ptr = nullptr;
        }

        out = nsPrintfCString("<%s %s:%d:%p>", side, objDesc, id, ptr);
    }


    void format(const ReceiverObj &obj, nsCString &out) {
        formatObject(true, true, obj.id, out);
    }

    void format(const nsTArray<JSParam> &values, nsCString &out) {
        nsAutoCString tmp;
        out.Truncate();
        for (size_t i = 0; i < values.Length(); i++) {
            if (i)
                out.AppendLiteral(", ");
            if (values[i].type() == JSParam::Tvoid_t) {
                out.AppendLiteral("<void>");
            } else {
                format(InVariant(values[i].get_JSVariant()), tmp);
                out += tmp;
            }
        }
    }

    void format(const InVariant &value, nsCString &out) {
        format(true, value.variant, out);
    }

    void format(const OutVariant &value, nsCString &out) {
        format(false, value.variant, out);
    }

    void format(bool incoming, const JSVariant &value, nsCString &out) {
        switch (value.type()) {
          case JSVariant::TUndefinedVariant: {
              out = "undefined";
              break;
          }
          case JSVariant::TNullVariant: {
              out = "null";
              break;
          }
          case JSVariant::TnsString: {
              nsAutoCString tmp;
              format(value.get_nsString(), tmp);
              out = nsPrintfCString("\"%s\"", tmp.get());
              break;
          }
          case JSVariant::TObjectVariant: {
              const ObjectVariant &ovar = value.get_ObjectVariant();
              if (ovar.type() == ObjectVariant::TLocalObject)
                  formatObject(incoming, true, ObjectId::deserialize(ovar.get_LocalObject().serializedId()), out);
              else
                  formatObject(incoming, false, ObjectId::deserialize(ovar.get_RemoteObject().serializedId()), out);
              break;
          }
          case JSVariant::TSymbolVariant: {
              out = "<Symbol>";
              break;
          }
          case JSVariant::Tdouble: {
              out = nsPrintfCString("%.0f", value.get_double());
              break;
          }
          case JSVariant::Tbool: {
              out = value.get_bool() ? "true" : "false";
              break;
          }
          case JSVariant::TJSIID: {
              out = "<JSIID>";
              break;
          }
          default: {
              out = "<JSIID>";
              break;
          }
        }
    }

    void format(const Identifier &id, nsCString &out) {
        switch (id.variant.type()) {
          case JSIDVariant::TSymbolVariant: {
              out = "<Symbol>";
              break;
          }
          case JSIDVariant::TnsString: {
              nsAutoCString tmp;
              format(id.variant.get_nsString(), tmp);
              out = nsPrintfCString("\"%s\"", tmp.get());
              break;
          }
          case JSIDVariant::Tint32_t: {
              out = nsPrintfCString("%d", id.variant.get_int32_t());
              break;
          }
          default: {
              out = "Unknown";
              break;
          }
      }
    }

  private:
    JavaScriptShared *shared;
    JSContext *cx;
};

}
}

#endif