From e3c2cb46560c0e1275149504ccf46e417d2d72d2 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Mon, 20 May 2013 02:15:54 +0200 Subject: [PATCH] MDL-39733 xhprof: implement export of runs --- lib/xhprof/xhprof_moodle.php | 169 ++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 4 deletions(-) diff --git a/lib/xhprof/xhprof_moodle.php b/lib/xhprof/xhprof_moodle.php index a53fd61f54c..c8c7be7fc51 100644 --- a/lib/xhprof/xhprof_moodle.php +++ b/lib/xhprof/xhprof_moodle.php @@ -23,13 +23,18 @@ defined('MOODLE_INTERNAL') || die(); -// need some stuff from xhprof +// Need some stuff from xhprof. require_once($CFG->libdir . '/xhprof/xhprof_lib/utils/xhprof_lib.php'); require_once($CFG->libdir . '/xhprof/xhprof_lib/utils/xhprof_runs.php'); -// need some stuff from moodle -require_once($CFG->libdir.'/tablelib.php'); +// Need some stuff from moodle. +require_once($CFG->libdir . '/tablelib.php'); +require_once($CFG->libdir . '/setuplib.php'); +require_once($CFG->libdir . '/phpunit/classes/util.php'); +require_once($CFG->dirroot . '/backup/util/xml/xml_writer.class.php'); +require_once($CFG->dirroot . '/backup/util/xml/output/xml_output.class.php'); +require_once($CFG->dirroot . '/backup/util/xml/output/file_xml_output.class.php'); -// TODO: Change the implementation below to proper profiling class +// TODO: Change the implementation below to proper profiling class. /** * Returns if profiling is running, optionally setting it @@ -408,6 +413,162 @@ function profiling_get_difference($number1, $number2, $units = '', $factor = 1, return $startspan . $delta . ' ' . $fnumdiff . ' ' . $units . ' (' . $fperdiff . '%)' . $endspan; } +/** + * Export profiling runs to a .mpr (moodle profile runs) file. + * + * This function gets an array of profiling runs (array of runids) and + * saves a .mpr file into destinantion for ulterior handling. + * + * Format of .mpr files: + * mpr files are simple zip packages containing these files: + * - moodle_profiling_runs.xml: Metadata about the information + * exported. Contains some header information (version and + * release of moodle, database, git hash - if available, date + * of export...) and a list of all the runids included in the + * export. + * - runid.xml: One file per each run detailed in the main file, + * containing the raw dump of the given runid in the profiling table. + * + * Posible improvement: Start storing some extra information in the + * profiling table for each run (moodle version, database, git hash...). + * + * @param array $runids list of runids to be exported. + * @param string $file filesystem fullpath to destination .mpr file. + * @return boolean the mpr file has been succesfully exported (true) or no (false). + */ +function profiling_export_runs(array $runids, $file) { + global $CFG, $DB; + + // Verify we have passed proper runids. + if (empty($runids)) { + return false; + } + + // Verify all the passed runids do exist. + list ($insql, $inparams) = $DB->get_in_or_equal($runids); + $reccount = $DB->count_records_select('profiling', 'runid ' . $insql, $inparams); + if ($reccount != count($runids)) { + return false; + } + + // Verify the $file path is writeable. + $base = dirname($file); + if (!is_writable($base)) { + return false; + } + + // Create temp directory where the temp information will be generated. + $tmpdir = $base . '/' . md5(implode($runids) . time() . random_string(20)); + mkdir($tmpdir); + + // Generate the xml contents in the temp directory. + $status = profiling_export_generate($runids, $tmpdir); + + // Package (zip) all the information into the final .mpr file. + if ($status) { + $status = profiling_export_package($file, $tmpdir); + } + + // Process finished ok, clean and return + fulldelete($tmpdir); + return $status; +} + +/** + * Generate the mpr contents (xml files) in the temporal directory. + * + * @param array $runids list of runids to be generated. + * @param string $tmpdir filesystem fullpath of tmp generation. + * @return boolean the mpr contents have been generated (true) or no (false). + */ +function profiling_export_generate(array $runids, $tmpdir) { + global $CFG, $DB; + + // Calculate the header information to be sent to moodle_profiling_runs.xml. + $release = $CFG->release; + $version = $CFG->version; + $dbtype = $CFG->dbtype; + $githash = phpunit_util::get_git_hash(); + $date = time(); + + // Create the xml output and writer for the main file. + $mainxo = new file_xml_output($tmpdir . '/moodle_profiling_runs.xml'); + $mainxw = new xml_writer($mainxo); + + // Output begins. + $mainxw->start(); + $mainxw->begin_tag('moodle_profiling_runs'); + + // Send header information. + $mainxw->begin_tag('info'); + $mainxw->full_tag('release', $release); + $mainxw->full_tag('version', $version); + $mainxw->full_tag('dbtype', $dbtype); + $mainxw->full_tag('githash', $githash); + $mainxw->full_tag('date', $date); + $mainxw->end_tag('info'); + + // Send information about runs. + $mainxw->begin_tag('runs'); + foreach ($runids as $runid) { + // Get the run information from DB. + $run = $DB->get_record('profiling', array('runid' => $runid), '*', MUST_EXIST); + $attributes = array( + 'id' => $run->id, + 'ref' => $run->runid . '.xml'); + $mainxw->full_tag('run', null, $attributes); + // Create the individual run file. + $runxo = new file_xml_output($tmpdir . '/' . $attributes['ref']); + $runxw = new xml_writer($runxo); + $runxw->start(); + $runxw->begin_tag('moodle_profiling_run'); + $runxw->full_tag('id', $run->id); + $runxw->full_tag('runid', $run->runid); + $runxw->full_tag('url', $run->url); + $runxw->full_tag('runreference', $run->runreference); + $runxw->full_tag('runcomment', $run->runcomment); + $runxw->full_tag('timecreated', $run->timecreated); + $runxw->full_tag('totalexecutiontime', $run->totalexecutiontime); + $runxw->full_tag('totalcputime', $run->totalcputime); + $runxw->full_tag('totalcalls', $run->totalcalls); + $runxw->full_tag('totalmemory', $run->totalmemory); + $runxw->full_tag('data', $run->data); + $runxw->end_tag('moodle_profiling_run'); + $runxw->stop(); + } + $mainxw->end_tag('runs'); + $mainxw->end_tag('moodle_profiling_runs'); + $mainxw->stop(); + + return true; +} + +/** + * Package (zip) the mpr contents (xml files) in the final location. + * + * @param string $file filesystem fullpath to destination .mpr file. + * @param string $tmpdir filesystem fullpath of tmp generation. + * @return boolean the mpr contents have been generated (true) or no (false). + */ +function profiling_export_package($file, $tmpdir) { + // Get the list of files in $tmpdir. + $filestemp = get_directory_list($tmpdir, '', false, true, true); + $files = array(); + + // Add zip paths and fs paths to all them. + foreach ($filestemp as $filetemp) { + $files[$filetemp] = $tmpdir . '/' . $filetemp; + } + + // Get the zip_packer. + $zippacker = get_file_packer('application/zip'); + + // Generate the packaged file. + $zippacker->archive_to_pathname($files, $file); + + return true; +} + /** * Custom implementation of iXHProfRuns *