| #! /usr/bin/env python |
| # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| from mtedit import MTEdit |
| from mtlib import Log |
| from mtreplay import MTReplay |
| from mtstat import MTStat |
| from optparse import OptionParser |
| |
| usage = """Multitouch Statistic Collector |
| |
| This tool is used to gather statistics on large amounts of touchpad data from |
| feedback reports. Samples are gathered by parsing the log output of the |
| gestures library. The gesture library provides the MTStatSample/Set commands to |
| create samples. |
| |
| Gestures Library API |
| -------------------- |
| |
| For example you can extend a decision tree in the gestures library code with |
| calls to: |
| |
| MTStatSample("MyDecision", "CaseA"); |
| MTStatSample("MyDecision", "CaseB"); |
| MTStatSample("MyDecision", "CaseC"); |
| |
| Next to string samples you can also sample integers with MTStatSampleInt. |
| For example you can sample the number of fingers in each hardware state: |
| |
| MTStatSampleInt("NumFingers", hwstate.num_fingers); |
| |
| Sometimes you want to only generate one sample per file, for example to |
| count the number of clicks in each file. Call MTStatUpdate[Int] every time |
| your value is changed and the last update will be used by MTStat. |
| For example every time a click is performed you call the following: |
| |
| static int num_of_clicks = 0; |
| MTStatUpdateInt("NumClicks", ++num_of_clicks); |
| |
| Preparing MTStat |
| ---------------- |
| |
| To run on feedback reports you first have to download a bunch of them. MTStat |
| does this for you, for example by downloading the latest 1000 reports: |
| |
| $ %prog -d 1000 |
| |
| Sample Histograms |
| ------------------ |
| |
| Running mtstat will then generate statistic on how often each of the logged |
| cases happened. Optionally you can specify the number of log files to process, |
| per default it will always run all downloaded reports: |
| |
| $ %prog [-n 1000] |
| |
| The output will be presented as a histogram of how often each case appeared. |
| For integer samples with a lot of different values, a bin-based histogram is |
| used. |
| |
| Searching Samples |
| ----------------- |
| |
| You can then also search for cases and view their activity log to figure out |
| which user activity lead to this decision. For example: |
| |
| $ %prog -s "MyDecision == CaseC" [-n 100] |
| |
| When searching integer samples, unequality operators are supported: |
| |
| $ %prog -s "NumFingers >= 3" [-n 100] |
| |
| Of course the same thing works on samples made with MTStatUpdate. For example |
| to find files with a lot of clicks: |
| |
| $ %prog -s "NumClicks > 300" [-n 100] |
| |
| Advanced Search |
| --------------- |
| |
| You can search the gestures library log output with regular expressions. |
| For example to see if any ERROR messages happen: |
| |
| $ %prog -r "ERROR:" |
| |
| Searching for Changed Gestures |
| ------------------------------ |
| |
| MTStat can also be used to search for changes in the generated gestures |
| introduced by changes to the code of the gestures library. |
| |
| In order to do so make local changes to the gestures library without |
| commiting them. Then run: |
| |
| $ %prog -c [-n 100] |
| |
| This command will compare to the HEAD version of gestures and present all |
| changed gestures. |
| |
| You can also limit the search to a certain type of gestures: |
| |
| $ %prog -c -g "ButtonDown" [-n 100] |
| """ |
| |
| def main(): |
| parser = OptionParser(usage=usage) |
| parser.add_option('-d', '--download', |
| dest='download', default=None, |
| help='download more log files') |
| parser.add_option('-o', '--offset', |
| dest='offset', default=0, |
| help='offset for downloading log files') |
| parser.add_option('-n', '--number', |
| dest='number', default=None, |
| help='number of log files to gather stats on') |
| parser.add_option('-p', '--platform', |
| dest='platform', default=None, |
| help='only use log files from specified platform') |
| parser.add_option('-s', '--search', |
| dest='search', default=None, |
| help='search for specific tags') |
| parser.add_option('-l', '--list', |
| dest='list', action='store_true', default=False, |
| help='just print list, don\'t view search results') |
| parser.add_option('-r', '--regex', |
| dest='regex', default=None, |
| help='search log output for regex') |
| parser.add_option('-g', '--gestures', |
| dest='gestures', default=None, |
| help='search log for gestures') |
| parser.add_option('-b', '--bins', |
| dest='bins', default=10, |
| help='number of bins for histograms') |
| parser.add_option('-c', '--changes', |
| dest='changes', action='store_true', default=False, |
| help='search for changes introduced by ' + |
| 'local code changes to the gestures library') |
| |
| (options, args) = parser.parse_args() |
| |
| stat = MTStat() |
| if options.download: |
| stat.Download(options.download, options.offset) |
| return |
| |
| number = None if options.number is None else int(options.number) |
| |
| if (options.search or options.regex or options.gestures or |
| options.changes): |
| if options.changes: |
| results = stat.SearchChanges(options.gestures, |
| number=number, platform=options.platform) |
| else: |
| results = stat.Search(search=options.search, gestures=options.gestures, |
| regex=options.regex, number=number, |
| platform=options.platform) |
| |
| print "Found occurences in %d files" % len(results) |
| print "Serving results, press CTRL-C to abort" |
| for filename, result in results.items(): |
| print "%d occurences in %s:" % (len(result.matches), filename) |
| for match in result.matches: |
| print " ", match |
| |
| # display link to MTEdit session |
| if not options.list: |
| MTEdit().View(result.log, result.matches[0].timestamp) |
| raw_input("Press Enter to continue...") |
| return |
| |
| |
| bins = int(options.bins) |
| results = stat.GatherStats(number, platform=options.platform, num_bins=bins) |
| for key, hist in results.items(): |
| print "%s:" % key |
| overall_count = sum(hist.values()) |
| for value, count in hist.items(): |
| percent = int(round(float(count) / float(overall_count) * 100)) |
| percent_str = "(" + str(percent) + "%)" |
| graph = "=" * int(round(percent / 2)) |
| print " {0:>6} {1:<6} {2:6} |{3}".format( |
| str(value) + ":", count, percent_str, graph) |
| |
| if __name__ == '__main__': |
| main() |