]> git.cworth.org Git - apitrace/commitdiff
Allow to use call sets instead of call numbers / frequencies.
authorJosé Fonseca <jose.r.fonseca@gmail.com>
Thu, 26 Jan 2012 19:08:32 +0000 (19:08 +0000)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Thu, 26 Jan 2012 19:08:32 +0000 (19:08 +0000)
Inspired on Carl Worth's --call=Range option, but:

- with the extra machinery to allow semantic divisors, in addition to numeric ones.

- allows reading the call numbers of a text file.

CMakeLists.txt
README.markdown
cli/cli_dump.cpp
common/trace_callset.cpp [new file with mode: 0644]
common/trace_callset.hpp [new file with mode: 0644]
glretrace.hpp
glretrace.py
glretrace_main.cpp
scripts/retracediff.py

index 24e2afc06bcdeb5e52a6e8ccb7ce9f3e1a2851de..22f7677570f9a8c0be9ca6d70ff500392a77fd0b 100755 (executable)
@@ -271,6 +271,7 @@ else ()
 endif ()
 
 add_library (common STATIC
+    common/trace_callset.cpp
     common/trace_dump.cpp
     common/trace_file.cpp
     common/trace_file_read.cpp
index 0a1bcebab065ecf85aa12ac15aa085ab45506417..38dfa5e21c357ae47a9187ded3d786b895a9dee4 100644 (file)
@@ -64,6 +64,34 @@ Advanced command line usage
 ===========================
 
 
+Call sets
+---------
+
+Several tools take `CALLSET` arguments, e.g:
+
+    apitrace dump --calls CALLSET foo.trace
+    glretrace -S CALLSET foo.trace
+
+The call syntax is very flexible. Here are a few examples:
+
+ * `4`             one call
+
+ * `1,2,4,5`       set of calls
+
+ * `"1 2 4 5"`     set of calls (commas are optional and can be replaced with whitespace)
+
+ * `1-100/2`       calls 1, 3, 5, ...,  99
+
+ * `1-1000/draw`   all draw calls between 1 and 1000
+
+ * `1-1000/fbo`    all fbo changes between calls 1 and 1000
+
+ * `frame`         all calls at end of frames
+
+ * `@foo.txt`      read call numbers from `foo.txt`, using the same syntax as above
+
+
+
 Tracing manually
 ----------------
 
index adecb22e4abd1d0178f0f25d64a13f8d669756fe..3f6bea2e0fd03d0463e74edf1d599b435cbb53ce 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "trace_parser.hpp"
 #include "trace_dump.hpp"
+#include "trace_callset.hpp"
 
 
 enum ColorOption {
@@ -44,6 +45,8 @@ static ColorOption color = COLOR_OPTION_AUTO;
 
 static bool verbose = false;
 
+static trace::CallSet calls(trace::FREQUENCY_ALL);
+
 static const char *synopsis = "Dump given trace(s) to standard output.";
 
 static void
@@ -54,6 +57,7 @@ usage(void)
         << synopsis << "\n"
         "\n"
         "    -v, --verbose       verbose output\n"
+        "    --calls <CALLSET>   Only dump specified calls\n"
         "    --color=<WHEN>\n"
         "    --colour=<WHEN>     Colored syntax highlighting\n"
         "                        WHEN is 'auto', 'always', or 'never'\n"
@@ -70,13 +74,15 @@ command(int argc, char *argv[])
 
     int i;
 
-    for (i = 0; i < argc; ++i) {
+    for (i = 0; i < argc;) {
         const char *arg = argv[i];
 
         if (arg[0] != '-') {
             break;
         }
 
+        ++i;
+
         if (!strcmp(arg, "--")) {
             break;
         } else if (!strcmp(arg, "--help")) {
@@ -85,6 +91,8 @@ command(int argc, char *argv[])
         } else if (strcmp(arg, "-v") == 0 ||
                    strcmp(arg, "--verbose") == 0) {
             verbose = true;
+        } else if (!strcmp(arg, "--calls")) {
+            calls = trace::CallSet(argv[i++]);
         } else if (!strcmp(arg, "--color=auto") ||
                    !strcmp(arg, "--colour=auto")) {
             color = COLOR_OPTION_AUTO;
@@ -132,12 +140,14 @@ command(int argc, char *argv[])
 
         trace::Call *call;
         while ((call = p.parse_call())) {
-            if (verbose ||
-                !(call->flags & trace::CALL_FLAG_VERBOSE)) {
-                if (dumpThreadIds) {
-                    std::cout << std::hex << call->thread_id << std::dec << " ";
+            if (calls.contains(*call)) {
+                if (verbose ||
+                    !(call->flags & trace::CALL_FLAG_VERBOSE)) {
+                    if (dumpThreadIds) {
+                        std::cout << std::hex << call->thread_id << std::dec << " ";
+                    }
+                    trace::dump(*call, std::cout, dumpFlags);
                 }
-                trace::dump(*call, std::cout, dumpFlags);
             }
             delete call;
         }
diff --git a/common/trace_callset.cpp b/common/trace_callset.cpp
new file mode 100644 (file)
index 0000000..3c33087
--- /dev/null
@@ -0,0 +1,247 @@
+/**************************************************************************
+ *
+ * Copyright 2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <limits>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include <trace_callset.hpp>
+
+
+using namespace trace;
+
+
+// Parser class for call sets
+class CallSetParser
+{
+    CallSet &set;
+
+protected:
+    char lookahead;
+
+    CallSetParser(CallSet &_set) :
+        set(_set),
+        lookahead(0)
+    {}
+
+public:
+    void parse() {
+        skipWhiteSpace();
+        while (lookahead) {
+            assert(!isSpace());
+            parseRange();
+            // skip any comma
+            isOperator(',');
+        }
+    }
+
+private:
+    void parseRange() {
+        CallNo start = std::numeric_limits<CallNo>::min();
+        CallNo stop = std::numeric_limits<CallNo>::max();
+        CallNo step = 1;
+        CallFlags freq = FREQUENCY_ALL;
+        if (isAlpha()) {
+            freq = parseFrequency();
+        } else {
+            if (isOperator('*')) {
+                // no-change
+            } else {
+                start = parseCallNo();
+                if (isOperator('-')) {
+                    if (isDigit()) {
+                        stop = parseCallNo();
+                    } else {
+                        // no-change
+                    }
+                } else {
+                    stop = start;
+                }
+            }
+            if (isOperator('/')) {
+                if (isDigit()) {
+                    step = parseCallNo();
+                } else {
+                    freq = parseFrequency();
+                }
+            }
+        }
+        set.addRange(CallRange(start, stop, step, freq));
+    }
+
+    // match and consume an operator
+    bool isOperator(char c) {
+        if (lookahead == c) {
+            consume();
+            skipWhiteSpace();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    CallNo parseCallNo() {
+        CallNo number = 0;
+        if (isDigit()) {
+            do {
+                CallNo digit = consume() - '0';
+                number = number * 10 + digit;
+            } while (isDigit());
+        } else {
+            std::cerr << "error: expected digit, found '" << lookahead << "'\n";
+            exit(0);
+        }
+        skipWhiteSpace();
+        return number;
+    }
+
+    CallFlags parseFrequency() {
+        std::string freq;
+        if (isAlpha()) {
+            do {
+                freq.push_back(consume());
+            } while (isAlpha());
+        } else {
+            std::cerr << "error: expected frequency, found '" << lookahead << "'\n";
+            exit(0);
+        }
+        skipWhiteSpace();
+        if (freq == "frame") {
+            return FREQUENCY_FRAME;
+        } else if (freq == "rendertarget" || freq == "fbo") {
+            return FREQUENCY_RENDERTARGET;
+        } else if (freq == "render" || freq == "draw") {
+            return FREQUENCY_RENDER;
+        } else {
+            std::cerr << "error: expected frequency, found '" << freq << "'\n";
+            exit(0);
+            return FREQUENCY_NONE;
+        }
+    }
+
+    // match lookahead with a digit (does not consume)
+    bool isDigit() const {
+        return lookahead >= '0' && lookahead <= '9';
+    }
+
+    bool isAlpha() const {
+        return lookahead >= 'a' && lookahead <= 'z';
+    }
+
+    void skipWhiteSpace() {
+        while (isSpace()) {
+            consume();
+        }
+    }
+
+    bool isSpace() const {
+        return lookahead == ' ' ||
+               lookahead == '\t' ||
+               lookahead == '\r' ||
+               lookahead == '\n';
+    }
+
+    virtual char consume() = 0;
+};
+
+
+class StringCallSetParser : public CallSetParser
+{
+    const char *buf;
+
+public:
+    StringCallSetParser(CallSet &_set, const char *_buf) :
+        CallSetParser(_set),
+        buf(_buf)
+    {
+        lookahead = *buf;
+    }
+
+    char consume() {
+        char c = lookahead;
+        if (lookahead) {
+            ++buf;
+            lookahead = *buf;
+        }
+        return c;
+    }
+};
+
+
+class FileCallSetParser : public CallSetParser
+{
+    std::ifstream stream;
+
+public:
+    FileCallSetParser(CallSet &_set, const char *filename) :
+        CallSetParser(_set)
+    {
+        stream.open(filename);
+        if (!stream.is_open()) {
+            std::cerr << "error: failed to open \"" << filename << "\"\n";
+            exit(1);
+        }
+
+        stream.get(lookahead);
+    }
+
+    char consume() {
+        char c = lookahead;
+        if (stream.eof()) {
+            lookahead = 0;
+        } else {
+            stream.get(lookahead);
+        }
+        return c;
+    }
+};
+
+
+CallSet::CallSet(const char *string)
+{
+    if (*string == '@') {
+        FileCallSetParser parser(*this, &string[1]);
+        parser.parse();
+    } else {
+        StringCallSetParser parser(*this, string);
+        parser.parse();
+    }
+}
+
+
+CallSet::CallSet(CallFlags freq) {
+    if (freq != FREQUENCY_NONE) {
+        CallNo start = std::numeric_limits<CallNo>::min();
+        CallNo stop = std::numeric_limits<CallNo>::max();
+        CallNo step = 1;
+        addRange(CallRange(start, stop, step, freq));
+        assert(!empty());
+    }
+}
diff --git a/common/trace_callset.hpp b/common/trace_callset.hpp
new file mode 100644 (file)
index 0000000..b679d94
--- /dev/null
@@ -0,0 +1,167 @@
+/**************************************************************************
+ *
+ * Copyright 2012 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Representation of call sets.
+ *
+ * Grammar:
+ *
+ *     set = '@' filename
+ *         | range ( ',' ? range ) *
+ *
+ *     range = interval ( '/' frequency )
+ *
+ *     interval = '*'
+ *              | number
+ *              | start_number '-' end_number
+ *
+ *     frequency = divisor
+ *               | "frame"
+ *               | "rendertarget" | "fbo"
+ *               | "render | "draw"
+ *
+ */
+
+#ifndef _TRACE_CALLSET_HPP_
+#define _TRACE_CALLSET_HPP_
+
+
+#include <list>
+
+#include "trace_model.hpp"
+
+
+namespace trace {
+
+
+    // Should match Call::no
+    typedef unsigned CallNo;
+
+
+    // Aliases for call flags
+    enum {
+        FREQUENCY_NONE         = 0,
+        FREQUENCY_FRAME        = CALL_FLAG_END_FRAME,
+        FREQUENCY_RENDERTARGET = CALL_FLAG_END_FRAME | CALL_FLAG_SWAP_RENDERTARGET,
+        FREQUENCY_RENDER       = CALL_FLAG_RENDER,
+        FREQUENCY_ALL          = 0xffffffff
+    };
+
+    // A linear range of calls
+    class CallRange
+    {
+    public:
+        CallNo start;
+        CallNo stop;
+        CallNo step;
+        CallFlags freq;
+
+        CallRange(CallNo callNo) :
+            start(callNo),
+            stop(callNo),
+            step(1),
+            freq(FREQUENCY_ALL)
+        {}
+
+        CallRange(CallNo _start, CallNo _stop, CallNo _step = 1, CallFlags _freq = FREQUENCY_ALL) :
+            start(_start),
+            stop(_stop),
+            step(_step),
+            freq(_freq)
+        {}
+
+        bool
+        contains(CallNo callNo, CallFlags callFlags) const {
+            return callNo >= start &&
+                   callNo <= stop &&
+                   ((callNo - start) % step) == 0 &&
+                   ((callFlags & freq) ||
+                    freq == FREQUENCY_ALL);
+        }
+    };
+
+
+    // A collection of call ranges
+    class CallSet
+    {
+    public:
+        // TODO: use binary tree to speed up lookups
+        typedef std::list< CallRange > RangeList;
+        RangeList ranges;
+
+        CallSet() {}
+
+        CallSet(CallFlags freq);
+
+        CallSet(const char *str);
+
+        // Not empty set
+        inline bool
+        empty() const {
+            return ranges.empty();
+        }
+
+        void
+        addRange(const CallRange & range) {
+            if (range.start <= range.stop &&
+                range.freq != FREQUENCY_NONE) {
+
+                RangeList::iterator it = ranges.begin();
+                while (it != ranges.end() && it->start < range.start) {
+                    ++it;
+                }
+
+                ranges.insert(it, range);
+            }
+        }
+
+        inline bool
+        contains(CallNo callNo, CallFlags callFlags = FREQUENCY_ALL) const {
+            if (empty()) {
+                return false;
+            }
+            RangeList::const_iterator it;
+            for (it = ranges.begin(); it != ranges.end() && it->start <= callNo; ++it) {
+                if (it->contains(callNo, callFlags)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        inline bool
+        contains(const trace::Call &call) {
+            return contains(call.no, call.flags);
+        }
+    };
+
+
+    CallSet parse(const char *string);
+
+
+} /* namespace trace */
+
+
+#endif /* _TRACE_CALLSET_HPP_ */
index 41e2997c1482c126ae9b9c9f40180bb4f09a5fba..83535223fcf3c7f1009d9731df459ff8a9090203 100644 (file)
@@ -46,17 +46,7 @@ extern unsigned frame;
 extern long long startTime;
 extern bool wait;
 
-enum frequency {
-    FREQUENCY_NEVER = 0,
-    FREQUENCY_FRAME,
-    FREQUENCY_FRAMEBUFFER,
-    FREQUENCY_DRAW,
-};
-
 extern bool benchmark;
-extern const char *compare_prefix;
-extern const char *snapshot_prefix;
-extern enum frequency snapshot_frequency;
 
 extern unsigned dump_state;
 
@@ -69,7 +59,6 @@ extern const retrace::Entry glx_callbacks[];
 extern const retrace::Entry wgl_callbacks[];
 extern const retrace::Entry egl_callbacks[];
 
-void snapshot(unsigned call_no);
 void frame_complete(trace::Call &call);
 
 void updateDrawable(int width, int height);
index f3ae91bda830965e951e6b20c00ab7ae534c6998..7570ebde957822cab1a3f35ae606e7be549cbfa9 100644 (file)
@@ -197,21 +197,12 @@ class GlRetracer(Retracer):
             print '    GLint __pack_buffer = 0;'
             print '    glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &__pack_buffer);'
             print '    if (!__pack_buffer) {'
-            if function.name == 'glReadPixels':
-                print '    glFinish();'
-                print '    if (glretrace::snapshot_frequency == glretrace::FREQUENCY_FRAME ||'
-                print '        glretrace::snapshot_frequency == glretrace::FREQUENCY_FRAMEBUFFER) {'
-                print '        glretrace::snapshot(call.no);'
-                print '    }'
             print '        return;'
             print '    }'
 
         # Pre-snapshots
         if function.name in self.bind_framebuffer_function_names:
             print '    assert(call.flags & trace::CALL_FLAG_SWAP_RENDERTARGET);'
-            print '    if (glretrace::snapshot_frequency == glretrace::FREQUENCY_FRAMEBUFFER) {'
-            print '        glretrace::snapshot(call.no - 1);'
-            print '    }'
         if function.name == 'glFrameTerminatorGREMEDY':
             print '    glretrace::frame_complete(call);'
             return
@@ -225,9 +216,6 @@ class GlRetracer(Retracer):
             print '    }'
         if is_draw_array or is_draw_elements or is_misc_draw:
             print '    assert(call.flags & trace::CALL_FLAG_RENDER);'
-            print '    if (glretrace::snapshot_frequency == glretrace::FREQUENCY_DRAW) {'
-            print '        glretrace::snapshot(call.no);'
-            print '    }'
 
 
     def invokeFunction(self, function):
index 12086321bd3a2bc94ef11a1b024e62ebecbe96c2..f9d77413eef276c8513aa294c19af90238745934 100644 (file)
@@ -30,6 +30,7 @@
 #include "os_time.hpp"
 #include "image.hpp"
 #include "retrace.hpp"
+#include "trace_callset.hpp"
 #include "glproc.hpp"
 #include "glstate.hpp"
 #include "glretrace.hpp"
@@ -50,9 +51,10 @@ long long startTime = 0;
 bool wait = false;
 
 bool benchmark = false;
-const char *compare_prefix = NULL;
-const char *snapshot_prefix = NULL;
-enum frequency snapshot_frequency = FREQUENCY_NEVER;
+static const char *compare_prefix = NULL;
+static const char *snapshot_prefix = NULL;
+static trace::CallSet snapshot_frequency;
+static trace::CallSet compare_frequency;
 
 unsigned dump_state = ~0;
 
@@ -138,9 +140,11 @@ updateDrawable(int width, int height) {
 }
 
 
-void snapshot(unsigned call_no) {
-    if (!drawable ||
-        (!snapshot_prefix && !compare_prefix)) {
+static void
+snapshot(unsigned call_no) {
+    assert(snapshot_prefix || compare_prefix);
+
+    if (!drawable) {
         return;
     }
 
@@ -194,11 +198,6 @@ void frame_complete(trace::Call &call) {
     if (!drawable->visible) {
         retrace::warning(call) << "could not infer drawable size (glViewport never called)\n";
     }
-
-    if (snapshot_frequency == FREQUENCY_FRAME ||
-        snapshot_frequency == FREQUENCY_FRAMEBUFFER) {
-        snapshot(call.no);
-    }
 }
 
 
@@ -215,8 +214,32 @@ static void display(void) {
     trace::Call *call;
 
     while ((call = parser.parse_call())) {
+        bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
+        bool doSnapshot =
+            snapshot_frequency.contains(*call) ||
+            compare_frequency.contains(*call)
+        ;
+
+        // For calls which cause rendertargets to be swaped, we take the
+        // snapshot _before_ swapping the rendertargets.
+        if (doSnapshot && swapRenderTarget) {
+            if (call->flags & trace::CALL_FLAG_END_FRAME) {
+                // For swapbuffers/presents we still use this call number,
+                // spite not have been executed yet.
+                snapshot(call->no);
+            } else {
+                // Whereas for ordinate fbo/rendertarget changes we use the
+                // previous call's number.
+                snapshot(call->no - 1);
+            }
+        }
+
         retracer.retrace(*call);
 
+        if (doSnapshot && !swapRenderTarget) {
+            snapshot(call->no);
+        }
+
         if (!insideGlBeginEnd &&
             drawable && context &&
             call->no >= dump_state) {
@@ -255,11 +278,12 @@ static void usage(void) {
         "\n"
         "  -b           benchmark mode (no error checking or warning messages)\n"
         "  -c PREFIX    compare against snapshots\n"
+        "  -C CALLSET   calls to compare (default is every frame)\n"
         "  -core        use core profile\n"
         "  -db          use a double buffer visual (default)\n"
         "  -sb          use a single buffer visual\n"
         "  -s PREFIX    take snapshots; `-` for PNM stdout output\n"
-        "  -S FREQUENCY snapshot frequency: frame (default), framebuffer, or draw\n"
+        "  -S CALLSET   calls to snapshot (default is every frame)\n"
         "  -v           increase output verbosity\n"
         "  -D CALLNO    dump state at specific call no\n"
         "  -w           wait on final frame\n";
@@ -268,6 +292,8 @@ static void usage(void) {
 extern "C"
 int main(int argc, char **argv)
 {
+    assert(compare_frequency.empty());
+    assert(snapshot_frequency.empty());
 
     int i;
     for (i = 1; i < argc; ++i) {
@@ -285,8 +311,13 @@ int main(int argc, char **argv)
             glws::debug = false;
         } else if (!strcmp(arg, "-c")) {
             compare_prefix = argv[++i];
-            if (snapshot_frequency == FREQUENCY_NEVER) {
-                snapshot_frequency = FREQUENCY_FRAME;
+            if (compare_frequency.empty()) {
+                compare_frequency = trace::CallSet(trace::FREQUENCY_FRAME);
+            }
+        } else if (!strcmp(arg, "-C")) {
+            compare_frequency = trace::CallSet(argv[++i]);
+            if (compare_prefix == NULL) {
+                compare_prefix = "";
             }
         } else if (!strcmp(arg, "-D")) {
             dump_state = atoi(argv[++i]);
@@ -302,25 +333,14 @@ int main(int argc, char **argv)
             return 0;
         } else if (!strcmp(arg, "-s")) {
             snapshot_prefix = argv[++i];
-            if (snapshot_frequency == FREQUENCY_NEVER) {
-                snapshot_frequency = FREQUENCY_FRAME;
+            if (snapshot_frequency.empty()) {
+                snapshot_frequency = trace::CallSet(trace::FREQUENCY_FRAME);
             }
             if (snapshot_prefix[0] == '-' && snapshot_prefix[1] == 0) {
                 retrace::verbosity = -2;
             }
         } else if (!strcmp(arg, "-S")) {
-            arg = argv[++i];
-            if (!strcmp(arg, "frame")) {
-                snapshot_frequency = FREQUENCY_FRAME;
-            } else if (!strcmp(arg, "framebuffer")) {
-                snapshot_frequency = FREQUENCY_FRAMEBUFFER;
-            } else if (!strcmp(arg, "draw")) {
-                snapshot_frequency = FREQUENCY_DRAW;
-            } else {
-                std::cerr << "error: unknown frequency " << arg << "\n";
-                usage();
-                return 1;
-            }
+            snapshot_frequency = trace::CallSet(argv[++i]);
             if (snapshot_prefix == NULL) {
                 snapshot_prefix = "";
             }
index bed216c314921e386ec03bc849c7a1a3d2c59359..34959753fc0acf9b137e07394a6e60e621028e9d 100755 (executable)
@@ -156,9 +156,9 @@ def main():
         type="float", dest="threshold", default=12.0,
         help="threshold precision  [default: %default]")
     optparser.add_option(
-        '-S', '--snapshot-frequency', metavar='FREQUENCY',
+        '-S', '--snapshot-frequency', metavar='CALLSET',
         type="string", dest="snapshot_frequency", default='draw',
-        help="snapshot frequency: frame, framebuffer, or draw  [default: %default]")
+        help="calls to compare [default: %default]")
 
     (options, args) = optparser.parse_args(sys.argv[1:])
     ref_env = parse_env(optparser, options.ref_env)