Skip to content

Instantly share code, notes, and snippets.

@vitillo
Last active September 16, 2015 16:10
Show Gist options
  • Save vitillo/fb3089f78ed9b93b442c to your computer and use it in GitHub Desktop.
Save vitillo/fb3089f78ed9b93b442c to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{"nbformat_minor": 0, "cells": [{"execution_count": 1, "cell_type": "code", "source": "import binascii\nimport pandas as pd\n\nfrom operator import attrgetter, itemgetter\nfrom moztelemetry import get_pings, get_pings_properties, get_one_ping_per_client, get_clients_history\nfrom collections import defaultdict\nfrom __future__ import division\n\n%pylab inline", "outputs": [{"output_type": "stream", "name": "stdout", "text": "Populating the interactive namespace from numpy and matplotlib\n"}], "metadata": {"scrolled": true, "collapsed": false, "trusted": true}}, {"execution_count": 2, "cell_type": "code", "source": "sc.defaultParallelism", "outputs": [{"execution_count": 2, "output_type": "execute_result", "data": {"text/plain": "80"}, "metadata": {}}], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Get all main pings for a set of recent build-ids:", "cell_type": "markdown", "metadata": {}}, {"execution_count": 3, "cell_type": "code", "source": "build_ids = (\"20150820000000\", \"20150827999999\")\n\npings = get_pings(sc,\n app=\"Firefox\",\n channel=\"aurora\",\n build_id=build_ids,\n doc_type=\"main\",\n schema=\"v4\")\n\ncrashes = get_pings(sc,\n app=\"Firefox\",\n channel=\"aurora\",\n build_id=build_ids,\n doc_type=\"crash\",\n schema=\"v4\")", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Take a subset of clients:", "cell_type": "markdown", "metadata": {}}, {"execution_count": 4, "cell_type": "code", "source": "def sample(ping):\n client_id = ping.get(\"clientId\", None)\n return client_id and binascii.crc32(ping[\"clientId\"]) % 100 < 10\n\nsampled_pings = pings.filter(sample)\nsampled_crashes = crashes.filter(sample)", "outputs": [], "metadata": {"collapsed": true, "trusted": true}}, {"execution_count": 5, "cell_type": "code", "source": "crashes_by_client = sampled_crashes.map(lambda c: (c[\"clientId\"], c[\"meta\"])).groupByKey().collectAsMap()", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Get a subset of fields:", "cell_type": "markdown", "metadata": {}}, {"execution_count": 6, "cell_type": "code", "source": "subset = get_pings_properties(sampled_pings, [\"clientId\",\n \"meta/documentId\",\n \"meta/submissionDate\",\n \"meta/creationTimestamp\",\n \"environment/system/os/name\",\n \"payload/info/reason\",\n \"payload/info/sessionId\",\n \"payload/info/subsessionId\",\n \"payload/info/previousSessionId\",\n \"payload/info/previousSubsessionId\",\n \"payload/info/subsessionCounter\",\n \"payload/info/profileSubsessionCounter\",\n \"payload/simpleMeasurements/firstPaint\",\n \"payload/simpleMeasurements/savedPings\",\n \"payload/simpleMeasurements/uptime\",\n \"payload/histograms/STARTUP_CRASH_DETECTED\",\n \"payload/histograms/E10S_AUTOSTART\",\n \"environment/settings/e10sEnabled\"]).\\\n filter(lambda p: p[\"payload/info/profileSubsessionCounter\"] is not None)", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Group fragments by client and dedupe by documentId:", "cell_type": "markdown", "metadata": {}}, {"execution_count": 7, "cell_type": "code", "source": "def dedupe_and_sort(group):\n key, history = group\n \n seen = set()\n result = []\n \n for fragment in history:\n id = fragment[\"meta/documentId\"]\n if id in seen:\n continue\n \n seen.add(id)\n result.append(fragment)\n \n result.sort(key=itemgetter(\"payload/info/profileSubsessionCounter\"))\n return result\n\ngrouped = subset.groupBy(lambda x: x[\"clientId\"]).map(dedupe_and_sort).collect()", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "**< Digression>** What's the percentage of clients that have at least one pair of fragments with different documentIds but the same profileSubsessionCounter?", "cell_type": "markdown", "metadata": {}}, {"execution_count": 8, "cell_type": "code", "source": "def duplicate_pssc(grouped):\n dupes = 0\n dupe_clients = set()\n\n for history in grouped:\n counts = defaultdict(int)\n\n for fragment in history:\n key = fragment[\"payload/info/profileSubsessionCounter\"]\n counts[key] += 1\n\n for _, v in counts.iteritems():\n if v > 1:\n dupes += 1\n dupe_clients.add(history[0][\"clientId\"])\n break\n\n print 100.0*dupes/len(grouped)\n return dupe_clients\n \ndupe_clients = duplicate_pssc(grouped)", "outputs": [{"output_type": "stream", "name": "stdout", "text": "1.72101917412\n"}], "metadata": {"scrolled": true, "collapsed": false, "trusted": true}}, {"source": "**< /Digression\\>** Let's remove those clients to be safe.", "cell_type": "markdown", "metadata": {}}, {"execution_count": 9, "cell_type": "code", "source": "dd_grouped = filter(lambda h: h[0][\"clientId\"] not in dupe_clients, grouped)", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Let's take only clients with e10s enabled:", "cell_type": "markdown", "metadata": {}}, {"execution_count": 10, "cell_type": "code", "source": "def has_e10s(grouped):\n return grouped[0][\"environment/settings/e10sEnabled\"]", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"execution_count": 11, "cell_type": "code", "source": "dd_grouped = filter(has_e10s, grouped)", "outputs": [], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Number of clients:", "cell_type": "markdown", "metadata": {}}, {"execution_count": 12, "cell_type": "code", "source": "len(dd_grouped)", "outputs": [{"execution_count": 12, "output_type": "execute_result", "data": {"text/plain": "14971"}, "metadata": {}}], "metadata": {"collapsed": false, "trusted": true}}, {"source": "Given the set of chain breaks, how many of them are due to missing starting/ending fragments?", "cell_type": "markdown", "metadata": {}}, {"execution_count": 13, "cell_type": "code", "source": "class AdjacentBreaks:\n def __init__(self):\n self.missing_total = 0\n self.missing_start = 0\n self.missing_end = 0\n self.missing_both = 0\n self.crashed_prev = 0\n self.reason = defaultdict(int)\n \n \n def process(self, prev, curr): \n if prev[\"payload/info/sessionId\"] == curr[\"payload/info/previousSessionId\"]:\n # Ignore fake missing fragments? See IncrementError class\n if prev[\"payload/info/reason\"] in (\"aborted-session\", \"shutdown\") and \\\n curr[\"payload/info/subsessionCounter\"] == 1:\n return\n \n self.missing_total += 1\n self.reason[\"{} -> {}\".format(prev[\"payload/info/reason\"], curr[\"payload/info/reason\"])] += 1\n \n # Are there missing starting fragments?\n missing_start = curr[\"payload/info/subsessionCounter\"] != 1\n \n # Are there missing ending fragments?\n missing_end = prev[\"payload/info/reason\"] not in (\"aborted-session\", \"shutdown\")\n \n if missing_start and missing_end:\n self.missing_both += 1\n elif missing_start:\n self.missing_start += 1\n elif missing_end:\n self.missing_end += 1\n \n self.crashed_prev += curr[\"payload/histograms/STARTUP_CRASH_DETECTED\"] or has_crash_ping(prev, curr)\n \n \n def stats(self, total):\n print \"ADJACENT SESSIONS STATS\"\n print \"{:5.2f}% of edges have fragments missing\".format(100*self.missing_total/total)\n print \"{:5.2f}% of edges are missing one or more starting fragments\".format(100*self.missing_start/total)\n print \"{:5.2f}% of edges are missing one or more ending fragments\".format(100*self.missing_end/total)\n print \"{:5.2f}% of edges are missing both starting and ending fragments\".format(100*self.missing_both/total)\n print \"{:5.2f}% of edges have a crash in-between\".format(100*self.crashed_prev/self.missing_total)\n \n print \"\"\n print \"Reason distribution:\"\n print dict(self.reason)\n print \"\"\n \n\nclass WithinBreaks:\n def __init__(self):\n self.missing_total = 0\n self.crashed_prev = 0\n self.reason = defaultdict(int)\n \n \n def process(self, prev, curr):\n if prev[\"payload/info/sessionId\"] == curr[\"payload/info/sessionId\"]:\n self.missing_total += 1\n self.reason[\"{} -> {}\".format(prev[\"payload/info/reason\"], curr[\"payload/info/reason\"])] += 1\n self.crashed_prev += curr[\"payload/histograms/STARTUP_CRASH_DETECTED\"] or has_crash_ping(prev, curr)\n \n \n def stats(self, total):\n print \"WITHIN SESSIONS STATS\"\n print \"{:5.2f}% of edges have fragments missing\".format(100*self.missing_total/total)\n print \"{:5.2f}% of edges have a crash in-between\".format(100*self.crashed_prev/self.missing_total)\n \n print \"\"\n print \"Reason distribution:\"\n print dict(self.reason)\n print \"\"\n\n \nclass NonAdjacentBreaks:\n def __init__(self):\n self.missing_total = 0\n self.reason = defaultdict(int)\n self.difference = defaultdict(int)\n self.crashed_prev = 0\n \n \n def process(self, prev, curr):\n if prev[\"payload/info/sessionId\"] != curr[\"payload/info/sessionId\"] and \\\n prev[\"payload/info/sessionId\"] != curr[\"payload/info/previousSessionId\"]:\n self.missing_total += 1\n self.reason[\"{} -> {}\".format(prev[\"payload/info/reason\"], curr[\"payload/info/reason\"])] += 1\n self.difference[curr[\"payload/info/profileSubsessionCounter\"] - prev[\"payload/info/profileSubsessionCounter\"]] += 1\n self.crashed_prev += curr[\"payload/histograms/STARTUP_CRASH_DETECTED\"] or has_crash_ping(prev, curr)\n \n \n def stats(self, total):\n print \"NON-ADJACENT SESSIONS STATS\"\n print \"{:5.2f}% of edges have fragments missing\".format(100*self.missing_total/total)\n print \"{:5.2f}% of edges have a crash in-between\".format(100*self.crashed_prev/self.missing_total)\n print \"\"\n \n print \"Reason distribution:\"\n print dict(self.reason)\n print \"\"\n \n print \"Difference distribution:\"\n dist = pd.Series(self.difference)\n dist.sort_index()\n print dist\n \n print \"\"\n \n \nclass IncrementError:\n def __init__(self):\n self.errors_total = 0\n self.reason = defaultdict(int)\n \n def process(self, prev, curr):\n if prev[\"payload/info/sessionId\"] == curr[\"payload/info/previousSessionId\"] and \\\n prev[\"payload/info/reason\"] in (\"aborted-session\", \"shutdown\") and \\\n curr[\"payload/info/subsessionCounter\"] == 1:\n self.errors_total += 1\n self.reason[\"{} -> {}\".format(prev[\"payload/info/reason\"], curr[\"payload/info/reason\"])] += 1\n \n def stats(self, total):\n print \"PROFILESUBSESSIONCOUNTER INCREMENT ERRORS\"\n print \"{:5.2f}% of edges have a mismatching profileSubsessionCounter\".format(100*self.errors_total/total)\n print \"\"\n \n print \"Reason distribution:\"\n print dict(self.reason) \n print \"\"\n\n\ndef has_crash_ping(prev, curr):\n client_id = prev[\"clientId\"]\n client_crashes = crashes_by_client.get(client_id, None)\n \n if client_crashes:\n for crash in list(client_crashes):\n if crash[\"creationTimestamp\"] >= prev[\"meta/creationTimestamp\"] and \\\n crash[\"creationTimestamp\"] <= curr[\"meta/creationTimestamp\"]:\n return True\n \n return False\n \n\ndef missing(grouped):\n broken_clients = set()\n correct_clients = set()\n num_broken_chains = 0\n num_crashed = 0\n total_edges = 0\n \n adjacent_breaks = AdjacentBreaks()\n within_breaks = WithinBreaks()\n non_adjacent_breaks = NonAdjacentBreaks()\n increment_errors = IncrementError()\n \n for history in grouped:\n correct_clients.add(history[0][\"clientId\"])\n\n for i in range(1, len(history)):\n prev_fragment = history[i - 1]\n prev_pss_counter = prev_fragment[\"payload/info/profileSubsessionCounter\"]\n \n curr_fragment = history[i]\n current_pss_counter = curr_fragment[\"payload/info/profileSubsessionCounter\"]\n\n num_crashed += curr_fragment[\"payload/histograms/STARTUP_CRASH_DETECTED\"] or has_crash_ping(prev_fragment, curr_fragment)\n total_edges += 1\n\n # Is a fragment missing?\n if prev_pss_counter + 1 != current_pss_counter:\n broken_clients.add(curr_fragment[\"clientId\"])\n num_broken_chains += 1\n \n adjacent_breaks.process(prev_fragment, curr_fragment)\n within_breaks.process(prev_fragment, curr_fragment)\n non_adjacent_breaks.process(prev_fragment, curr_fragment)\n increment_errors.process(prev_fragment, curr_fragment)\n \n correct_clients = correct_clients.difference(broken_clients)\n \n print \"GENERAL STATS\"\n print \"{:5.2f}% clients have a broken session chain\".format(100*len(broken_clients)/len(grouped)) \n print \"{:5.2f}% of clients with a missing fragment experienced at least one crash\".format(100*len(broken_clients.intersection(crashes_by_client.keys()))/len(broken_clients))\n print \"{:5.2f}% of clients without a missing fragment experienced at least one crash\".format(100*len(correct_clients.intersection(crashes_by_client.keys()))/len(correct_clients))\n print \"{:5.2f}% of edges have a crash in-between\\n\".format(100*num_crashed/total_edges)\n \n increment_errors.stats(num_broken_chains)\n adjacent_breaks.stats(num_broken_chains)\n within_breaks.stats(num_broken_chains)\n non_adjacent_breaks.stats(num_broken_chains)\n \nmissing(dd_grouped)", "outputs": [{"output_type": "stream", "name": "stdout", "text": "GENERAL STATS\n 4.90% clients have a broken session chain\n17.05% of clients with a missing fragment experienced at least one crash\n 5.44% of clients without a missing fragment experienced at least one crash\n 1.63% of edges have a crash in-between\n\nPROFILESUBSESSIONCOUNTER INCREMENT ERRORS\n 4.35% of edges have a mismatching profileSubsessionCounter\n\nReason distribution:\n{'aborted-session -> shutdown': 46, 'aborted-session -> environment-change': 11, 'aborted-session -> aborted-session': 14, 'aborted-session -> daily': 16, 'shutdown -> shutdown': 1}\n\nADJACENT SESSIONS STATS\n10.18% of edges have fragments missing\n 5.68% of edges are missing one or more starting fragments\n 4.50% of edges are missing one or more ending fragments\n 0.00% of edges are missing both starting and ending fragments\n 3.88% of edges have a crash in-between\n\nReason distribution:\n{'shutdown -> shutdown': 21, 'aborted-session -> environment-change': 1, 'environment-change -> shutdown': 16, 'environment-change -> environment-change': 10, 'daily -> shutdown': 30, 'aborted-session -> aborted-session': 24, 'environment-change -> aborted-session': 7, 'daily -> daily': 19, 'daily -> aborted-session': 5, 'shutdown -> environment-change': 1, 'environment-change -> daily': 3, 'daily -> environment-change': 1, 'shutdown -> aborted-session': 68}\n\nWITHIN SESSIONS STATS\n 9.29% of edges have fragments missing\n 2.66% of edges have a crash in-between\n\nReason distribution:\n{'aborted-session -> daily': 27, 'aborted-session -> environment-change': 39, 'daily -> shutdown': 1, 'environment-change -> aborted-session': 41, 'daily -> daily': 1, 'daily -> aborted-session': 42, 'aborted-session -> shutdown': 14, 'shutdown -> aborted-session': 23}\n\nNON-ADJACENT SESSIONS STATS\n76.17% of edges have fragments missing\n 9.34% of edges have a crash in-between\n\nReason distribution:\n{'shutdown -> shutdown': 1034, 'environment-change -> environment-change': 58, 'daily -> shutdown': 31, 'shutdown -> daily': 51, 'aborted-session -> daily': 6, 'daily -> daily': 8, 'daily -> aborted-session': 5, 'shutdown -> environment-change': 62, 'aborted-session -> environment-change': 3, 'aborted-session -> aborted-session': 26, 'shutdown -> aborted-session': 86, 'environment-change -> shutdown': 62, 'environment-change -> aborted-session': 9, 'environment-change -> daily': 7, 'aborted-session -> shutdown': 85, 'daily -> environment-change': 8}\n\nDifference distribution:\n0 1075\n2 234\n3 49\n4 23\n5 14\n6 7\n7 4\n8 6\n9 4\n10 7\n11 1\n12 4\n14 3\n15 2\n16 5\n...\n183 1\n192 1\n193 1\n201 1\n204 1\n205 1\n226 1\n241 1\n263 1\n272 1\n278 1\n289 1\n330 1\n406 1\n900 1\nLength: 88, dtype: int64\n\n"}], "metadata": {"scrolled": false, "collapsed": false, "trusted": true}}], "nbformat": 4, "metadata": {"kernelspec": {"display_name": "Python 2", "name": "python2", "language": "python"}, "language_info": {"mimetype": "text/x-python", "nbconvert_exporter": "python", "version": "2.7.9", "name": "python", "file_extension": ".py", "pygments_lexer": "ipython2", "codemirror_mode": {"version": 2, "name": "ipython"}}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment