layout/printing/DrawEventRecorder.h
author Markus Stange <mstange@themasta.com>
Fri, 18 May 2018 17:36:30 -0400
changeset 421027 1d38a4cf5a4a7e54adbdb5ffa31c944e41e27e18
parent 400242 877e173a91045756a8abd578377f28a542a77461
child 434992 52dabf91acd0e3fb7f1683b9f80df072f24cc8e4
permissions -rw-r--r--
Bug 1462784 - Remove EVENTS category. r=njn Categories are useful to indicate: This much % of time was spent in this category. The EVENTS category isn't a very good match for this. This category is currently only set on labels of functions that handle the processing of an event. But those functions are usually closer to the base of the stack, and the actual CPU work during the processing of an event is usually in another category closer to the top of the stack, e.g. in JS if we're running an event handler, or in LAYOUT if we're hit testing the position of the event. This changeset removes the EVENTS category and replaces all uses of it with the OTHER category. MozReview-Commit-ID: JPm5hQiBkvp

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 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_layout_printing_DrawEventRecorder_h
#define mozilla_layout_printing_DrawEventRecorder_h

#include <memory>

#include "mozilla/gfx/DrawEventRecorder.h"
#include "mozilla/gfx/RecordingTypes.h"
#include "prio.h"

namespace mozilla {
namespace layout {

class PRFileDescStream : public mozilla::gfx::EventStream {
  // Most writes, as seen in the print IPC use case, are very small (<32 bytes),
  // with a small number of very large (>40KB) writes. Writes larger than this
  // value are not buffered.
  static const size_t kBufferSize = 1024;
public:
  PRFileDescStream() : mFd(nullptr), mBuffer(nullptr), mBufferPos(0),
                       mGood(true) {}
  PRFileDescStream(const PRFileDescStream& other) = delete;
  ~PRFileDescStream() { Close(); }

  void OpenFD(PRFileDesc* aFd)
  {
    MOZ_DIAGNOSTIC_ASSERT(!IsOpen());
    mFd = aFd;
    mGood = !!mFd;
    mBuffer.reset(new uint8_t[kBufferSize]);
    mBufferPos = 0;
  }

  void Close() {
    // We need to be API compatible with std::ostream, and so we silently handle
    // closes on a closed FD.
    if (IsOpen()) {
      Flush();
      PR_Close(mFd);
      mFd = nullptr;
      mBuffer.reset();
      mBufferPos = 0;
    }
  }

  bool IsOpen() {
    return mFd != nullptr;
  }

  void Flush() {
    // See comment in Close().
    if (IsOpen() && mBufferPos > 0) {
      PRInt32 length =
        PR_Write(mFd, static_cast<const void*>(mBuffer.get()), mBufferPos);
      mGood = length >= 0 && static_cast<size_t>(length) == mBufferPos;
      mBufferPos = 0;
    }
  }

  void Seek(PRInt64 aOffset, PRSeekWhence aWhence)
  {
    Flush();
    PRInt64 pos = PR_Seek64(mFd, aOffset, aWhence);
    mGood = pos != -1;
  }

  void write(const char* aData, size_t aSize) override {
    if (!good()) {
      return;
    }

    // See comment in Close().
    if (IsOpen()) {
      // If we're writing more data than could ever fit in our buffer, flush the
      // buffer and write directly.
      if (aSize > kBufferSize) {
        Flush();
        PRInt32 length = PR_Write(mFd, static_cast<const void*>(aData), aSize);
        mGood = length >= 0 && static_cast<size_t>(length) == aSize;
      // If our write could fit in our buffer, but doesn't because the buffer
      // is partially full, write to the buffer, flush the buffer, and then
      // write the rest of the data to the buffer.
      } else if (aSize > AvailableBufferSpace()) {
        size_t length = AvailableBufferSpace();
        WriteToBuffer(aData, length);
        Flush();

        WriteToBuffer(aData + length, aSize - length);
      // Write fits in the buffer.
      } else {
        WriteToBuffer(aData, aSize);
      }
    }
  }

  void read(char* aOut, size_t aSize) override {
    if (!good()) {
      return;
    }

    Flush();
    PRInt32 res = PR_Read(mFd, static_cast<void*>(aOut), aSize);
    mGood = res >= 0 && (static_cast<size_t>(res) == aSize);
  }

  bool good() {
    return mGood;
  }

private:
  size_t AvailableBufferSpace() {
    return kBufferSize - mBufferPos;
  }

  void WriteToBuffer(const char* aData, size_t aSize) {
    MOZ_ASSERT(aSize <= AvailableBufferSpace());
    memcpy(mBuffer.get() + mBufferPos, aData, aSize);
    mBufferPos += aSize;
  }

  PRFileDesc* mFd;
  std::unique_ptr<uint8_t[]> mBuffer;
  size_t mBufferPos;
  bool mGood;
};

class DrawEventRecorderPRFileDesc : public gfx::DrawEventRecorderPrivate
{
public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPRFileDesc, override)
  explicit DrawEventRecorderPRFileDesc(){};
  ~DrawEventRecorderPRFileDesc();

  void RecordEvent(const gfx::RecordedEvent& aEvent) override;

  /**
   * Returns whether a recording file is currently open.
   */
  bool IsOpen();

  /**
   * Opens the recorder with the provided PRFileDesc *.
   */
  void OpenFD(PRFileDesc* aFd);

  /**
   * Closes the file so that it can be processed. The recorder does NOT forget
   * which objects it has recorded. This can be used with OpenNew, so that a
   * recording can be processed in chunks. The file must be open.
   */
  void Close();

private:
  void Flush() override;

  PRFileDescStream mOutputStream;
};

}

}

#endif /* mozilla_layout_printing_DrawEventRecorder_h */