Completed
Pull Request — master (#268)
by
unknown
01:20
created

Xhgui_Controller_Run::index()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 41
rs 9.264
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
use Slim\Slim;
4
5
class Xhgui_Controller_Run extends Xhgui_Controller
6
{
7
    /**
8
     * @var Xhgui_Profiles
9
     */
10
    private $profiles;
11
12
    /**
13
     * @var \Xhgui_WatchedFunctionsStorageInterface
14
     */
15
    private $watches;
16
17
    /**
18
     * Xhgui_Controller_Run constructor.
19
     * @param Slim $app
20
     * @param Xhgui_Profiles $profiles
21
     * @param Xhgui_WatchedFunctionsStorageInterface $watches
22
     */
23
    public function __construct(Slim $app, Xhgui_Profiles $profiles, \Xhgui_WatchedFunctionsStorageInterface $watches)
24
    {
25
        parent::__construct($app);
26
        $this->setProfiles($profiles);
27
        $this->setWatches($watches);
28
    }
29
30
    /**
31
     *
32
     */
33
    public function index()
34
    {
35
        $response = $this->app->response();
36
        // The list changes whenever new profiles are recorded.
37
        // Generally avoid caching, but allow re-use in browser's bfcache
38
        // and by cache proxies for concurrent requests.
39
        // https://github.com/perftools/xhgui/issues/261
40
        $response->headers->set('Cache-Control', 'public, max-age=0');
41
42
        $request = $this->app->request();
43
44
        $filter = Xhgui_Storage_Filter::fromRequest($request);
45
46
        $result = $this->getProfiles()->getAll($filter);
47
        $title = 'Recent runs';
48
        $titleMap = array(
49
            'wt'    => 'Longest wall time',
50
            'cpu'   => 'Most CPU time',
51
            'mu'    => 'Highest memory use',
52
        );
53
        if (isset($titleMap[$filter->getSort()])) {
54
            $title = $titleMap[$filter->getSort()];
55
        }
56
        $paging = array(
57
            'total_pages'   => $result['totalPages'],
58
            'page'          => $result['page'],
59
            'sort'          => $filter->getSort(),
60
            'direction'     => $result['direction']
61
        );
62
63
        $this->_template = 'runs/list.twig';
64
65
        $this->set(array(
66
            'paging'        => $paging,
67
            'base_url'      => 'home',
68
            'runs'          => $result['results'],
69
            'date_format'   => $this->app->config('date.format'),
70
            'search'        => $filter->toArray(),
71
            'title'         => $title
72
        ));
73
    }
74
75
    /**
76
     *
77
     */
78
    public function view()
79
    {
80
        $response = $this->app->response();
81
        // Permalink views to a specific run are meant to be public and immutable.
82
        // But limit the cache to only a short period of time (enough to allow
83
        // handling of abuse or other stampedes). This way we don't have to
84
        // deal with any kind of purging system for when profiles are deleted,
85
        // or for after XHGui itself is upgraded and static assets may be
86
        // incompatible etc.
87
        // https://github.com/perftools/xhgui/issues/261
88
        $response->headers->set('Cache-Control', 'public, max-age=60, must-revalidate');
89
90
        $request = $this->app->request();
91
        $detailCount = $this->app->config('detail.count');
92
        $result = $this->getProfiles()->get($request->get('id'));
93
94
        $result->calculateSelf();
95
96
        // Self wall time graph
97
        $timeChart = $result->extractDimension('ewt', $detailCount);
98
99
        // Memory Block
100
        $memoryChart = $result->extractDimension('emu', $detailCount);
101
102
        // Watched Functions Block
103
        $watchedFunctions = array();
104
        foreach ($this->getWatches()->getWatchedFunctions() as $watch) {
105
            $matches = $result->getWatched($watch['name']);
106
107
            if ($matches) {
108
                $watchedFunctions = array_merge($watchedFunctions, $matches);
109
            }
110
        }
111
112
        $profile = $result->sort('ewt', $result->getProfile());
113
114
        $this->_template = 'runs/view.twig';
115
        $this->set(array(
116
            'profile'       => $profile,
117
            'result'        => $result,
118
            'wall_time'     => $timeChart,
119
            'memory'        => $memoryChart,
120
            'watches'       => $watchedFunctions,
121
            'date_format'   => $this->app->config('date.format'),
122
        ));
123
    }
124
125
    /**
126
     * @throws Exception
127
     */
128
    public function deleteForm()
129
    {
130
        $request = $this->app->request();
131
        $id = $request->get('id');
132
        if (empty($id)) {
133
            throw new Exception('The "id" parameter is required.');
134
        }
135
136
        // Get details
137
        $result = $this->getProfiles()->get($id);
138
139
        $this->_template = 'runs/delete-form.twig';
140
        $this->set(array(
141
            'run_id' => $id,
142
            'result' => $result,
143
        ));
144
    }
145
146
    /**
147
     * @throws Exception
148
     */
149
    public function deleteSubmit()
150
    {
151
        $request = $this->app->request();
152
        $id = $request->post('id');
153
154
        // Don't call profilers->delete() unless $id is set,
155
        // otherwise it will turn the null into a MongoId and return "Sucessful".
156
        if (empty($id)) {
157
            // Form checks this already,
158
            // only reachable by handcrafted or malformed requests.
159
            throw new Exception('The "id" parameter is required.');
160
        }
161
162
        // Delete the profile run.
163
        $this->getProfiles()->delete($id);
164
165
        $this->app->flash('success', 'Deleted profile ' . $id);
166
167
        $this->app->redirect($this->app->urlFor('home'));
168
    }
169
170
    /**
171
     *
172
     */
173
    public function deleteAllForm()
174
    {
175
        $this->_template = 'runs/delete-all-form.twig';
176
    }
177
178
    /**
179
     *
180
     */
181
    public function deleteAllSubmit()
182
    {
183
        // Delete all profile runs.
184
        $this->getProfiles()->truncate();
185
186
        $this->app->flash('success', 'Deleted all profiles');
187
188
        $this->app->redirect($this->app->urlFor('home'));
189
    }
190
191
    /**
192
     *
193
     */
194
    public function url()
195
    {
196
        $request = $this->app->request();
197
198
        $filter = Xhgui_Storage_Filter::fromRequest($request);
199
        $filter->setUrl($request->get('url'));
200
        $result = $this->getProfiles()->getAll($filter);
201
202
        $chartData = $this->getProfiles()->getPercentileForUrl(
203
            90,
204
            $request->get('url'),
205
            $filter
206
        );
207
208
        $paging = array(
209
            'total_pages'   => $result['totalPages'],
210
            'sort'          => $filter->getSort(),
211
            'page'          => $result['page'],
212
            'direction'     => $result['direction']
213
        );
214
215
        $this->_template = 'runs/url.twig';
216
        $this->set(array(
217
            'paging'        => $paging,
218
            'base_url'      => 'url.view',
219
            'runs'          => $result['results'],
220
            'url'           => $filter->getUrl(),
221
            'chart_data'    => $chartData,
222
            'date_format'   => $this->app->config('date.format'),
223
            'search'        => array_merge($filter->toArray(), array('url' => $request->get('url'))),
224
        ));
225
    }
226
227
    /**
228
     *
229
     */
230
    public function compare()
231
    {
232
        $request = $this->app->request();
233
234
        $baseRun = $headRun = $candidates = $comparison = null;
235
        $paging = array();
236
237
        if ($request->get('base')) {
238
            $baseRun = $this->getProfiles()->get($request->get('base'));
239
        }
240
241
        // we have one selected but we need to list other runs.
242
        if ($baseRun && !$request->get('head')) {
243
            $filter = Xhgui_Storage_Filter::fromRequest($request);
244
            $filter->setUrl($baseRun->getMeta('simple_url'));
245
246
            $candidates = $this->getProfiles()->getAll($filter);
247
248
            $paging = array(
249
                'total_pages'   => $candidates['totalPages'],
250
                'page'          => $candidates['page'],
251
                'sort'          => $filter->getSort(),
252
                'direction'     => $candidates['direction']
253
            );
254
        }
255
256
        if ($request->get('head')) {
257
            $headRun = $this->getProfiles()->get($request->get('head'));
258
        }
259
260
        if ($baseRun && $headRun) {
261
            $comparison = $baseRun->compare($headRun);
262
        }
263
264
        $this->_template = 'runs/compare.twig';
265
        $this->set(array(
266
            'base_url'      => 'run.compare',
267
            'base_run'      => $baseRun,
268
            'head_run'      => $headRun,
269
            'candidates'    => $candidates,
270
            'url_params'    => $request->get(),
271
            'date_format'   => $this->app->config('date.format'),
272
            'comparison'    => $comparison,
273
            'paging'        => $paging,
274
            'search'        => array(
275
                'base' => $request->get('base'),
276
                'head' => $request->get('head'),
277
            )
278
        ));
279
    }
280
281
    /**
282
     *
283
     */
284
    public function symbol()
285
    {
286
        $request    = $this->app->request();
287
        $id         = $request->get('id');
288
        $symbol     = $request->get('symbol');
289
290
        $profile = $this->getProfiles()->get($id);
291
        $profile->calculateSelf();
292
        list($parents, $current, $children) = $profile->getRelatives($symbol);
293
294
        $this->_template = 'runs/symbol.twig';
295
        $this->set(array(
296
            'symbol'    => $symbol,
297
            'id'        => $id,
298
            'main'      => $profile->get('main()'),
299
            'parents'   => $parents,
300
            'current'   => $current,
301
            'children'  => $children,
302
        ));
303
    }
304
305
    /**
306
     *
307
     */
308
    public function symbolShort()
309
    {
310
        $request    = $this->app->request();
311
        $id         = $request->get('id');
312
        $threshold  = $request->get('threshold');
313
        $symbol     = $request->get('symbol');
314
        $metric     = $request->get('metric');
315
316
        $profile = $this->getProfiles()->get($id);
317
        $profile->calculateSelf();
318
        list($parents, $current, $children) = $profile->getRelatives($symbol, $metric, $threshold);
319
320
        $this->_template = 'runs/symbol-short.twig';
321
        $this->set(array(
322
            'symbol'    => $symbol,
323
            'id'        => $id,
324
            'main'      => $profile->get('main()'),
325
            'parents'   => $parents,
326
            'current'   => $current,
327
            'children'  => $children,
328
        ));
329
    }
330
331
    /**
332
     *
333
     */
334
    public function callgraph()
335
    {
336
        $request = $this->app->request();
337
        $profile = $this->getProfiles()->get($request->get('id'));
338
339
        $this->_template = 'runs/callgraph.twig';
340
        $this->set(array(
341
            'profile'       => $profile,
342
            'date_format'   => $this->app->config('date.format'),
343
        ));
344
    }
345
346
    /**
347
     * @return string
348
     * @throws Exception
349
     */
350
    public function callgraphData($nodes = false)
351
    {
352
        $request    = $this->app->request();
353
        $response   = $this->app->response();
354
        $profile    = $this->getProfiles()->get($request->get('id'));
355
        $metric     = $request->get('metric') ?: 'wt';
356
        $threshold  = (float)$request->get('threshold') ?: 0.01;
357
358
        if ($nodes) {
359
            $callgraph  = $profile->getCallgraphNodes($metric, $threshold);
0 ignored issues
show
Bug introduced by
The method getCallgraphNodes() does not exist on Xhgui_Profile. Did you maybe mean getCallgraph()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
360
        } else {
361
            $callgraph  = $profile->getCallgraph($metric, $threshold);
362
        }
363
364
        $response->headers->set('Content-Type', 'application/json');
365
        return $response->body(json_encode($callgraph));
366
    }
367
368
    /**
369
     * @return Xhgui_WatchedFunctionsStorageInterface
370
     */
371
    public function getWatches()
372
    {
373
        return $this->watches;
374
    }
375
376
    /**
377
     * @param Xhgui_WatchedFunctionsStorageInterface $watches
378
     */
379
    public function setWatches($watches)
380
    {
381
        $this->watches = $watches;
382
    }
383
384
    /**
385
     * @return Xhgui_Profiles
386
     */
387
    public function getProfiles()
388
    {
389
        return $this->profiles;
390
    }
391
392
    /**
393
     * @param Xhgui_Profiles $profiles
394
     */
395
    public function setProfiles($profiles)
396
    {
397
        $this->profiles = $profiles;
398
    }
399
}
400