diff options
| author | ruki <[email protected]> | 2023-03-17 11:04:09 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-03-17 11:04:09 +0800 |
| commit | 0c21c9d45db5beae07b3b04c8c2d415b9dbee69a (patch) | |
| tree | 608a891d20576b4666e89606f61a6da9c0cce67f | |
| parent | 7cb489300326c7c6b83c7787ed2f8d6d909b4666 (diff) | |
| parent | cad3383225d2f7a8eeeac8f095468d8f1c1bf6f6 (diff) | |
Merge pull request #3517 from xmake-io/profile
Profile compile and link
| -rw-r--r-- | xmake/core/base/profiler.lua | 158 | ||||
| -rw-r--r-- | xmake/core/package/package.lua | 2 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/profiler.lua | 40 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/lib/detect/find_file.lua | 20 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/lib/detect/find_path.lua | 20 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/lib/detect/find_program.lua | 3 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/lib/detect/find_programver.lua | 3 | ||||
| -rw-r--r-- | xmake/core/tool/compiler.lua | 6 | ||||
| -rw-r--r-- | xmake/core/tool/linker.lua | 6 | ||||
| -rw-r--r-- | xmake/modules/lib/detect/has_flags.lua | 7 | ||||
| -rw-r--r-- | xmake/plugins/show/lists/envs.lua | 2 |
11 files changed, 221 insertions, 46 deletions
diff --git a/xmake/core/base/profiler.lua b/xmake/core/base/profiler.lua index 429715eae..09b4c45f5 100644 --- a/xmake/core/base/profiler.lua +++ b/xmake/core/base/profiler.lua @@ -24,6 +24,7 @@ local profiler = {} -- load modules local os = require("base/os") local path = require("base/path") +local heap = require("base/heap") local table = require("base/table") local utils = require("base/utils") local string = require("base/string") @@ -52,11 +53,10 @@ function profiler:_func_report(funcinfo) local key = self:_func_key(funcinfo) local report = self._REPORTS_BY_KEY[key] if not report then - report = - { - funcinfo = funcinfo - , callcount = 0 - , totaltime = 0 + report = { + funcinfo = funcinfo, + callcount = 0, + totaltime = 0 } self._REPORTS_BY_KEY[key] = report table.insert(self._REPORTS, report) @@ -64,6 +64,55 @@ function profiler:_func_report(funcinfo) return report end +-- get the tag key +function profiler:_tag_key(name, argv) + local key = name + if argv then + for _, item in ipairs(argv) do + if type(item) == "table" then + key = key .. os.args(item) + else + key = key .. tostring(item) + end + end + end + return key +end + +-- get the tag title +function profiler:_tag_title(name, argv) + local key = name + if argv then + for _, item in ipairs(argv) do + if type(item) == "table" then + key = key .. ": " .. os.args(item) + else + key = key .. ": " .. tostring(item) + end + end + end + return key +end + +-- get the tag report +function profiler:_tag_report(name, argv) + self._REPORTS_BY_KEY = self._REPORTS_BY_KEY or {} + local key = self:_tag_key(name, argv) + local report = self._REPORTS_BY_KEY[key] + if not report then + report = { + name = name, + argv = argv, + callcount = 0, + totaltime = 0 + } + self._REPORTS_BY_KEY[key] = report + self._REPORTS = self._REPORTS or {} + table.insert(self._REPORTS, report) + end + return report +end + -- profiling call function profiler:_profiling_call(funcinfo) local report = self:_func_report(funcinfo) @@ -106,10 +155,9 @@ end -- start profiling function profiler:start() - local mode = self:mode() - if mode and mode == "trace" then + if self:is_trace() then debug.sethook(profiler._tracing_handler, 'cr', 0) - else + elseif self:is_perf("call") then self._REPORTS = self._REPORTS or {} self._REPORTS_BY_KEY = self._REPORTS_BY_KEY or {} self._STARTIME = self._STARTIME or os.clock() @@ -119,44 +167,87 @@ end -- stop profiling function profiler:stop() - - -- trace? - local mode = self:mode() - if mode and mode == "trace" then - -- stop to hook + if self:is_trace() then debug.sethook() - else - - -- save the stop time + elseif self:is_perf("call") then self._STOPTIME = os.clock() - - -- stop to hook debug.sethook() -- calculate the total time local totaltime = self._STOPTIME - self._STARTIME -- sort reports - table.sort(self._REPORTS, function(a, b) + local reports = self._REPORTS or {} + table.sort(reports, function(a, b) return a.totaltime > b.totaltime end) -- show reports - for _, report in ipairs(self._REPORTS) do - - -- calculate percent + for _, report in ipairs(reports) do local percent = (report.totaltime / totaltime) * 100 if percent < 1 then break end - - -- trace utils.print("%6.3f, %6.2f%%, %7d, %s", report.totaltime, percent, report.callcount, self:_func_title(report.funcinfo)) end + elseif self:is_perf("tag") then + + -- sort reports, topN + local reports = self._REPORTS or {} + local h = heap.valueheap({cmp = function(a, b) + return a.totaltime > b.totaltime + end}) + for _, report in ipairs(reports) do + h:push(report) + end + + -- show reports + local count = 0 + while count < 64 and h:length() > 0 do + local report = h:pop() + utils.print("%6.3f, %7d, %s", report.totaltime, report.callcount, self:_tag_title(report.name, report.argv)) + count = count + 1 + end + if h:length() > 0 then + utils.print("...") + end end end --- get profiler mode, e.g. perf, trace +-- enter the given tag for perl:tag +function profiler:enter(name, ...) + local is_perf_tag = self._IS_PERF_TAG + if is_perf_tag == nil then + is_perf_tag = self:is_perf("tag") + self._IS_PERF_TAG = is_perf_tag + end + if is_perf_tag then + local argv = table.pack(...) + local report = self:_tag_report(name, argv) + report.calltime = os.clock() + report.callcount = report.callcount + 1 + end +end + +-- leave the given tag for perl:tag +function profiler:leave(name, ...) + local is_perf_tag = self._IS_PERF_TAG + if is_perf_tag == nil then + is_perf_tag = self:is_perf("tag") + self._IS_PERF_TAG = is_perf_tag + end + if is_perf_tag then + local stoptime = os.clock() + local argv = table.pack(...) + local report = self:_tag_report(name, argv) + if report.calltime and report.calltime > 0 then + report.totaltime = report.totaltime + (stoptime - report.calltime) + report.calltime = 0 + end + end +end + +-- get profiler mode, e.g. perf:call, perf:tag, trace function profiler:mode() local mode = self._MODE if mode == nil then @@ -166,10 +257,23 @@ function profiler:mode() return mode or nil end +-- is trace? +function profiler:is_trace() + local mode = self:mode() + return mode and mode == "trace" +end + +-- is perf? +function profiler:is_perf(name) + local mode = self:mode() + if mode and name then + return mode == "perf:" .. name + end +end + -- profiler is enabled? function profiler:enabled() - local mode = self:mode() - return mode ~= nil and (mode == "trace" or mode == "perf") + return self:is_perf("call") or self:is_perf("tag") or self:is_trace() end -- return module diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua index f279950d0..a7171c1d1 100644 --- a/xmake/core/package/package.lua +++ b/xmake/core/package/package.lua @@ -1507,12 +1507,10 @@ function _instance:_fetch_tool(opt) end end else - print("find_tool", self:name()) fetchinfo = self:find_tool(self:name(), {require_version = opt.require_version, cachekey = "fetch_package_xmake", norun = true, -- we need not run it to check for xmake/packages, @see https://github.com/xmake-io/xmake-repo/issues/66 force = opt.force}) - print("fetchinfo", fetchinfo) -- may be toolset, not single tool if not fetchinfo then diff --git a/xmake/core/sandbox/modules/import/core/base/profiler.lua b/xmake/core/sandbox/modules/import/core/base/profiler.lua new file mode 100644 index 000000000..cd2a42460 --- /dev/null +++ b/xmake/core/sandbox/modules/import/core/base/profiler.lua @@ -0,0 +1,40 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, TBOOX Open Source Group. +-- +-- @author ruki +-- @file profiler.lua +-- + +-- define module +local sandbox_core_base_profiler = sandbox_core_base_profiler or {} + +-- load modules +local profiler = require("base/profiler") +local raise = require("sandbox/modules/raise") + +-- enter tag +function sandbox_core_base_profiler.enter(name, ...) + profiler:enter(name, ...) +end + +-- leave tag +function sandbox_core_base_profiler.leave(name, ...) + profiler:leave(name, ...) +end + +-- return module +return sandbox_core_base_profiler + diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_file.lua b/xmake/core/sandbox/modules/import/lib/detect/find_file.lua index 0c878e87d..9d7448ea8 100644 --- a/xmake/core/sandbox/modules/import/lib/detect/find_file.lua +++ b/xmake/core/sandbox/modules/import/lib/detect/find_file.lua @@ -26,6 +26,7 @@ local os = require("base/os") local path = require("base/path") local utils = require("base/utils") local table = require("base/table") +local profiler = require("base/profiler") local raise = require("sandbox/modules/raise") local vformat = require("sandbox/modules/vformat") @@ -70,16 +71,18 @@ function sandbox_lib_detect_find_file.main(name, paths, opt) opt = opt or {} -- find file + profiler:enter("find_file", name) + local results local suffixes = table.wrap(opt.suffixes) for _, _path in ipairs(table.wrap(paths)) do -- format path for builtin variables if type(_path) == "function" then - local ok, results = sandbox.load(_path) + local ok, result_or_errors = sandbox.load(_path) if ok then - _path = results or "" + _path = result_or_errors or "" else - raise(results) + raise(result_or_errors) end elseif type(_path) == "string" then if _path:match("^%$%(env .+%)$") then @@ -95,21 +98,24 @@ function sandbox_lib_detect_find_file.main(name, paths, opt) if #suffixes > 0 then for _, suffix in ipairs(suffixes) do local filedir = path.join(_s_path, suffix) - local results = sandbox_lib_detect_find_file._find(filedir, name) + results = sandbox_lib_detect_find_file._find(filedir, name) if results then - return results + goto found end end else -- find file in the given path - local results = sandbox_lib_detect_find_file._find(_s_path, name) + results = sandbox_lib_detect_find_file._find(_s_path, name) if results then - return results + goto found end end end end end +::found:: + profiler:leave("find_file", name) + return results end -- return module diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_path.lua b/xmake/core/sandbox/modules/import/lib/detect/find_path.lua index a88562a76..78b9f4c9d 100644 --- a/xmake/core/sandbox/modules/import/lib/detect/find_path.lua +++ b/xmake/core/sandbox/modules/import/lib/detect/find_path.lua @@ -25,6 +25,7 @@ local sandbox_lib_detect_find_path = sandbox_lib_detect_find_path or {} local os = require("base/os") local path = require("base/path") local table = require("base/table") +local profiler = require("base/profiler") local raise = require("sandbox/modules/raise") local vformat = require("sandbox/modules/vformat") @@ -76,16 +77,18 @@ function sandbox_lib_detect_find_path.main(name, paths, opt) opt = opt or {} -- find path + local results + profiler:enter("find_path", name) local suffixes = table.wrap(opt.suffixes) for _, _path in ipairs(table.wrap(paths)) do -- format path for builtin variables if type(_path) == "function" then - local ok, results = sandbox.load(_path) + local ok, result_or_errors = sandbox.load(_path) if ok then - _path = results or "" + _path = result_or_errors or "" else - raise(results) + raise(result_or_errors) end else _path = vformat(_path) @@ -95,19 +98,22 @@ function sandbox_lib_detect_find_path.main(name, paths, opt) if #suffixes > 0 then for _, suffix in ipairs(suffixes) do local filedir = path.join(_path, suffix) - local results = sandbox_lib_detect_find_path._find(filedir, name) + results = sandbox_lib_detect_find_path._find(filedir, name) if results then - return results + goto found end end else -- find file in the given path - local results = sandbox_lib_detect_find_path._find(_path, name) + results = sandbox_lib_detect_find_path._find(_path, name) if results then - return results + goto found end end end +::found:: + profiler:leave("find_path", name) + return results end -- return module diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_program.lua b/xmake/core/sandbox/modules/import/lib/detect/find_program.lua index 2bb04697e..2ac26e660 100644 --- a/xmake/core/sandbox/modules/import/lib/detect/find_program.lua +++ b/xmake/core/sandbox/modules/import/lib/detect/find_program.lua @@ -28,6 +28,7 @@ local option = require("base/winos") local table = require("base/table") local utils = require("base/utils") local option = require("base/option") +local profiler = require("base/profiler") local project = require("project/project") local detectcache = require("cache/detectcache") local sandbox = require("sandbox/sandbox") @@ -303,7 +304,9 @@ function sandbox_lib_detect_find_program.main(name, opt) -- find executable program checking = coroutine_running and name or nil + profiler:enter("find_program", name) result = sandbox_lib_detect_find_program._find(name, paths, opt) + profiler:leave("find_program", name) checking = nil -- cache result diff --git a/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua b/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua index 376b11005..988180ecb 100644 --- a/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua +++ b/xmake/core/sandbox/modules/import/lib/detect/find_programver.lua @@ -28,6 +28,7 @@ local table = require("base/table") local utils = require("base/utils") local option = require("base/option") local semver = require("base/semver") +local profiler = require("base/profiler") local project = require("project/project") local detectcache = require("cache/detectcache") local sandbox = require("sandbox/sandbox") @@ -81,6 +82,7 @@ function sandbox_lib_detect_find_programver.main(program, opt) -- attempt to get version output info checking = coroutine_running and program or nil + profiler:enter("find_programver", program) local ok = false local outdata = nil local command = opt.command @@ -95,6 +97,7 @@ function sandbox_lib_detect_find_programver.main(program, opt) ok, outdata = os.iorunv(program, {command or "--version"}, {envs = opt.envs}) end checking = nil + profiler:leave("find_programver", program) -- find version info if ok and outdata and #outdata > 0 then diff --git a/xmake/core/tool/compiler.lua b/xmake/core/tool/compiler.lua index bf5dead36..8dc7bffd9 100644 --- a/xmake/core/tool/compiler.lua +++ b/xmake/core/tool/compiler.lua @@ -28,6 +28,7 @@ local utils = require("base/utils") local table = require("base/table") local string = require("base/string") local option = require("base/option") +local profiler = require("base/profiler") local tool = require("tool/tool") local builder = require("tool/builder") local config = require("project/config") @@ -269,7 +270,10 @@ function compiler:compile(sourcefiles, objectfile, opt) -- compile it opt = table.copy(opt) opt.target = self:target() - return sandbox.load(self:_tool().compile, self:_tool(), sourcefiles, objectfile, opt.dependinfo, compflags, opt) + profiler:enter(self:name(), "compile", sourcefiles) + local ok, errors = sandbox.load(self:_tool().compile, self:_tool(), sourcefiles, objectfile, opt.dependinfo, compflags, opt) + profiler:leave(self:name(), "compile", sourcefiles) + return ok, errors end -- get the compile arguments list diff --git a/xmake/core/tool/linker.lua b/xmake/core/tool/linker.lua index 09436aeee..88b6da6a9 100644 --- a/xmake/core/tool/linker.lua +++ b/xmake/core/tool/linker.lua @@ -28,6 +28,7 @@ local utils = require("base/utils") local table = require("base/table") local string = require("base/string") local option = require("base/option") +local profiler = require("base/profiler") local config = require("project/config") local sandbox = require("sandbox/sandbox") local language = require("language/language") @@ -217,7 +218,10 @@ function linker:link(objectfiles, targetfile, opt) local linkflags = opt.linkflags or self:linkflags(opt) opt = table.copy(opt) opt.target = self:target() - return sandbox.load(self:_tool().link, self:_tool(), table.wrap(objectfiles), self:_targetkind(), targetfile, linkflags, opt) + profiler:enter(self:name(), "link", targetfile) + local ok, errors = sandbox.load(self:_tool().link, self:_tool(), table.wrap(objectfiles), self:_targetkind(), targetfile, linkflags, opt) + profiler:leave(self:name(), "link", targetfile) + return ok, errors end -- get the link arguments list diff --git a/xmake/modules/lib/detect/has_flags.lua b/xmake/modules/lib/detect/has_flags.lua index 3567ceae6..dc11db11c 100644 --- a/xmake/modules/lib/detect/has_flags.lua +++ b/xmake/modules/lib/detect/has_flags.lua @@ -21,6 +21,7 @@ -- imports import("core.base.option") import("core.base.scheduler") +import("core.base.profiler") import("core.project.config") import("core.cache.detectcache") import("lib.detect.find_tool") @@ -117,6 +118,9 @@ function main(name, flags, opt) end checkflags = results + -- start profile + profiler.enter("has_flags", tool.name, checkflags[1]) + -- detect.tools.xxx.has_flags(flags, opt)? _g._checking = coroutine_running and key or nil local hasflags = import("detect.tools." .. tool.name .. ".has_flags", {try = true}) @@ -132,6 +136,9 @@ function main(name, flags, opt) _g._checking = nil result = result or false + -- stop profile + profiler.leave("has_flags", tool.name, checkflags[1]) + -- trace if option.get("verbose") or option.get("diagnosis") or opt.verbose then cprintf("${dim}checking for flags (") diff --git a/xmake/plugins/show/lists/envs.lua b/xmake/plugins/show/lists/envs.lua index 753efd997..11d3ffc59 100644 --- a/xmake/plugins/show/lists/envs.lua +++ b/xmake/plugins/show/lists/envs.lua @@ -36,7 +36,7 @@ function main() XMAKE_RAMDIR = {"Set the ramdisk directory.", os.getenv("XMAKE_RAMDIR")}, XMAKE_RCFILES = {"Set the runtime configuration files.", path.joinenv(project.rcfiles())}, XMAKE_TMPDIR = {"Set the temporary directory.", os.tmpdir()}, - XMAKE_PROFILE = {"Start profiler, e.g. perf, trace, stuck.", os.getenv("XMAKE_PROFILE")}, + XMAKE_PROFILE = {"Start profiler, e.g. perf:call, perf:tag, trace, stuck.", os.getenv("XMAKE_PROFILE")}, XMAKE_PKG_CACHEDIR = {"Set the cache directory of packages.", os.getenv("XMAKE_PKG_CACHEDIR")}, XMAKE_PKG_INSTALLDIR = {"Set the install directory of packages.", os.getenv("XMAKE_PKG_INSTALLDIR")}} local width = 24 |
