Socorro Support Classifiers

Socorro has a new feature: classifiers inside the processors. It is  a programmatic way of tagging a crash. The Support Classifiers are based on the TransformRule system added to the Socorro Processors way back in February of 2012.

Think of the term “Support” as a category for a set of tags.  Another term for “category” in this context is “facet”. Support Classifiers are intended for helping with user support.  For example, let's say we're getting crashes from installations of Firefox for which there are known support articles.  This tagging system will make it simpler to associate the crash with the support article.  Eventually, we could implement a system where the user could be automatically directed to a support article based on how the processor categorized the crash.

Classifications are defined by a list of rules. The first rule to match gets to assign the classification. Rules are in two parts: a predicate and an action:
  • predicate : a Python function that implements a test to see if a condition within the raw and/or processed crash is True
  • action : a Python function that will attach a tag to the appropriate place within a processed crash
In the initial implementation, there is only one Support Classifier rule. It is called the BitguardClassifier. It tests to see if the list of loaded modules contains “bitguard.dll”. If that module is present, then the classification “bitguard” is assigned to the Support classification. Since that is the only rule defined so far, “bitguard” is the only possible value. We hope to add more as this feature becomes more well known and we move toward engaging our users about crashes.

Support Classifiers are the second implementation of Classifiers within the processor. The first was the experimental SkunkClassfiers. We can add as many Classifiers as we wish. While both Support and Skunk classifiers define a single facet with a single value, more complex rules could add multiple classifications. Sets of rules can work together in many ways: apply all rules, apply rules until one fails, apply rules until one succeeds, etc.

Support Classifiers are pretty simple since it can only assign one value to the facet “support”. Each rule is tried one at a time and the first one to succeed gets to assign the value. The Skunk Classifiers work the same way. Future Signature Classifiers could include alternate or experimental signature generation algorithms.

Do you have a Classifier that you'd like to see applied to crashes? There are two ways that you can get your idea implemented in the processor.

  1. decide if your classifier is one for Support or some other categorization.
  2. define, in plain English, what you want your predicate and action to be. For example:
    • predicate : if the the user put a comment in the crash submission AND they specified an email address and the crash has the signature “EnterBaseline”
    • action : add a Support Classification: “contact about EnterBaseline”
  3. Enter a bug in Bugzilla with the topic New Classifier with your classification category as well as the predicate and action.  Make sure that you CC :lars so I can vet your work.
  4. pending approval, your classifier will be implemented by someone on the Socorro team and pushed to production with the next release after passing review.
How do I search in the UI for crashes with a certain classification?
At the moment, the UI for Socorro does not support searching for classifiers. See Bug 947723 for the current status of adding classifications to the UI.

Want to try your hand at writing your own Support classifier?

Classifier functions are implemented as methods _predicate and _action in a class derived from the base class SupportClassifierBase.

The predicate is a function that accepts references to the raw and processed crashes as well as a reference to a Socorro Processor object itself. It returns a boolean value. The purpose is to determine if the rule is eligible to be applied. For example, the rule could test to see if the crash is a specific product and version. If the test is true, the predicate returns true and execution passes to the action function. If the test returns False, then the action is skipped and we move on to the next rule.

The action is a function that also accepts a copy of the raw and processed crashes as well as a reference to the Socorro Processor object. The action is generally to just add the classification to the processed crash.

All together, a support classifier should look like this:

from socorro.processor.support_classifiers import SupportClassificationRule
class WriteToThisPersonSupportClassifier(SupportClassificationRule):
    def version(self):
        return '1.0'

    def _predicate(self, raw_crash, processed_crash, processor):
        # implement the predicate as a boolean expression to be 
        # returned by this method 
        return (
            raw_crash.UserComment is not None 
            and raw_crash.EmailAddress is not None 
            and processed_crash.signature == 'EnterBaseline'
        )

    def _action(self, raw_crash, processed_crash, processor):
    self._add_classification(
        # add the support classification to the processed_crash
        processed_crash,
        # choose the value of your classification on the next line
        'contact about EnterBaseline',
        # any extra data about the classification (if any)
        None,
        # the place to log that a classification has been assigned
        processor.config.logger, 
    )

What's inside the raw_crash and processed_crash that my classifier can access?

The raw_crash and the processed_crash are represented in persistent storage the form of a json compatible mapping.  When passed to a classifier, they are in the form of a DotDict, a DOM-like structure accessed with '.' notation.  There are no "out of bounds" fields with in the crash.  The classifier code is running a privileged environment, so all classifiers must be fully vetted before they can be put into production.

raw_crash.UserComment
raw_crash.ProductName
raw_crash.BuildID
processed_crash.json_dump.system_info.OS
processed_crash.json_dump.crash_info.crash_address
processed_crash.json_dump.threads[thread_number][frame_number].function
processed_crash.upload_file_minidump_flash1.json_dump.modules[1].filename

Here's the form of a raw_crash .  This is what Socorro receives from crashing instances of Firefox.

{
    "AdapterDeviceID" : "0x104a",
    "AdapterVendorID" : "0x10de",
    "Add-ons" : "%7B972ce4c6-7e08-4474-a285-3208198ce6fd%7D:25.0.1",
    "BuildID" : "20131112160018",
    "CrashTime" : "1386534180",
    "EMCheckCompatibility" : "true",
    "EmailAddress": "...@..."
    "FlashProcessDump" : "Sandbox",
    "id" : "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
    "InstallTime" : "1384605839",
    "legacy_processing" : 0,
    "Notes" : "AdapterVendorID: ... ",
    "PluginContentURL" : "http://www.j...",
    "PluginFilename" : "NPSWF32_11_7_700_169.dll",
    "PluginName" : "Shockwave Flash",
    "PluginVersion" : "11.7.700.169",
    "ProcessType" : "plugin",
    "ProductID" : "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
    "ProductName" : "Firefox",
    "ReleaseChannel" : "release",
    "StartupTime" : "1386531556",
    "submitted_timestamp" : "2013-12-08T20:23:08.450870+00:00",
    "Theme" : "classic/1.0",
    "throttle_rate" : 10,
    "timestamp" : 1386534188.45089,
    "Vendor" : "Mozilla",
    "Version" : "25.0.1",
    "URL" : "http://...",
    "UserComment" : "Horrors!",
    "Winsock_LSP" : "MSAFD Tcpip ... "
}

Here's the form of the processed_crash :

{
    "additional_minidumps" : [
        "upload_file_minidump_flash2",
        "upload_file_minidump_browser",
        "upload_file_minidump_flash1"
    ],
    "addons" : [
        [
            "testpilot@labs.mozilla.com",
            "1.2.3"
        ],
        ...  more addons ...
    ],
    "addons_checked" : true,
    "address" : "0x77a1015d",
    "app_notes" : "AdapterVendorID: 0x8086, ...",
    "build" : "20131202182626",
    "classifications" : {
        "support" : {
            "classification_version" : "1.0",
            "classification_data" : null,
            "classification" : "contact about EnterBaseline"
        },
        "skunk_works" : {
            "classification_version" : "0.1",
            "classification_data" : null,
            "classification" : "not classified"
        },
        ... additional classifiers ...
    },
    "client_crash_date" : "2013-12-05 23:59:13.000000",
    "completeddatetime" : "2013-12-05 23:59:56.119158",
    "cpu_info" : "GenuineIntel family 6 model 42 stepping 7 | 4",
    "cpu_name" : "x86",
    "crashedThread" : 0,
    "crash_time" : 1386287953,
    "date_processed" : "2013-12-05 23:59:38.160492",
    "distributor" : null,
    "distributor_version" : null,
    "dump" : "OS|Windows NT|6.1.7601 Service Pack 1\n... PIPE DUMP ...,
    "email" : null,
    "exploitability" : "none",
    "flash_version" : "11.9.900.152",
    "hangid" : "fake-e167ea3d-8732-4bae-a403-352e32131205",
    "hang_type" : -1,
    "install_age" : 680,
    "json_dump" : {
    "system_info" : {
        "os_ver" : "6.1.7601 Service Pack 1",
        "cpu_count" : 4,
        "cpu_info" : "GenuineIntel family 6 model 42 stepping 7",
        "cpu_arch" : "x86",
        "os" : "Windows NT"
    },
    "crashing_thread" : {
        "threads_index" : 0,
        "total_frames" : 55,
        "frames" : [
            {
            "function_offset" : "0x15",
            "function" : "NtWaitForMultipleObjects",
            "trust" : "context",
            "frame" : 0,
            "offset" : "0x77a1015d",
            "normalized" : "NtWaitForMultipleObjects",
            "module" : "ntdll.dll",
            "module_offset" : "0x2015d"
            },
            ... more frames ...
        ]
    },
    "thread_count" : 10,
    "status" : "OK",
    "threads" : [
        {
            "frame_count" : 55,
            "frames" : [
                {
                "function_offset" : "0x15",
                "function" : "NtWaitForMultipleObjects",
                "trust" : "context",
                "frame" : 0,
                "module" : "ntdll.dll",
                "offset" : "0x77a1015d",
                "module_offset" : "0x2015d"
                },
                ...  more frames ...
            ]
        },
        ...  more threads ...
    ],
    "modules" : [
        {
            "end_addr" : "0x12e6000",
            "filename" : "plugin-container.exe",
            "version" : "26.0.0.5084",
            "debug_id" : "8385BD80FD534F6E80CF65811735A7472",
            "debug_file" : "plugin-container.pdb",
            "base_addr" : "0x12e0000"
        },
        ... more modules ...
    ],
    "sensitive" : {
        "exploitability" : "none"
    },
    "crash_info" : {
        "crashing_thread" : 0,
        "address" : "0x77a1015d",
        "type" : "EXCEPTION_BREAKPOINT"
    },
    ...