OLD | NEW |
---|---|
1 // Application that captures SKPs from CT's webpage archives. | 1 // run_chromium_analysis is an application that runs the specified benchmark ove r |
2 // CT's webpage archives. It is intended to be run on swarming bots. | |
2 package main | 3 package main |
3 | 4 |
4 import ( | 5 import ( |
6 "bytes" | |
5 "flag" | 7 "flag" |
6 "fmt" | |
7 "io/ioutil" | 8 "io/ioutil" |
8 "os" | 9 "os" |
9 "path" | |
10 "path/filepath" | 10 "path/filepath" |
11 "strconv" | |
12 "sync" | 11 "sync" |
13 "time" | 12 "time" |
14 | 13 |
15 "github.com/skia-dev/glog" | 14 "github.com/skia-dev/glog" |
16 | 15 |
16 "strings" | |
17 | |
17 "go.skia.org/infra/ct/go/util" | 18 "go.skia.org/infra/ct/go/util" |
18 "go.skia.org/infra/ct/go/worker_scripts/worker_common" | 19 "go.skia.org/infra/ct/go/worker_scripts/worker_common" |
19 "go.skia.org/infra/go/common" | 20 "go.skia.org/infra/go/common" |
20 skutil "go.skia.org/infra/go/util" | 21 skutil "go.skia.org/infra/go/util" |
21 ) | 22 ) |
22 | 23 |
23 const ( | 24 const ( |
24 » // The number of goroutines that will run in parallel to capture SKPs. | 25 » // The number of goroutines that will run in parallel to run benchmarks. |
25 WORKER_POOL_SIZE = 10 | 26 WORKER_POOL_SIZE = 10 |
26 ) | 27 ) |
27 | 28 |
28 var ( | 29 var ( |
29 » startRange = flag.Int("start_range", 1, "The number this worker will capture SKPs from.") | 30 » startRange = flag.Int("start_range", 1, "The number this worker will run benchmarks from.") |
30 » num = flag.Int("num", 100, "The total number of SKPs to c apture starting from the start_range.") | 31 » num = flag.Int("num", 100, "The total number of benchmark s to run starting from the start_range.") |
31 » pagesetType = flag.String("pageset_type", util.PAGESET_TYPE_MOBIL E_10k, "The type of pagesets to create SKPs from. Eg: 10k, Mobile10k, All.") | 32 » pagesetType = flag.String("pageset_type", util.PAGESET_TYPE_MOBIL E_10k, "The type of pagesets to create from the Alexa CSV list. Eg: 10k, Mobile1 0k, All.") |
dogben
2016/05/19 21:06:41
s/to create from the Alexa CSV list/to analyze/
rmistry
2016/05/20 11:07:16
Done.
| |
32 » chromiumBuild = flag.String("chromium_build", "", "The chromium bui ld that will be used to create the SKPs.") | 33 » chromiumBuild = flag.String("chromium_build", "", "The chromium bui ld to use.") |
33 runID = flag.String("run_id", "", "The unique run id (typic ally requester + timestamp).") | 34 runID = flag.String("run_id", "", "The unique run id (typic ally requester + timestamp).") |
34 » targetPlatform = flag.String("target_platform", util.PLATFORM_LINUX, "The platform the benchmark will run on (Android / Linux).") | 35 » benchmarkName = flag.String("benchmark_name", "", "The telemetry be nchmark to run on this worker.") |
35 » chromeCleanerTimer = flag.Duration("cleaner_timer", 30*time.Minute, "How often all chrome processes will be killed on this slave.") | 36 » benchmarkExtraArgs = flag.String("benchmark_extra_args", "", "The extra arguments that are passed to the specified benchmark.") |
37 » browserExtraArgs = flag.String("browser_extra_args", "", "The extra ar guments that are passed to the browser while running the benchmark.") | |
38 » chromeCleanerTimer = flag.Duration("cleaner_timer", 15*time.Minute, "How often all chrome processes will be killed on this slave.") | |
36 ) | 39 ) |
37 | 40 |
38 func main() { | 41 func main() { |
39 defer common.LogPanic() | 42 defer common.LogPanic() |
40 worker_common.Init() | 43 worker_common.Init() |
41 » defer util.TimeTrack(time.Now(), "Capturing SKPs") | 44 » defer util.TimeTrack(time.Now(), "Running Chromium Perf") |
dogben
2016/05/19 21:06:41
s/Perf/Analysis/
rmistry
2016/05/20 11:07:16
Done.
| |
42 defer glog.Flush() | 45 defer glog.Flush() |
43 | 46 |
44 // Validate required arguments. | 47 // Validate required arguments. |
45 if *chromiumBuild == "" { | 48 if *chromiumBuild == "" { |
46 glog.Error("Must specify --chromium_build") | 49 glog.Error("Must specify --chromium_build") |
47 return | 50 return |
48 } | 51 } |
49 if *runID == "" { | 52 if *runID == "" { |
50 glog.Error("Must specify --run_id") | 53 glog.Error("Must specify --run_id") |
51 return | 54 return |
52 } | 55 } |
53 » if *targetPlatform == util.PLATFORM_ANDROID { | 56 » if *benchmarkName == "" { |
54 » » glog.Error("Android is not yet supported for capturing SKPs.") | 57 » » glog.Error("Must specify --benchmark_name") |
55 return | 58 return |
56 } | 59 } |
57 | 60 |
58 // Reset the local chromium checkout. | 61 // Reset the local chromium checkout. |
59 if err := util.ResetCheckout(util.ChromiumSrcDir); err != nil { | 62 if err := util.ResetCheckout(util.ChromiumSrcDir); err != nil { |
60 glog.Errorf("Could not reset %s: %s", util.ChromiumSrcDir, err) | 63 glog.Errorf("Could not reset %s: %s", util.ChromiumSrcDir, err) |
61 return | 64 return |
62 } | 65 } |
63 // Sync the local chromium checkout. | 66 // Sync the local chromium checkout. |
64 if err := util.SyncDir(util.ChromiumSrcDir); err != nil { | 67 if err := util.SyncDir(util.ChromiumSrcDir); err != nil { |
65 glog.Errorf("Could not gclient sync %s: %s", util.ChromiumSrcDir , err) | 68 glog.Errorf("Could not gclient sync %s: %s", util.ChromiumSrcDir , err) |
66 return | 69 return |
67 } | 70 } |
68 | 71 |
69 // Instantiate GsUtil object. | 72 // Instantiate GsUtil object. |
70 gs, err := util.NewGsUtil(nil) | 73 gs, err := util.NewGsUtil(nil) |
71 if err != nil { | 74 if err != nil { |
72 glog.Error(err) | 75 glog.Error(err) |
73 return | 76 return |
74 } | 77 } |
75 | 78 |
79 // Download the benchmark patch for this run from Google storage. | |
80 benchmarkPatchName := *runID + ".benchmark.patch" | |
81 benchmarkPatchLocalPath := filepath.Join(os.TempDir(), benchmarkPatchNam e) | |
dogben
2016/05/19 21:06:41
nit: ioutil.TempDir or ioutil.TempFile
rmistry
2016/05/20 11:07:16
Done.
| |
82 remotePatchesDir := filepath.Join(util.ChromiumPerfRunsDir, *runID) | |
83 benchmarkPatchRemotePath := filepath.Join(remotePatchesDir, benchmarkPat chName) | |
84 respBody, err := gs.GetRemoteFileContents(benchmarkPatchRemotePath) | |
85 if err != nil { | |
86 glog.Errorf("Could not fetch %s: %s", benchmarkPatchRemotePath, err) | |
87 return | |
88 } | |
89 defer skutil.Close(respBody) | |
90 buf := new(bytes.Buffer) | |
91 if _, err := buf.ReadFrom(respBody); err != nil { | |
92 glog.Errorf("Could not read from %s: %s", benchmarkPatchRemotePa th, err) | |
93 return | |
94 } | |
95 if err := ioutil.WriteFile(benchmarkPatchLocalPath, buf.Bytes(), 0666); err != nil { | |
dogben
2016/05/19 21:06:41
nit: os.Create + io.Copy is probably slightly bett
rmistry
2016/05/20 11:07:16
This is also how the poller creates local patches,
| |
96 glog.Errorf("Unable to create file %s: %s", benchmarkPatchLocalP ath, err) | |
97 return | |
98 } | |
99 defer skutil.Remove(benchmarkPatchLocalPath) | |
100 // Apply benchmark patch to the local chromium checkout. | |
101 if buf.Len() > 10 { | |
dogben
2016/05/19 21:06:41
(io.Copy returns the number of bytes copied)
rmistry
2016/05/20 11:07:16
Acknowledged.
| |
102 if err := util.ApplyPatch(benchmarkPatchLocalPath, util.Chromium SrcDir); err != nil { | |
103 glog.Errorf("Could not apply Telemetry's patch in %s: %s ", util.ChromiumSrcDir, err) | |
104 return | |
105 } | |
106 } | |
107 | |
76 // Download the specified chromium build. | 108 // Download the specified chromium build. |
77 if err := gs.DownloadChromiumBuild(*chromiumBuild); err != nil { | 109 if err := gs.DownloadChromiumBuild(*chromiumBuild); err != nil { |
78 glog.Error(err) | 110 glog.Error(err) |
79 return | 111 return |
80 } | 112 } |
81 » // Delete the chromium build to save space when we are done. | 113 » //Delete the chromium build to save space when we are done. |
82 defer skutil.RemoveAll(filepath.Join(util.ChromiumBuildsDir, *chromiumBu ild)) | 114 defer skutil.RemoveAll(filepath.Join(util.ChromiumBuildsDir, *chromiumBu ild)) |
115 | |
83 chromiumBinary := filepath.Join(util.ChromiumBuildsDir, *chromiumBuild, util.BINARY_CHROME) | 116 chromiumBinary := filepath.Join(util.ChromiumBuildsDir, *chromiumBuild, util.BINARY_CHROME) |
84 if *targetPlatform == util.PLATFORM_ANDROID { | |
85 // Install the APK on the Android device. | |
86 if err := util.InstallChromeAPK(*chromiumBuild); err != nil { | |
87 glog.Errorf("Could not install the chromium APK: %s", er r) | |
88 return | |
89 } | |
90 } | |
91 | 117 |
92 // Download pagesets if they do not exist locally. | 118 // Download pagesets if they do not exist locally. |
93 pathToPagesets := filepath.Join(util.PagesetsDir, *pagesetType) | 119 pathToPagesets := filepath.Join(util.PagesetsDir, *pagesetType) |
94 if _, err := gs.DownloadSwarmingArtifacts(pathToPagesets, util.PAGESETS_ DIR_NAME, *pagesetType, *startRange, *num); err != nil { | 120 if _, err := gs.DownloadSwarmingArtifacts(pathToPagesets, util.PAGESETS_ DIR_NAME, *pagesetType, *startRange, *num); err != nil { |
95 glog.Error(err) | 121 glog.Error(err) |
96 return | 122 return |
97 } | 123 } |
98 defer skutil.RemoveAll(pathToPagesets) | 124 defer skutil.RemoveAll(pathToPagesets) |
99 | 125 |
100 // Download archives if they do not exist locally. | 126 // Download archives if they do not exist locally. |
101 pathToArchives := filepath.Join(util.WebArchivesDir, *pagesetType) | 127 pathToArchives := filepath.Join(util.WebArchivesDir, *pagesetType) |
102 » archivesToIndex, err := gs.DownloadSwarmingArtifacts(pathToArchives, uti l.WEB_ARCHIVES_DIR_NAME, *pagesetType, *startRange, *num) | 128 » if _, err := gs.DownloadSwarmingArtifacts(pathToArchives, util.WEB_ARCHI VES_DIR_NAME, *pagesetType, *startRange, *num); err != nil { |
103 » if err != nil { | |
104 glog.Error(err) | 129 glog.Error(err) |
105 return | 130 return |
106 } | 131 } |
107 defer skutil.RemoveAll(pathToArchives) | 132 defer skutil.RemoveAll(pathToArchives) |
108 | 133 |
109 » // Create the dir that SKPs will be stored in. | 134 » // Establish nopatch output paths. |
110 » pathToSkps := filepath.Join(util.SkpsDir, *pagesetType, *chromiumBuild) | 135 » localOutputDir := filepath.Join(util.StorageDir, util.BenchmarkRunsDir, *runID) |
111 » // Delete and remake the local SKPs directory. | 136 » skutil.RemoveAll(localOutputDir) |
112 » skutil.RemoveAll(pathToSkps) | 137 » skutil.MkdirAll(localOutputDir, 0700) |
113 » skutil.MkdirAll(pathToSkps, 0700) | 138 » defer skutil.RemoveAll(localOutputDir) |
114 » defer skutil.RemoveAll(pathToSkps) | 139 » remoteDir := filepath.Join(util.BenchmarkRunsDir, *runID) |
115 | 140 |
116 » // Construct path to the ct_run_benchmark python script. | 141 » // Construct path to CT's python scripts. |
117 pathToPyFiles := util.GetPathToPyFiles(!*worker_common.Local) | 142 pathToPyFiles := util.GetPathToPyFiles(!*worker_common.Local) |
118 | 143 |
119 timeoutSecs := util.PagesetTypeToInfo[*pagesetType].CaptureSKPsTimeoutSe cs | |
120 fileInfos, err := ioutil.ReadDir(pathToPagesets) | 144 fileInfos, err := ioutil.ReadDir(pathToPagesets) |
121 if err != nil { | 145 if err != nil { |
122 glog.Errorf("Unable to read the pagesets dir %s: %s", pathToPage sets, err) | 146 glog.Errorf("Unable to read the pagesets dir %s: %s", pathToPage sets, err) |
123 return | 147 return |
124 } | 148 } |
125 | 149 |
150 glog.Infoln("===== Going to run the task with parallel chrome processes =====") | |
151 | |
126 // Create channel that contains all pageset file names. This channel wil l | 152 // Create channel that contains all pageset file names. This channel wil l |
127 // be consumed by the worker pool. | 153 // be consumed by the worker pool. |
128 pagesetRequests := util.GetClosedChannelOfPagesets(fileInfos) | 154 pagesetRequests := util.GetClosedChannelOfPagesets(fileInfos) |
129 | 155 |
130 var wg sync.WaitGroup | 156 var wg sync.WaitGroup |
131 // Use a RWMutex for the chromeProcessesCleaner goroutine to communicate to | 157 // Use a RWMutex for the chromeProcessesCleaner goroutine to communicate to |
132 // the workers (acting as "readers") when it wants to be the "writer" an d | 158 // the workers (acting as "readers") when it wants to be the "writer" an d |
133 // kill all zombie chrome processes. | 159 // kill all zombie chrome processes. |
134 var mutex sync.RWMutex | 160 var mutex sync.RWMutex |
135 | 161 |
136 // Loop through workers in the worker pool. | 162 // Loop through workers in the worker pool. |
137 for i := 0; i < WORKER_POOL_SIZE; i++ { | 163 for i := 0; i < WORKER_POOL_SIZE; i++ { |
138 // Increment the WaitGroup counter. | 164 // Increment the WaitGroup counter. |
139 wg.Add(1) | 165 wg.Add(1) |
140 | 166 |
141 // Create and run a goroutine closure that captures SKPs. | 167 // Create and run a goroutine closure that captures SKPs. |
dogben
2016/05/19 21:06:41
nit: update comment
rmistry
2016/05/20 11:07:16
Done.
| |
142 go func() { | 168 go func() { |
143 // Decrement the WaitGroup counter when the goroutine co mpletes. | 169 // Decrement the WaitGroup counter when the goroutine co mpletes. |
144 defer wg.Done() | 170 defer wg.Done() |
145 | 171 |
146 for pagesetName := range pagesetRequests { | 172 for pagesetName := range pagesetRequests { |
147 | 173 |
148 mutex.RLock() | 174 mutex.RLock() |
149 | 175 » » » » if err := util.RunBenchmark(pagesetName, pathToP agesets, pathToPyFiles, localOutputDir, *chromiumBuild, chromiumBinary, *runID, *browserExtraArgs, *benchmarkName, "Linux", *benchmarkExtraArgs, *pagesetType, - 1); err != nil { |
150 » » » » // Read the pageset. | 176 » » » » » glog.Errorf("Error while running withpat ch benchmark: %s", err) |
151 » » » » pagesetPath := filepath.Join(pathToPagesets, pag esetName) | 177 » » » » » return |
152 » » » » decodedPageset, err := util.ReadPageset(pagesetP ath) | |
153 » » » » if err != nil { | |
154 » » » » » glog.Errorf("Could not read %s: %s", pag esetPath, err) | |
155 » » » » » continue | |
156 } | 178 } |
157 | |
158 glog.Infof("===== Processing %s =====", pagesetP ath) | |
159 | |
160 skutil.LogErr(os.Chdir(pathToPyFiles)) | |
161 index, ok := archivesToIndex[decodedPageset.Arch iveDataFile] | |
162 if !ok { | |
163 glog.Errorf("%s not found in the archive sToIndex map", decodedPageset.ArchiveDataFile) | |
164 continue | |
165 } | |
166 args := []string{ | |
167 filepath.Join(util.TelemetryBinariesDir, util.BINARY_RUN_BENCHMARK), | |
168 util.BenchmarksToTelemetryName[util.BENC HMARK_SKPICTURE_PRINTER], | |
169 "--also-run-disabled-tests", | |
170 "--page-repeat=1", // Only need one run for SKPs. | |
171 "--skp-outdir=" + path.Join(pathToSkps, strconv.Itoa(index)), | |
172 "--extra-browser-args=" + util.DEFAULT_B ROWSER_ARGS, | |
173 "--user-agent=" + decodedPageset.UserAge nt, | |
174 "--urls-list=" + decodedPageset.UrlsList , | |
175 "--archive-data-file=" + decodedPageset. ArchiveDataFile, | |
176 } | |
177 // Figure out which browser and device should be used. | |
178 if *targetPlatform == util.PLATFORM_ANDROID { | |
179 args = append(args, "--browser=android-c hromium") | |
180 } else { | |
181 args = append(args, "--browser=exact", " --browser-executable="+chromiumBinary) | |
182 args = append(args, "--device=desktop") | |
183 } | |
184 // Set the PYTHONPATH to the pagesets and the te lemetry dirs. | |
185 env := []string{ | |
186 fmt.Sprintf("PYTHONPATH=%s:%s:%s:%s:$PYT HONPATH", pathToPagesets, util.TelemetryBinariesDir, util.TelemetrySrcDir, util. CatapultSrcDir), | |
187 "DISPLAY=:0", | |
188 } | |
189 skutil.LogErr( | |
190 util.ExecuteCmd("python", args, env, tim e.Duration(timeoutSecs)*time.Second, nil, nil)) | |
191 | |
192 mutex.RUnlock() | 179 mutex.RUnlock() |
193 } | 180 } |
194 }() | 181 }() |
195 } | 182 } |
196 | 183 |
197 if !*worker_common.Local { | 184 if !*worker_common.Local { |
198 // Start the cleaner. | 185 // Start the cleaner. |
199 go util.ChromeProcessesCleaner(&mutex, *chromeCleanerTimer) | 186 go util.ChromeProcessesCleaner(&mutex, *chromeCleanerTimer) |
200 } | 187 } |
201 | 188 |
202 // Wait for all spawned goroutines to complete. | 189 // Wait for all spawned goroutines to complete. |
203 wg.Wait() | 190 wg.Wait() |
204 | 191 |
205 » // Move and validate all SKP files. | 192 » // If "--output-format=csv-pivot-table" was specified then merge all CSV files and upload. |
206 » if err := util.ValidateSKPs(pathToSkps, pathToPyFiles); err != nil { | 193 » if strings.Contains(*benchmarkExtraArgs, "--output-format=csv-pivot-tabl e") { |
207 » » glog.Error(err) | 194 » » if err := util.MergeUploadCSVFilesOnWorkers(localOutputDir, path ToPyFiles, *runID, remoteDir, gs, *startRange); err != nil { |
208 » » return | 195 » » » glog.Errorf("Error while processing withpatch CSV files: %s", err) |
209 » } | 196 » » » return |
210 | 197 » » } |
211 » // Upload SKPs dir to Google Storage. | |
212 » if err := gs.UploadSwarmingArtifacts(util.SKPS_DIR_NAME, *pagesetType); err != nil { | |
213 » » glog.Error(err) | |
214 » » return | |
215 } | 198 } |
216 } | 199 } |
OLD | NEW |