Chromium Code Reviews| 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 |