blob: 0e66ec5e59b06ea92d5c658b425935f9bec2532f [file] [log] [blame]
Takuto Ikuta9af233a2018-11-29 03:53:53 +00001#!/usr/bin/env python
2# Copyright 2018 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""
7This is script to upload ninja_log from googler.
8
9Server side implementation is in
10https://cs.chromium.org/chromium/infra/go/src/infra/appengine/chromium_build_stats/
11
12Uploaded ninjalog is stored in BigQuery table having following schema.
13https://cs.chromium.org/chromium/infra/go/src/infra/appengine/chromium_build_stats/ninjaproto/ninjalog.proto
14
15The log will be used to analyze user side build performance.
16"""
17
18import argparse
19import cStringIO
20import gzip
21import json
22import logging
23import multiprocessing
24import os
25import platform
26import socket
Takuto Ikuta96fdf7c2018-12-03 09:18:39 +000027import subprocess
Takuto Ikuta9af233a2018-11-29 03:53:53 +000028import sys
29
30from third_party import httplib2
31
32def IsGoogler(server):
33 """Check whether this script run inside corp network."""
34 try:
35 h = httplib2.Http()
36 _, content = h.request('https://'+server+'/should-upload', 'GET')
37 return content == 'Success'
38 except httplib2.HttpLib2Error:
39 return False
40
Takuto Ikuta96fdf7c2018-12-03 09:18:39 +000041def ParseGNArgs(gn_args):
Takuto Ikuta61cb9d62018-12-17 23:45:49 +000042 """Parse gn_args as json and return config dictionary."""
Takuto Ikuta96fdf7c2018-12-03 09:18:39 +000043 configs = json.loads(gn_args)
44 build_configs = {}
45 for config in configs:
46 build_configs[config["name"]] = config["current"]["value"]
47 return build_configs
48
49
Takuto Ikuta9af233a2018-11-29 03:53:53 +000050def GetMetadata(cmdline, ninjalog):
51 """Get metadata for uploaded ninjalog."""
52
Takuto Ikuta9af233a2018-11-29 03:53:53 +000053 build_dir = os.path.dirname(ninjalog)
Takuto Ikuta96fdf7c2018-12-03 09:18:39 +000054
55 build_configs = {}
56
57 try:
58 args = ['gn', 'args', build_dir, '--list', '--overrides-only',
59 '--short', '--json']
60 if sys.platform == 'win32':
61 # gn in PATH is bat file in windows environment (except cygwin).
62 args = ['cmd', '/c'] + args
63
64 gn_args = subprocess.check_output(args)
65 build_configs = ParseGNArgs(gn_args)
66 except subprocess.CalledProcessError as e:
67 logging.error("Failed to call gn %s", e)
68 build_configs = {}
69
70 # Stringify config.
71 for k in build_configs:
72 build_configs[k] = str(build_configs[k])
73
Takuto Ikuta9af233a2018-11-29 03:53:53 +000074 metadata = {
75 'platform': platform.system(),
76 'cwd': build_dir,
77 'hostname': socket.gethostname(),
78 'cpu_core': multiprocessing.cpu_count(),
79 'cmdline': cmdline,
Takuto Ikuta96fdf7c2018-12-03 09:18:39 +000080 'build_configs': build_configs,
Takuto Ikuta9af233a2018-11-29 03:53:53 +000081 }
82
83 return metadata
84
85def GetNinjalog(cmdline):
Takuto Ikuta61cb9d62018-12-17 23:45:49 +000086 """GetNinjalog returns the path to ninjalog from cmdline."""
Takuto Ikuta9af233a2018-11-29 03:53:53 +000087 # ninjalog is in current working directory by default.
88 ninjalog_dir = '.'
89
90 i = 0
91 while i < len(cmdline):
92 cmd = cmdline[i]
93 i += 1
94 if cmd == '-C' and i < len(cmdline):
95 ninjalog_dir = cmdline[i]
96 i += 1
97 continue
98
99 if cmd.startswith('-C') and len(cmd) > len('-C'):
100 ninjalog_dir = cmd[len('-C'):]
101
102 return os.path.join(ninjalog_dir, '.ninja_log')
103
104def main():
105 parser = argparse.ArgumentParser()
106 parser.add_argument('--server',
107 default='chromium-build-stats.appspot.com',
108 help='server to upload ninjalog file.')
109 parser.add_argument('--ninjalog', help='ninjalog file to upload.')
110 parser.add_argument('--verbose', action='store_true',
111 help='Enable verbose logging.')
112 parser.add_argument('--cmdline', required=True, nargs=argparse.REMAINDER,
113 help='command line args passed to ninja.')
114
115 args = parser.parse_args()
116
117 if args.verbose:
118 logging.basicConfig(level=logging.INFO)
119 else:
120 # Disable logging.
121 logging.disable(logging.CRITICAL)
122
123 if not IsGoogler(args.server):
124 return 0
125
126
127 ninjalog = args.ninjalog or GetNinjalog(args.cmdline)
128 if not os.path.isfile(ninjalog):
129 logging.warn("ninjalog is not found in %s", ninjalog)
130 return 1
131
132 output = cStringIO.StringIO()
133
134 with open(ninjalog) as f:
135 with gzip.GzipFile(fileobj=output, mode='wb') as g:
136 g.write(f.read())
137 g.write('# end of ninja log\n')
138
139 metadata = GetMetadata(args.cmdline, ninjalog)
140 logging.info('send metadata: %s', metadata)
141 g.write(json.dumps(metadata))
142
143 h = httplib2.Http()
144 resp_headers, content = h.request(
145 'https://'+args.server+'/upload_ninja_log/', 'POST',
146 body=output.getvalue(), headers={'Content-Encoding': 'gzip'})
147
148 if resp_headers.status != 200:
149 logging.warn("unexpected status code for response: %s",
150 resp_headers.status)
151 return 1
152
153 logging.info('response header: %s', resp_headers)
154 logging.info('response content: %s', content)
155 return 0
156
157if __name__ == '__main__':
158 sys.exit(main())