forked from mirrors/gecko-dev
		
	 55a00a13ea
			
		
	
	
		55a00a13ea
		
	
	
	
	
		
			
			The "products" property is optional and defaults to `firefox, fennec`. Recording of scalars is disabled if the probe doesn't have the products property for the current running product. MozReview-Commit-ID: 4HVBrBw1lls --HG-- extra : rebase_source : 5820a8bc8a9397db4ed728354cbb10984bb04946
		
			
				
	
	
		
			203 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # This Source Code Form is subject to the terms of the Mozilla Public
 | |
| # License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | |
| 
 | |
| # This file contains utility functions shared by the scalars and the histogram generation
 | |
| # scripts.
 | |
| 
 | |
| from __future__ import print_function
 | |
| 
 | |
| import re
 | |
| import yaml
 | |
| import sys
 | |
| 
 | |
| # This is a list of flags that determine which process a measurement is allowed
 | |
| # to record from.
 | |
| KNOWN_PROCESS_FLAGS = {
 | |
|     'all': 'All',
 | |
|     'all_children': 'AllChildren',
 | |
|     'main': 'Main',
 | |
|     'content': 'Content',
 | |
|     'gpu': 'Gpu',
 | |
|     # Historical Values
 | |
|     'all_childs': 'AllChildren',  # Supporting files from before bug 1363725
 | |
| }
 | |
| 
 | |
| SUPPORTED_PRODUCTS = {
 | |
|     'firefox': 'Firefox',
 | |
|     'fennec': 'Fennec',
 | |
|     'geckoview': 'Geckoview',
 | |
|     'all': 'All',
 | |
| }
 | |
| 
 | |
| PROCESS_ENUM_PREFIX = "mozilla::Telemetry::Common::RecordedProcessType::"
 | |
| PRODUCT_ENUM_PREFIX = "mozilla::Telemetry::Common::SupportedProduct::"
 | |
| 
 | |
| 
 | |
| class ParserError(Exception):
 | |
|     """Thrown by different probe parsers. Errors are partitioned into
 | |
|     'immediately fatal' and 'eventually fatal' so that the parser can print
 | |
|     multiple error messages at a time. See bug 1401612 ."""
 | |
| 
 | |
|     eventual_errors = []
 | |
| 
 | |
|     def __init__(self, *args):
 | |
|         Exception.__init__(self, *args)
 | |
| 
 | |
|     def handle_later(self):
 | |
|         ParserError.eventual_errors.append(self)
 | |
| 
 | |
|     def handle_now(self):
 | |
|         ParserError.print_eventuals()
 | |
|         print(self.message, file=sys.stderr)
 | |
|         sys.exit(1)
 | |
| 
 | |
|     @classmethod
 | |
|     def print_eventuals(cls):
 | |
|         while cls.eventual_errors:
 | |
|             print(cls.eventual_errors.pop(0).message, file=sys.stderr)
 | |
| 
 | |
|     @classmethod
 | |
|     def exit_func(cls):
 | |
|         if cls.eventual_errors:
 | |
|             cls("Some errors occurred").handle_now()
 | |
| 
 | |
| 
 | |
| def is_valid_process_name(name):
 | |
|     return (name in KNOWN_PROCESS_FLAGS)
 | |
| 
 | |
| 
 | |
| def process_name_to_enum(name):
 | |
|     return PROCESS_ENUM_PREFIX + KNOWN_PROCESS_FLAGS.get(name)
 | |
| 
 | |
| 
 | |
| def is_valid_product(name):
 | |
|     return (name in SUPPORTED_PRODUCTS)
 | |
| 
 | |
| 
 | |
| def product_name_to_enum(product):
 | |
|     return PRODUCT_ENUM_PREFIX + SUPPORTED_PRODUCTS.get(product)
 | |
| 
 | |
| 
 | |
| class StringTable:
 | |
|     """Manages a string table and allows C style serialization to a file."""
 | |
| 
 | |
|     def __init__(self):
 | |
|         self.current_index = 0
 | |
|         self.table = {}
 | |
| 
 | |
|     def c_strlen(self, string):
 | |
|         """The length of a string including the null terminating character.
 | |
|         :param string: the input string.
 | |
|         """
 | |
|         return len(string) + 1
 | |
| 
 | |
|     def stringIndex(self, string):
 | |
|         """Returns the index in the table of the provided string. Adds the string to
 | |
|         the table if it's not there.
 | |
|         :param string: the input string.
 | |
|         """
 | |
|         if string in self.table:
 | |
|             return self.table[string]
 | |
|         else:
 | |
|             result = self.current_index
 | |
|             self.table[string] = result
 | |
|             self.current_index += self.c_strlen(string)
 | |
|             return result
 | |
| 
 | |
|     def stringIndexes(self, strings):
 | |
|         """ Returns a list of indexes for the provided list of strings.
 | |
|         Adds the strings to the table if they are not in it yet.
 | |
|         :param strings: list of strings to put into the table.
 | |
|         """
 | |
|         return [self.stringIndex(s) for s in strings]
 | |
| 
 | |
|     def writeDefinition(self, f, name):
 | |
|         """Writes the string table to a file as a C const char array.
 | |
| 
 | |
|         This writes out the string table as one single C char array for memory
 | |
|         size reasons, separating the individual strings with '\0' characters.
 | |
|         This way we can index directly into the string array and avoid the additional
 | |
|         storage costs for the pointers to them (and potential extra relocations for those).
 | |
| 
 | |
|         :param f: the output stream.
 | |
|         :param name: the name of the output array.
 | |
|         """
 | |
|         entries = self.table.items()
 | |
|         entries.sort(key=lambda x: x[1])
 | |
| 
 | |
|         # Avoid null-in-string warnings with GCC and potentially
 | |
|         # overlong string constants; write everything out the long way.
 | |
|         def explodeToCharArray(string):
 | |
|             def toCChar(s):
 | |
|                 if s == "'":
 | |
|                     return "'\\''"
 | |
|                 else:
 | |
|                     return "'%s'" % s
 | |
|             return ", ".join(map(toCChar, string))
 | |
| 
 | |
|         f.write("const char %s[] = {\n" % name)
 | |
|         for (string, offset) in entries:
 | |
|             if "*/" in string:
 | |
|                 raise ValueError("String in string table contains unexpected sequence '*/': %s" %
 | |
|                                  string)
 | |
| 
 | |
|             e = explodeToCharArray(string)
 | |
|             if e:
 | |
|                 f.write("  /* %5d - \"%s\" */ %s, '\\0',\n"
 | |
|                         % (offset, string, explodeToCharArray(string)))
 | |
|             else:
 | |
|                 f.write("  /* %5d - \"%s\" */ '\\0',\n" % (offset, string))
 | |
|         f.write("};\n\n")
 | |
| 
 | |
| 
 | |
| def static_assert(output, expression, message):
 | |
|     """Writes a C++ compile-time assertion expression to a file.
 | |
|     :param output: the output stream.
 | |
|     :param expression: the expression to check.
 | |
|     :param message: the string literal that will appear if the expression evaluates to
 | |
|         false.
 | |
|     """
 | |
|     print("static_assert(%s, \"%s\");" % (expression, message), file=output)
 | |
| 
 | |
| 
 | |
| def validate_expiration_version(expiration):
 | |
|     """ Makes sure the expiration version has the expected format.
 | |
| 
 | |
|     Allowed examples: "10", "20", "60", "never"
 | |
|     Disallowed examples: "Never", "asd", "4000000", "60a1", "30.5a1"
 | |
| 
 | |
|     :param expiration: the expiration version string.
 | |
|     :return: True if the expiration validates correctly, False otherwise.
 | |
|     """
 | |
|     if expiration != 'never' and not re.match(r'^\d{1,3}$', expiration):
 | |
|         return False
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| def add_expiration_postfix(expiration):
 | |
|     """ Formats the expiration version and adds a version postfix if needed.
 | |
| 
 | |
|     :param expiration: the expiration version string.
 | |
|     :return: the modified expiration string.
 | |
|     """
 | |
|     if re.match(r'^[1-9][0-9]*$', expiration):
 | |
|         return expiration + ".0a1"
 | |
| 
 | |
|     if re.match(r'^[1-9][0-9]*\.0$', expiration):
 | |
|         return expiration + "a1"
 | |
| 
 | |
|     return expiration
 | |
| 
 | |
| 
 | |
| def load_yaml_file(filename):
 | |
|     """ Load a YAML file from disk, throw a ParserError on failure."""
 | |
|     try:
 | |
|         with open(filename, 'r') as f:
 | |
|             return yaml.safe_load(f)
 | |
|     except IOError, e:
 | |
|         raise ParserError('Error opening ' + filename + ': ' + e.message)
 | |
|     except ValueError, e:
 | |
|         raise ParserError('Error parsing processes in {}: {}'
 | |
|                           .format(filename, e.message))
 |