diff -r f457cea31b95 -r 163253a6e979 configs/example/fs.py --- a/configs/example/fs.py Thu Nov 03 15:24:21 2011 -0500 +++ b/configs/example/fs.py Thu Nov 03 15:24:29 2011 -0500 @@ -72,6 +72,10 @@ # System options parser.add_option("--kernel", action="store", type="string") parser.add_option("--script", action="store", type="string") +parser.add_option("--frame-capture", action="store_true", + help="Stores changed frame buffers from the VNC server to compressed "\ + "files in the gem5 output directory") + if buildEnv['TARGET_ISA'] == "arm": parser.add_option("--bare-metal", action="store_true", help="Provide the raw system without the linux specific bits") @@ -205,4 +209,7 @@ if options.timesync: root.time_sync_enable = True +if options.frame_capture: + VncServer.frame_capture = True + Simulation.run(options, root, test_sys, FutureClass) diff -r f457cea31b95 -r 163253a6e979 src/base/bitmap.hh --- a/src/base/bitmap.hh Thu Nov 03 15:24:21 2011 -0500 +++ b/src/base/bitmap.hh Thu Nov 03 15:24:29 2011 -0500 @@ -36,6 +36,7 @@ * * Authors: William Wang * Ali Saidi + * Chris Emmons */ #ifndef __BASE_BITMAP_HH__ #define __BASE_BITMAP_HH__ @@ -62,6 +63,9 @@ */ Bitmap(VideoConvert::Mode mode, uint16_t w, uint16_t h, uint8_t *d); + /** Destructor */ + ~Bitmap(); + /** Provide the converter with the data that should be output. It will be * converted into rgb8888 and write out when write() is called. * @param d the data @@ -71,7 +75,13 @@ /** Write the provided data into the fstream provided * @param bmp stream to write to */ - void write(std::ostream *bmp); + void write(std::ostream *bmp) const; + + /** Gets a hash over the bitmap for quick comparisons to other bitmaps. + * @return hash of the bitmap + */ + uint64_t getHash() const { return vc.getHash(data); } + private: VideoConvert::Mode mode; @@ -81,6 +91,9 @@ VideoConvert vc; + mutable char *headerBuffer; + static const size_t sizeofHeaderBuffer; + struct Magic { unsigned char magic_number[2]; diff -r f457cea31b95 -r 163253a6e979 src/base/bitmap.cc --- a/src/base/bitmap.cc Thu Nov 03 15:24:21 2011 -0500 +++ b/src/base/bitmap.cc Thu Nov 03 15:24:29 2011 -0500 @@ -36,6 +36,7 @@ * * Authors: William Wang * Ali Saidi + * Chris Emmons */ #include @@ -43,29 +44,50 @@ #include "base/bitmap.hh" #include "base/misc.hh" +const size_t Bitmap::sizeofHeaderBuffer = sizeof(Magic) + sizeof(Header) + + sizeof(Info); + // bitmap class ctor Bitmap::Bitmap(VideoConvert::Mode _mode, uint16_t w, uint16_t h, uint8_t *d) : mode(_mode), height(h), width(w), data(d), - vc(mode, VideoConvert::rgb8888, width, height) + vc(mode, VideoConvert::rgb8888, width, height), headerBuffer(0) { } +Bitmap::~Bitmap() { + if (headerBuffer) + delete [] headerBuffer; +} + void -Bitmap::write(std::ostream *bmp) +Bitmap::write(std::ostream *bmp) const { assert(data); - // For further information see: http://en.wikipedia.org/wiki/BMP_file_format - Magic magic = {{'B','M'}}; - Header header = {sizeof(VideoConvert::Rgb8888) * width * height , 0, 0, 54}; - Info info = {sizeof(Info), width, height, 1, - sizeof(VideoConvert::Rgb8888) * 8, 0, - sizeof(VideoConvert::Rgb8888) * width * height, 1, 1, 0, 0}; + // header is always the same for a bitmap object; compute the info once per + // bitmap object + if (!headerBuffer) { + // For further information see: + // http://en.wikipedia.org/wiki/BMP_file_format + Magic magic = {{'B','M'}}; + Header header = {sizeof(VideoConvert::Rgb8888) * width * height, + 0, 0, 54}; + Info info = {sizeof(Info), width, height, 1, + sizeof(VideoConvert::Rgb8888) * 8, 0, + sizeof(VideoConvert::Rgb8888) * width * height, 1, 1, 0, 0}; - bmp->write(reinterpret_cast(&magic), sizeof(magic)); - bmp->write(reinterpret_cast(&header), sizeof(header)); - bmp->write(reinterpret_cast(&info), sizeof(info)); + char *p = headerBuffer = new char[sizeofHeaderBuffer]; + memcpy(p, &magic, sizeof(Magic)); + p += sizeof(Magic); + memcpy(p, &header, sizeof(Header)); + p += sizeof(Header); + memcpy(p, &info, sizeof(Info)); + } + // 1. write the header + bmp->write(headerBuffer, sizeofHeaderBuffer); + + // 2. write the bitmap data uint8_t *tmp = vc.convert(data); uint32_t *tmp32 = (uint32_t*)tmp; diff -r f457cea31b95 -r 163253a6e979 src/base/vnc/VncServer.py --- a/src/base/vnc/VncServer.py Thu Nov 03 15:24:21 2011 -0500 +++ b/src/base/vnc/VncServer.py Thu Nov 03 15:24:29 2011 -0500 @@ -43,3 +43,4 @@ type = 'VncServer' port = Param.TcpPort(5900, "listen port") number = Param.Int(0, "vnc client number") + frame_capture = Param.Bool(False, "capture changed frames to files") diff -r f457cea31b95 -r 163253a6e979 src/base/vnc/convert.hh --- a/src/base/vnc/convert.hh Thu Nov 03 15:24:21 2011 -0500 +++ b/src/base/vnc/convert.hh Thu Nov 03 15:24:29 2011 -0500 @@ -44,6 +44,7 @@ #ifndef __BASE_VNC_CONVERT_HH__ #define __BASE_VNC_CONVERT_HH__ +#include #include "base/bitunion.hh" class VideoConvert @@ -107,12 +108,21 @@ * @param fb the frame buffer to convert * @return the converted data (user must free) */ - uint8_t* convert(uint8_t *fb); + uint8_t* convert(const uint8_t *fb) const; /** Return the number of pixels that this buffer specifies * @return number of pixels */ - int area() { return width * height; } + int area() const { return width * height; } + + /** + * Returns a hash on the raw data. + * + * @return hash of the buffer + */ + inline uint64_t getHash(const uint8_t *fb) const { + return adler32(0UL, fb, width * height); + } private: @@ -121,7 +131,7 @@ * @param fb the data to convert * @return converted data */ - uint8_t* bgr8888rgb8888(uint8_t *fb); + uint8_t* bgr8888rgb8888(const uint8_t *fb) const; /** * Convert a bgr565 or rgb565 input to rgb8888. @@ -129,7 +139,7 @@ * @param bgr true if the input data is bgr565 * @return converted data */ - uint8_t* m565rgb8888(uint8_t *fb, bool bgr); + uint8_t* m565rgb8888(const uint8_t *fb, bool bgr) const; Mode inputMode; Mode outputMode; diff -r f457cea31b95 -r 163253a6e979 src/base/vnc/convert.cc --- a/src/base/vnc/convert.cc Thu Nov 03 15:24:21 2011 -0500 +++ b/src/base/vnc/convert.cc Thu Nov 03 15:24:29 2011 -0500 @@ -67,7 +67,7 @@ } uint8_t* -VideoConvert::convert(uint8_t *fb) +VideoConvert::convert(const uint8_t *fb) const { switch (inputMode) { case bgr565: @@ -82,7 +82,7 @@ } uint8_t* -VideoConvert::m565rgb8888(uint8_t *fb, bool bgr) +VideoConvert::m565rgb8888(const uint8_t *fb, bool bgr) const { uint8_t *out = new uint8_t[area() * sizeof(uint32_t)]; uint32_t *out32 = (uint32_t*)out; @@ -113,7 +113,7 @@ uint8_t* -VideoConvert::bgr8888rgb8888(uint8_t *fb) +VideoConvert::bgr8888rgb8888(const uint8_t *fb) const { uint8_t *out = new uint8_t[area() * sizeof(uint32_t)]; uint32_t *out32 = (uint32_t*)out; @@ -136,4 +136,21 @@ return out; } +/* +uint64_t +VideoConvert::getHash(const uint8_t *fb) const +{ + const uint8_t *fb_e = fb + area(); + uint64_t hash = 1; + while (fb < fb_e - 8) { + hash += *((const uint64_t*)fb); + fb += 8; + } + + while (fb < fb_e) { + hash += *(fb++); + } + + return hash; +}*/ diff -r f457cea31b95 -r 163253a6e979 src/base/vnc/vncserver.hh --- a/src/base/vnc/vncserver.hh Thu Nov 03 15:24:21 2011 -0500 +++ b/src/base/vnc/vncserver.hh Thu Nov 03 15:24:29 2011 -0500 @@ -48,6 +48,7 @@ #include #include "base/vnc/convert.hh" +#include "base/bitmap.hh" #include "base/circlebuf.hh" #include "base/pollevent.hh" #include "base/socket.hh" @@ -55,6 +56,7 @@ #include "params/VncServer.hh" #include "sim/sim_object.hh" + /** * A device that expects to receive input from the vnc server should derrive * (through mulitple inheritence if necessary from VncKeyboard or VncMouse @@ -316,7 +318,25 @@ /** The video converter that transforms data for us */ VideoConvert *vc; + /** Flag indicating whether to capture snapshots of frame buffer or not */ + bool captureEnabled; + + /** Current frame number being captured to a file */ + int captureCurrentFrame; + + /** Directory to store captured frames to */ + std::string captureOutputDirectory; + + /** Computed hash of the last captured frame */ + uint64_t captureLastHash; + + /** Cached bitmap object for writing out frame buffers to file */ + Bitmap *captureBitmap; + protected: + /** Captures the current frame buffer to a file */ + void captureFrameBuffer(); + /** * vnc client Interface */ @@ -449,6 +469,8 @@ setDirty() { sendUpdate = true; + if (captureEnabled) + captureFrameBuffer(); sendFrameBufferUpdate(); } diff -r f457cea31b95 -r 163253a6e979 src/base/vnc/vncserver.cc --- a/src/base/vnc/vncserver.cc Thu Nov 03 15:24:21 2011 -0500 +++ b/src/base/vnc/vncserver.cc Thu Nov 03 15:24:29 2011 -0500 @@ -43,7 +43,10 @@ */ #include +#include #include +#include +#include #include #include @@ -52,11 +55,14 @@ #include "base/vnc/vncserver.hh" #include "base/atomicio.hh" +#include "base/bitmap.hh" #include "base/misc.hh" +#include "base/output.hh" #include "base/socket.hh" #include "base/trace.hh" #include "debug/VNC.hh" #include "sim/byteswap.hh" +#include "sim/core.hh" using namespace std; @@ -98,14 +104,14 @@ : SimObject(p), listenEvent(NULL), dataEvent(NULL), number(p->number), dataFd(-1), _videoWidth(1), _videoHeight(1), clientRfb(0), keyboard(NULL), mouse(NULL), sendUpdate(false), videoMode(VideoConvert::UnknownMode), - vc(NULL) + vc(NULL), captureEnabled(p->frame_capture), captureCurrentFrame(0), + captureLastHash(0), captureBitmap(0) { if (p->port) listen(p->port); curState = WaitForProtocolVersion; - // currently we only support this one pixel format // unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha) // keep it around for telling the client and making @@ -121,6 +127,14 @@ pixelFormat.greenshift = 8; pixelFormat.blueshift = 0; + if (captureEnabled) { + // remove existing frame output directory if it exists, then create a + // clean empty directory + const string FRAME_OUTPUT_SUBDIR = "frames_" + name(); + simout.remove(FRAME_OUTPUT_SUBDIR, true); + captureOutputDirectory = simout.createSubdirectory( + FRAME_OUTPUT_SUBDIR); + } DPRINTF(VNC, "Vnc server created at port %d\n", p->port); } @@ -686,6 +700,16 @@ vc = new VideoConvert(mode, VideoConvert::rgb8888, videoWidth(), videoHeight()); + if (captureEnabled) { + // create bitmap of the frame with new attributes + if (captureBitmap) + delete captureBitmap; + + assert(clientRfb); + captureBitmap = new Bitmap(videoMode, width, height, clientRfb); + assert(captureBitmap); + } + if (dataFd > 0 && clientRfb && curState == NormalPhase) { if (supportsResizeEnc) sendFrameBufferResized(); @@ -702,3 +726,29 @@ { return new VncServer(this); } + +void +VncServer::captureFrameBuffer() +{ + assert(captureBitmap); + + // skip identical frames + uint64_t new_hash = captureBitmap->getHash(); + if (captureLastHash == new_hash) + return; + captureLastHash = new_hash; + + // get the filename for the current frame + char frameFilenameBuffer[64]; + snprintf(frameFilenameBuffer, 64, "fb.%06d.%lld.bmp.gz", + captureCurrentFrame, static_cast(curTick())); + const string frameFilename(frameFilenameBuffer); + + // create the compressed framebuffer file + ostream *fb_out = simout.create(captureOutputDirectory + frameFilename, + true); + captureBitmap->write(fb_out); + simout.close(fb_out); + + ++captureCurrentFrame; +}