diff --git a/.github/workflows/mutalkCI.yml b/.github/workflows/mutalkCI.yml new file mode 100644 index 0000000..cc622da --- /dev/null +++ b/.github/workflows/mutalkCI.yml @@ -0,0 +1,96 @@ +name: MutalkCI + +on: + push: + branches: [ main ] + paths-ignore: + - 'README.md' + - 'resources/**' + pull_request: + branches: [ main ] + paths-ignore: + - 'README.md' + - 'resources/**' + + workflow_dispatch: +# inputs: +# mode: +# description: 'modes: full (run on all project), diff (run on the last commit diff), commit (especify a commit to calculate diff)' +# required: true +# default: 'diff' +# commit: +# description: 'If mode == commit, here you enter the Commid id/name/shortId to evaluate the diff' +# required: false +# default: '' + +jobs: + eventBased: + runs-on: ubuntu-latest + if: github.event_name != 'workflow_dispatch' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: hpi-swa/setup-smalltalkCI@v1 + id: smalltalkci + with: + smalltalk-image: Pharo64-stable + + - run: smalltalkci -s ${{ steps.smalltalkci.outputs.smalltalk-image }} + shell: bash + timeout-minutes: 15 + + - run: ${{env.SMALLTALK_CI_VM}} ${{env.SMALLTALK_CI_IMAGE}} metacello install "github://matburnx/mutalk:-CI-not-working" BaselineOfMuTalk + shell: bash + + - run: ${{env.SMALLTALK_CI_VM}} ${{env.SMALLTALK_CI_IMAGE}} mutalk --project=${{github.event.repository.name}} + shell: bash + + - run: cp ${{env.SMALLTALK_CI_BUILD_BASE}}/__mutalk_summary.md $GITHUB_STEP_SUMMARY + + - uses: actions/upload-artifact@v4 + with: + name: mutation-testing-output + path: ${{env.SMALLTALK_CI_BUILD_BASE}}/__mutalk_export.json + + manual: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: hpi-swa/setup-smalltalkCI@v1 + id: smalltalkci + with: + smalltalk-image: Pharo64-stable + + - run: smalltalkci -s ${{ steps.smalltalkci.outputs.smalltalk-image }} + shell: bash + timeout-minutes: 15 + + - run: ${{env.SMALLTALK_CI_VM}} ${{env.SMALLTALK_CI_IMAGE}} metacello install "github://matburnx/mutalk:-CI-not-working" BaselineOfMuTalk + shell: bash + + - run: ${{env.SMALLTALK_CI_VM}} ${{env.SMALLTALK_CI_IMAGE}} mutalk --project=${{github.event.repository.name}} + +# - name: full mutation testing +# if: github.event.inputs.mode == 'full' +# run: ${{env.SMALLTALK_CI_VM}} ${{env.SMALLTALK_CI_IMAGE}} mutalk --project=${{github.event.repository.name}} + +# - name: diff mutation testing +# if: github.event.inputs.mode == 'diff' +# run: ${{env.SMALLTALK_CI_VM}} ${{env.SMALLTALK_CI_IMAGE}} mutalk --project=${{github.event.repository.name}} --diff + +# - name: commit mutation testing +# if: github.event.inputs.mode == 'commit' +# run: ${{env.SMALLTALK_CI_VM}} ${{env.SMALLTALK_CI_IMAGE}} mutalk --project=${{github.event.repository.name}} --commit=${{github.event.inputs.commit}} + + - run: cp ${{env.SMALLTALK_CI_BUILD_BASE}}/__mutalk_summary.md $GITHUB_STEP_SUMMARY + + - uses: actions/upload-artifact@v4 + with: + name: mutation-testing-output + path: ${{env.SMALLTALK_CI_BUILD_BASE}}/__mutalk_export.json diff --git a/.github/workflows/smalltalkCI.yml b/.github/workflows/smalltalkCI.yml index fe86f88..a2db122 100644 --- a/.github/workflows/smalltalkCI.yml +++ b/.github/workflows/smalltalkCI.yml @@ -1,14 +1,20 @@ # This is a basic workflow to help you get started with Actions -name: CI +name: pharoCI # Controls when the workflow will run on: # Triggers the workflow on push or pull request events but only for the "main" branch push: branches: [ "main" ] + paths-ignore: + - 'README.md' + - 'resources/**' pull_request: branches: [ "main" ] + paths-ignore: + - 'README.md' + - 'resources/**' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -23,7 +29,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: hpi-swa/setup-smalltalkCI@v1 id: smalltalkci with: diff --git a/.smalltalk.ston b/.smalltalk.ston index d29d713..5c019e9 100644 --- a/.smalltalk.ston +++ b/.smalltalk.ston @@ -6,7 +6,7 @@ SmalltalkCISpec { #platforms : [ #pharo ] }, SCIMetacelloLoadSpec { - #baseline : 'TreeParser', + #baseline : 'PerfTreeParser', #directory : 'src', #platforms : [ #pharo ] } diff --git a/README.md b/README.md index 0a032ff..c54972f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,46 @@ # XCVMProfiler -VM Profiler based on instruments +VM Profiler based on [instruments](https://help.apple.com/instruments/mac/current/#/dev7b09c84f5) and [perf](https://perf.wiki.kernel.org/) + +## Instruments 🍎 +### Install +```smalltalk +Metacello new + baseline: 'XCTrace'; + repository: 'github://Alamvic/XCVMProfiler:main/src'; + load. +``` + +### How to use +```smalltalk +fr := (FileLocator home / 'path/to/XCVMProfiler/resources/test-profile.trace') asFileReference. +"To get the XML data" +tree := XCTraceTree fromTimeProfileFileReference: fr. +samples := tree samples. + +"To directly get the different classified profiles" +primitiveProfiles := (XCVMDifferentialPrimitiveProfiler onFiles: {fr}) profiles. +profiles := (XCVMDifferentialProfiler onFiles: {fr}) profiles. +``` +--- +## perf 🐧 +### Install +```smalltalk +Metacello new + baseline: 'PerfTreeParser'; + repository: 'github://Alamvic/XCVMProfiler:main/src'; + load. +``` + +### How to use +```smalltalk +fr := (FileLocator home / 'path/to/XCVMProfiler/resources/perf_stock_multiple_children.txt') asFileReference. + +"Use `parseFile:` to directly get the head of the node with all the parsing done" +node := PerfTreeParser parseFile: fr. + +"To get the traces of the nodes:" +traces := node traces. + +"You can use `fromFile:` if you want to play with the parser" +parser := PerfTreeParser fromFile: fr +``` diff --git a/resources/perf_stock_multiple_children.txt b/resources/perf_stock_multiple_children.txt new file mode 100644 index 0000000..07cf843 --- /dev/null +++ b/resources/perf_stock_multiple_children.txt @@ -0,0 +1,78 @@ +# ======== +# captured on : Wed May 29 09:28:58 2024 +# header version : 1 +# data offset : 352 +# data size : 16366888 +# feat offset : 16367240 +# hostname : matburnx-laptop +# os release : 6.8.0-76060800daily20240311-generic +# perf version : 6.8.0 +# arch : x86_64 +# nrcpus online : 12 +# nrcpus avail : 12 +# cpudesc : AMD Ryzen 5 4600H with Radeon Graphics +# cpuid : AuthenticAMD,23,96,1 +# total memory : 15776888 kB +# cmdline : /usr/lib/linux-tools-6.8.0-76060800daily20240311/perf record -g --call-graph=dwarf -- ./script2.sh --all-user +# event : name = cycles:P, , id = { 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287 }, type = 0 (PERF_TYPE_HARDWARE), size = 136, config = 0 (PERF_COUNT_HW_CPU_CYCLES), { sample_period, sample_freq } = 4000, sample_type = IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC, read_format = ID|LOST, disabled = 1, inherit = 1, mmap = 1, comm = 1, freq = 1, enable_on_exec = 1, task = 1, precise_ip = 2, mmap_data = 1, sample_id_all = 1, exclude_callchain_user = 1, mmap2 = 1, comm_exec = 1, ksymbol = 1, bpf_event = 1, sample_regs_user = 0xff0fff, sample_stack_user = 8192 +# CPU_TOPOLOGY info available, use -I to display +# NUMA_TOPOLOGY info available, use -I to display +# pmu mappings: cpu = 4, amd_iommu_0 = 12, breakpoint = 5, ibs_fetch = 10, ibs_op = 11, kprobe = 8, msr = 13, power = 14, software = 1, tracepoint = 2, uprobe = 9 +# CACHE info available, use -I to display +# time of first sample : 924.754662 +# time of last sample : 925.152255 +# sample duration : 397.592 ms +# MEM_TOPOLOGY info available, use -I to display +# bpf_prog_info 2: bpf_prog_7cc47bbf07148bfe_hid_tail_call addr 0xffffffffc0314954 size 110 +# bpf_prog_info 5: bpf_prog_e3dbd137be8d6168 addr 0xffffffffc0314b30 size 313 +# bpf_prog_info 6: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314c9c size 58 +# bpf_prog_info 7: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314d2c size 58 +# bpf_prog_info 8: bpf_prog_0ecd07b7b633809f addr 0xffffffffc0316e30 size 777 +# bpf_prog_info 9: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0317158 size 95 +# bpf_prog_info 10: bpf_prog_6deef7357e7b4530 addr 0xffffffffc03171d8 size 95 +# bpf_prog_info 11: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031eb10 size 675 +# bpf_prog_info 12: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031f018 size 671 +# bpf_prog_info 13: bpf_prog_8b9c33f36f812014 addr 0xffffffffc03214a4 size 1109 +# bpf_prog_info 14: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321914 size 95 +# bpf_prog_info 15: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321994 size 94 +# bpf_prog_info 19: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328bdc size 94 +# bpf_prog_info 20: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328c48 size 95 +# bpf_prog_info 21: bpf_prog_28a890580b33b0dc addr 0xffffffffc032b50c size 872 +# bpf_prog_info 22: bpf_prog_ee0e253c78993a24 addr 0xffffffffc032d4d0 size 673 +# bpf_prog_info 29: bpf_prog_6be0d743674e5faa_syscall__execve addr 0xffffffffc034c570 size 5360 +# bpf_prog_info 30: bpf_prog_a43530058ef998f5_do_ret_sys_execve addr 0xffffffffc034daa0 size 589 +# btf info of id 5 +# btf info of id 176 +# cpu pmu capabilities: max_precise=0 +# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details. +# missing features: (null) BRANCH_STACK GROUP_DESC AUXTRACE STAT CLOCKID DIR_FORMAT COMPRESSED CLOCK_DATA HYBRID_TOPOLOGY +# ======== +# +# +# Total Lost Samples: 0 +# +# Samples: 1K of event 'cycles:P' +# Event count (approx.): 963528052 +# +# Children Self Command Shared Object Symbol +# ........ ........ ............... ......................................... ......................................................... +# + 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe + | + ---entry_SYSCALL_64_after_hwframe + | + --45.91%--do_syscall_64 + | + |--36.78%--__x64_sys_read + | + | + |--6.88%--syscall_exit_to_user_mode + | | + | --6.86%--arch_do_signal_or_restart + | | + | --6.86%--get_signal + | + | + --0.53%--__x64_sys_openat + do_sys_openat2 + diff --git a/resources/perf_stock_same_percentage.txt b/resources/perf_stock_same_percentage.txt new file mode 100644 index 0000000..839183c --- /dev/null +++ b/resources/perf_stock_same_percentage.txt @@ -0,0 +1,72 @@ +# ======== +# captured on : Wed May 29 09:28:58 2024 +# header version : 1 +# data offset : 352 +# data size : 16366888 +# feat offset : 16367240 +# hostname : matburnx-laptop +# os release : 6.8.0-76060800daily20240311-generic +# perf version : 6.8.0 +# arch : x86_64 +# nrcpus online : 12 +# nrcpus avail : 12 +# cpudesc : AMD Ryzen 5 4600H with Radeon Graphics +# cpuid : AuthenticAMD,23,96,1 +# total memory : 15776888 kB +# cmdline : /usr/lib/linux-tools-6.8.0-76060800daily20240311/perf record -g --call-graph=dwarf -- ./script2.sh --all-user +# event : name = cycles:P, , id = { 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287 }, type = 0 (PERF_TYPE_HARDWARE), size = 136, config = 0 (PERF_COUNT_HW_CPU_CYCLES), { sample_period, sample_freq } = 4000, sample_type = IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC, read_format = ID|LOST, disabled = 1, inherit = 1, mmap = 1, comm = 1, freq = 1, enable_on_exec = 1, task = 1, precise_ip = 2, mmap_data = 1, sample_id_all = 1, exclude_callchain_user = 1, mmap2 = 1, comm_exec = 1, ksymbol = 1, bpf_event = 1, sample_regs_user = 0xff0fff, sample_stack_user = 8192 +# CPU_TOPOLOGY info available, use -I to display +# NUMA_TOPOLOGY info available, use -I to display +# pmu mappings: cpu = 4, amd_iommu_0 = 12, breakpoint = 5, ibs_fetch = 10, ibs_op = 11, kprobe = 8, msr = 13, power = 14, software = 1, tracepoint = 2, uprobe = 9 +# CACHE info available, use -I to display +# time of first sample : 924.754662 +# time of last sample : 925.152255 +# sample duration : 397.592 ms +# MEM_TOPOLOGY info available, use -I to display +# bpf_prog_info 2: bpf_prog_7cc47bbf07148bfe_hid_tail_call addr 0xffffffffc0314954 size 110 +# bpf_prog_info 5: bpf_prog_e3dbd137be8d6168 addr 0xffffffffc0314b30 size 313 +# bpf_prog_info 6: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314c9c size 58 +# bpf_prog_info 7: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314d2c size 58 +# bpf_prog_info 8: bpf_prog_0ecd07b7b633809f addr 0xffffffffc0316e30 size 777 +# bpf_prog_info 9: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0317158 size 95 +# bpf_prog_info 10: bpf_prog_6deef7357e7b4530 addr 0xffffffffc03171d8 size 95 +# bpf_prog_info 11: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031eb10 size 675 +# bpf_prog_info 12: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031f018 size 671 +# bpf_prog_info 13: bpf_prog_8b9c33f36f812014 addr 0xffffffffc03214a4 size 1109 +# bpf_prog_info 14: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321914 size 95 +# bpf_prog_info 15: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321994 size 94 +# bpf_prog_info 19: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328bdc size 94 +# bpf_prog_info 20: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328c48 size 95 +# bpf_prog_info 21: bpf_prog_28a890580b33b0dc addr 0xffffffffc032b50c size 872 +# bpf_prog_info 22: bpf_prog_ee0e253c78993a24 addr 0xffffffffc032d4d0 size 673 +# bpf_prog_info 29: bpf_prog_6be0d743674e5faa_syscall__execve addr 0xffffffffc034c570 size 5360 +# bpf_prog_info 30: bpf_prog_a43530058ef998f5_do_ret_sys_execve addr 0xffffffffc034daa0 size 589 +# btf info of id 5 +# btf info of id 176 +# cpu pmu capabilities: max_precise=0 +# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details. +# missing features: (null) BRANCH_STACK GROUP_DESC AUXTRACE STAT CLOCKID DIR_FORMAT COMPRESSED CLOCK_DATA HYBRID_TOPOLOGY +# ======== +# +# +# Total Lost Samples: 0 +# +# Samples: 1K of event 'cycles:P' +# Event count (approx.): 963528052 +# +# Children Self Command Shared Object Symbol +# ........ ........ ............... ......................................... ......................................................... +# + 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe + | + ---entry_SYSCALL_64_after_hwframe + | + --45.91%--do_syscall_64 + | + |--36.78%--__x64_sys_read + | ksys_read + | vfs_read + | | + | --36.46%--ext4_file_read_iter + | | + diff --git a/resources/perf_stock_simple.txt b/resources/perf_stock_simple.txt new file mode 100644 index 0000000..d6d2bbc --- /dev/null +++ b/resources/perf_stock_simple.txt @@ -0,0 +1,67 @@ +# ======== +# captured on : Wed May 29 09:28:58 2024 +# header version : 1 +# data offset : 352 +# data size : 16366888 +# feat offset : 16367240 +# hostname : matburnx-laptop +# os release : 6.8.0-76060800daily20240311-generic +# perf version : 6.8.0 +# arch : x86_64 +# nrcpus online : 12 +# nrcpus avail : 12 +# cpudesc : AMD Ryzen 5 4600H with Radeon Graphics +# cpuid : AuthenticAMD,23,96,1 +# total memory : 15776888 kB +# cmdline : /usr/lib/linux-tools-6.8.0-76060800daily20240311/perf record -g --call-graph=dwarf -- ./script2.sh --all-user +# event : name = cycles:P, , id = { 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287 }, type = 0 (PERF_TYPE_HARDWARE), size = 136, config = 0 (PERF_COUNT_HW_CPU_CYCLES), { sample_period, sample_freq } = 4000, sample_type = IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC, read_format = ID|LOST, disabled = 1, inherit = 1, mmap = 1, comm = 1, freq = 1, enable_on_exec = 1, task = 1, precise_ip = 2, mmap_data = 1, sample_id_all = 1, exclude_callchain_user = 1, mmap2 = 1, comm_exec = 1, ksymbol = 1, bpf_event = 1, sample_regs_user = 0xff0fff, sample_stack_user = 8192 +# CPU_TOPOLOGY info available, use -I to display +# NUMA_TOPOLOGY info available, use -I to display +# pmu mappings: cpu = 4, amd_iommu_0 = 12, breakpoint = 5, ibs_fetch = 10, ibs_op = 11, kprobe = 8, msr = 13, power = 14, software = 1, tracepoint = 2, uprobe = 9 +# CACHE info available, use -I to display +# time of first sample : 924.754662 +# time of last sample : 925.152255 +# sample duration : 397.592 ms +# MEM_TOPOLOGY info available, use -I to display +# bpf_prog_info 2: bpf_prog_7cc47bbf07148bfe_hid_tail_call addr 0xffffffffc0314954 size 110 +# bpf_prog_info 5: bpf_prog_e3dbd137be8d6168 addr 0xffffffffc0314b30 size 313 +# bpf_prog_info 6: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314c9c size 58 +# bpf_prog_info 7: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314d2c size 58 +# bpf_prog_info 8: bpf_prog_0ecd07b7b633809f addr 0xffffffffc0316e30 size 777 +# bpf_prog_info 9: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0317158 size 95 +# bpf_prog_info 10: bpf_prog_6deef7357e7b4530 addr 0xffffffffc03171d8 size 95 +# bpf_prog_info 11: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031eb10 size 675 +# bpf_prog_info 12: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031f018 size 671 +# bpf_prog_info 13: bpf_prog_8b9c33f36f812014 addr 0xffffffffc03214a4 size 1109 +# bpf_prog_info 14: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321914 size 95 +# bpf_prog_info 15: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321994 size 94 +# bpf_prog_info 19: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328bdc size 94 +# bpf_prog_info 20: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328c48 size 95 +# bpf_prog_info 21: bpf_prog_28a890580b33b0dc addr 0xffffffffc032b50c size 872 +# bpf_prog_info 22: bpf_prog_ee0e253c78993a24 addr 0xffffffffc032d4d0 size 673 +# bpf_prog_info 29: bpf_prog_6be0d743674e5faa_syscall__execve addr 0xffffffffc034c570 size 5360 +# bpf_prog_info 30: bpf_prog_a43530058ef998f5_do_ret_sys_execve addr 0xffffffffc034daa0 size 589 +# btf info of id 5 +# btf info of id 176 +# cpu pmu capabilities: max_precise=0 +# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the 'perf list' man page for further details. +# missing features: (null) BRANCH_STACK GROUP_DESC AUXTRACE STAT CLOCKID DIR_FORMAT COMPRESSED CLOCK_DATA HYBRID_TOPOLOGY +# ======== +# +# +# Total Lost Samples: 0 +# +# Samples: 1K of event 'cycles:P' +# Event count (approx.): 963528052 +# +# Children Self Command Shared Object Symbol +# ........ ........ ............... ......................................... ......................................................... +# + 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe + | + ---entry_SYSCALL_64_after_hwframe + | + --45.91%--do_syscall_64 + | + |--36.78%--__x64_sys_read + diff --git a/resources/test-empty-profile.trace b/resources/test-empty-profile.trace new file mode 100644 index 0000000..ca9ba50 --- /dev/null +++ b/resources/test-empty-profile.trace @@ -0,0 +1,4 @@ + + +timeSample Timesample-timethreadThreadthreadprocessProcessprocesscoreCorecorethread-stateStatethread-stateweightWeightweightstackBacktracebacktrace + diff --git a/resources/test-profile.trace b/resources/test-profile.trace new file mode 100644 index 0000000..37820de --- /dev/null +++ b/resources/test-profile.trace @@ -0,0 +1,46 @@ + + +time +Sample Timesample-time +threadThreadthread +processProcessprocess +coreCorecore +thread-stateStatethread-state +weightWeightweight +stackBacktracebacktrace +383060916 + +1515263 + +86036 +TODO +0 +Running +1000000 + + + + + + +384060375 + + + + + + + + + + + + + + + + + + + + diff --git a/src/BaselineOfPerfTreeParser/BaselineOfPerfTreeParser.class.st b/src/BaselineOfPerfTreeParser/BaselineOfPerfTreeParser.class.st new file mode 100644 index 0000000..2606b70 --- /dev/null +++ b/src/BaselineOfPerfTreeParser/BaselineOfPerfTreeParser.class.st @@ -0,0 +1,20 @@ +Class { + #name : #BaselineOfPerfTreeParser, + #superclass : #BaselineOf, + #category : #BaselineOfPerfTreeParser +} + +{ #category : #baselines } +BaselineOfPerfTreeParser >> baseline: spec [ + + spec for: #common do: [ + "Packages" + spec + package: #PerfTreeParser; + package: 'PerfTreeParser-Tests' with: [ spec requires: #PerfTreeParser ] ] +] + +{ #category : #accessing } +BaselineOfPerfTreeParser >> projectClass [ + ^ MetacelloCypressBaselineProject +] diff --git a/src/BaselineOfPerfTreeParser/package.st b/src/BaselineOfPerfTreeParser/package.st new file mode 100644 index 0000000..2dc44c9 --- /dev/null +++ b/src/BaselineOfPerfTreeParser/package.st @@ -0,0 +1 @@ +Package { #name : #BaselineOfPerfTreeParser } diff --git a/src/BaselineOfTreeParser/BaselineOfTreeParser.class.st b/src/BaselineOfTreeParser/BaselineOfTreeParser.class.st deleted file mode 100644 index be5629d..0000000 --- a/src/BaselineOfTreeParser/BaselineOfTreeParser.class.st +++ /dev/null @@ -1,20 +0,0 @@ -Class { - #name : #BaselineOfTreeParser, - #superclass : #BaselineOf, - #category : #BaselineOfTreeParser -} - -{ #category : #baselines } -BaselineOfTreeParser >> baseline: spec [ - - spec for: #common do: [ - "Packages" - spec - package: #TreeParser; - package: 'TreeParser-Tests' with: [ spec requires: #TreeParser ] ] -] - -{ #category : #accessing } -BaselineOfTreeParser >> projectClass [ - ^ MetacelloCypressBaselineProject -] diff --git a/src/BaselineOfTreeParser/package.st b/src/BaselineOfTreeParser/package.st deleted file mode 100644 index 3aa26e5..0000000 --- a/src/BaselineOfTreeParser/package.st +++ /dev/null @@ -1 +0,0 @@ -Package { #name : #BaselineOfTreeParser } diff --git a/src/PerfTreeParser-Tests/PerfTreeParserTest.class.st b/src/PerfTreeParser-Tests/PerfTreeParserTest.class.st new file mode 100644 index 0000000..d7beb92 --- /dev/null +++ b/src/PerfTreeParser-Tests/PerfTreeParserTest.class.st @@ -0,0 +1,536 @@ +Class { + #name : 'PerfTreeParserTest', + #superclass : 'TestCase', + #instVars : [ + 'memoryFS', + 'fileSimple', + 'fileSamePercentage', + 'fileMultipleChildren', + 'testParserSimple', + 'testParserSamePercentage', + 'testParserMultipleChildren', + 'testNodeSimple', + 'testNodeSamePercentage', + 'testNodeMultipleChildren' + ], + #category : 'PerfTreeParser-Tests', + #package : 'PerfTreeParser-Tests' +} + +{ #category : 'running' } +PerfTreeParserTest >> setUp [ + "Commands ran:" + "sudo perf record -g --call-graph=dwarf -- ./script2.sh --all-user" + "sudo perf report --header --stdio > perf_example.txt" + + super setUp. + memoryFS := FileSystem memory. + memoryFS / ('perf_stock_simple.txt') writeStreamDo: [:stream | stream nextPutAll: '# ======== +# captured on : Wed May 29 09:28:58 2024 +# header version : 1 +# data offset : 352 +# data size : 16366888 +# feat offset : 16367240 +# hostname : matburnx-laptop +# os release : 6.8.0-76060800daily20240311-generic +# perf version : 6.8.0 +# arch : x86_64 +# nrcpus online : 12 +# nrcpus avail : 12 +# cpudesc : AMD Ryzen 5 4600H with Radeon Graphics +# cpuid : AuthenticAMD,23,96,1 +# total memory : 15776888 kB +# cmdline : /usr/lib/linux-tools-6.8.0-76060800daily20240311/perf record -g --call-graph=dwarf -- ./script2.sh --all-user +# event : name = cycles:P, , id = { 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287 }, type = 0 (PERF_TYPE_HARDWARE), size = 136, config = 0 (PERF_COUNT_HW_CPU_CYCLES), { sample_period, sample_freq } = 4000, sample_type = IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC, read_format = ID|LOST, disabled = 1, inherit = 1, mmap = 1, comm = 1, freq = 1, enable_on_exec = 1, task = 1, precise_ip = 2, mmap_data = 1, sample_id_all = 1, exclude_callchain_user = 1, mmap2 = 1, comm_exec = 1, ksymbol = 1, bpf_event = 1, sample_regs_user = 0xff0fff, sample_stack_user = 8192 +# CPU_TOPOLOGY info available, use -I to display +# NUMA_TOPOLOGY info available, use -I to display +# pmu mappings: cpu = 4, amd_iommu_0 = 12, breakpoint = 5, ibs_fetch = 10, ibs_op = 11, kprobe = 8, msr = 13, power = 14, software = 1, tracepoint = 2, uprobe = 9 +# CACHE info available, use -I to display +# time of first sample : 924.754662 +# time of last sample : 925.152255 +# sample duration : 397.592 ms +# MEM_TOPOLOGY info available, use -I to display +# bpf_prog_info 2: bpf_prog_7cc47bbf07148bfe_hid_tail_call addr 0xffffffffc0314954 size 110 +# bpf_prog_info 5: bpf_prog_e3dbd137be8d6168 addr 0xffffffffc0314b30 size 313 +# bpf_prog_info 6: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314c9c size 58 +# bpf_prog_info 7: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314d2c size 58 +# bpf_prog_info 8: bpf_prog_0ecd07b7b633809f addr 0xffffffffc0316e30 size 777 +# bpf_prog_info 9: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0317158 size 95 +# bpf_prog_info 10: bpf_prog_6deef7357e7b4530 addr 0xffffffffc03171d8 size 95 +# bpf_prog_info 11: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031eb10 size 675 +# bpf_prog_info 12: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031f018 size 671 +# bpf_prog_info 13: bpf_prog_8b9c33f36f812014 addr 0xffffffffc03214a4 size 1109 +# bpf_prog_info 14: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321914 size 95 +# bpf_prog_info 15: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321994 size 94 +# bpf_prog_info 19: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328bdc size 94 +# bpf_prog_info 20: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328c48 size 95 +# bpf_prog_info 21: bpf_prog_28a890580b33b0dc addr 0xffffffffc032b50c size 872 +# bpf_prog_info 22: bpf_prog_ee0e253c78993a24 addr 0xffffffffc032d4d0 size 673 +# bpf_prog_info 29: bpf_prog_6be0d743674e5faa_syscall__execve addr 0xffffffffc034c570 size 5360 +# bpf_prog_info 30: bpf_prog_a43530058ef998f5_do_ret_sys_execve addr 0xffffffffc034daa0 size 589 +# btf info of id 5 +# btf info of id 176 +# cpu pmu capabilities: max_precise=0 +# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the ''perf list'' man page for further details. +# missing features: (null) BRANCH_STACK GROUP_DESC AUXTRACE STAT CLOCKID DIR_FORMAT COMPRESSED CLOCK_DATA HYBRID_TOPOLOGY +# ======== +# +# +# Total Lost Samples: 0 +# +# Samples: 1K of event ''cycles:P'' +# Event count (approx.): 963528052 +# +# Children Self Command Shared Object Symbol +# ........ ........ ............... ......................................... ......................................................... +# + 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe + | + ---entry_SYSCALL_64_after_hwframe + | + --45.91%--do_syscall_64 + | + |--36.78%--__x64_sys_read +']. + + memoryFS / ('perf_stock_same_percentage.txt') writeStreamDo: [:stream | stream nextPutAll: '# ======== +# captured on : Wed May 29 09:28:58 2024 +# header version : 1 +# data offset : 352 +# data size : 16366888 +# feat offset : 16367240 +# hostname : matburnx-laptop +# os release : 6.8.0-76060800daily20240311-generic +# perf version : 6.8.0 +# arch : x86_64 +# nrcpus online : 12 +# nrcpus avail : 12 +# cpudesc : AMD Ryzen 5 4600H with Radeon Graphics +# cpuid : AuthenticAMD,23,96,1 +# total memory : 15776888 kB +# cmdline : /usr/lib/linux-tools-6.8.0-76060800daily20240311/perf record -g --call-graph=dwarf -- ./script2.sh --all-user +# event : name = cycles:P, , id = { 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287 }, type = 0 (PERF_TYPE_HARDWARE), size = 136, config = 0 (PERF_COUNT_HW_CPU_CYCLES), { sample_period, sample_freq } = 4000, sample_type = IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC, read_format = ID|LOST, disabled = 1, inherit = 1, mmap = 1, comm = 1, freq = 1, enable_on_exec = 1, task = 1, precise_ip = 2, mmap_data = 1, sample_id_all = 1, exclude_callchain_user = 1, mmap2 = 1, comm_exec = 1, ksymbol = 1, bpf_event = 1, sample_regs_user = 0xff0fff, sample_stack_user = 8192 +# CPU_TOPOLOGY info available, use -I to display +# NUMA_TOPOLOGY info available, use -I to display +# pmu mappings: cpu = 4, amd_iommu_0 = 12, breakpoint = 5, ibs_fetch = 10, ibs_op = 11, kprobe = 8, msr = 13, power = 14, software = 1, tracepoint = 2, uprobe = 9 +# CACHE info available, use -I to display +# time of first sample : 924.754662 +# time of last sample : 925.152255 +# sample duration : 397.592 ms +# MEM_TOPOLOGY info available, use -I to display +# bpf_prog_info 2: bpf_prog_7cc47bbf07148bfe_hid_tail_call addr 0xffffffffc0314954 size 110 +# bpf_prog_info 5: bpf_prog_e3dbd137be8d6168 addr 0xffffffffc0314b30 size 313 +# bpf_prog_info 6: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314c9c size 58 +# bpf_prog_info 7: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314d2c size 58 +# bpf_prog_info 8: bpf_prog_0ecd07b7b633809f addr 0xffffffffc0316e30 size 777 +# bpf_prog_info 9: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0317158 size 95 +# bpf_prog_info 10: bpf_prog_6deef7357e7b4530 addr 0xffffffffc03171d8 size 95 +# bpf_prog_info 11: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031eb10 size 675 +# bpf_prog_info 12: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031f018 size 671 +# bpf_prog_info 13: bpf_prog_8b9c33f36f812014 addr 0xffffffffc03214a4 size 1109 +# bpf_prog_info 14: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321914 size 95 +# bpf_prog_info 15: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321994 size 94 +# bpf_prog_info 19: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328bdc size 94 +# bpf_prog_info 20: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328c48 size 95 +# bpf_prog_info 21: bpf_prog_28a890580b33b0dc addr 0xffffffffc032b50c size 872 +# bpf_prog_info 22: bpf_prog_ee0e253c78993a24 addr 0xffffffffc032d4d0 size 673 +# bpf_prog_info 29: bpf_prog_6be0d743674e5faa_syscall__execve addr 0xffffffffc034c570 size 5360 +# bpf_prog_info 30: bpf_prog_a43530058ef998f5_do_ret_sys_execve addr 0xffffffffc034daa0 size 589 +# btf info of id 5 +# btf info of id 176 +# cpu pmu capabilities: max_precise=0 +# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the ''perf list'' man page for further details. +# missing features: (null) BRANCH_STACK GROUP_DESC AUXTRACE STAT CLOCKID DIR_FORMAT COMPRESSED CLOCK_DATA HYBRID_TOPOLOGY +# ======== +# +# +# Total Lost Samples: 0 +# +# Samples: 1K of event ''cycles:P'' +# Event count (approx.): 963528052 +# +# Children Self Command Shared Object Symbol +# ........ ........ ............... ......................................... ......................................................... +# + 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe + | + ---entry_SYSCALL_64_after_hwframe + | + --45.91%--do_syscall_64 + | + |--36.78%--__x64_sys_read + | ksys_read + | vfs_read + | | + | --36.46%--ext4_file_read_iter + | | +']. + + memoryFS / ('perf_stock_multiple_children.txt') writeStreamDo: [:stream | stream nextPutAll: '# ======== +# captured on : Wed May 29 09:28:58 2024 +# header version : 1 +# data offset : 352 +# data size : 16366888 +# feat offset : 16367240 +# hostname : matburnx-laptop +# os release : 6.8.0-76060800daily20240311-generic +# perf version : 6.8.0 +# arch : x86_64 +# nrcpus online : 12 +# nrcpus avail : 12 +# cpudesc : AMD Ryzen 5 4600H with Radeon Graphics +# cpuid : AuthenticAMD,23,96,1 +# total memory : 15776888 kB +# cmdline : /usr/lib/linux-tools-6.8.0-76060800daily20240311/perf record -g --call-graph=dwarf -- ./script2.sh --all-user +# event : name = cycles:P, , id = { 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287 }, type = 0 (PERF_TYPE_HARDWARE), size = 136, config = 0 (PERF_COUNT_HW_CPU_CYCLES), { sample_period, sample_freq } = 4000, sample_type = IP|TID|TIME|ADDR|CALLCHAIN|PERIOD|REGS_USER|STACK_USER|DATA_SRC, read_format = ID|LOST, disabled = 1, inherit = 1, mmap = 1, comm = 1, freq = 1, enable_on_exec = 1, task = 1, precise_ip = 2, mmap_data = 1, sample_id_all = 1, exclude_callchain_user = 1, mmap2 = 1, comm_exec = 1, ksymbol = 1, bpf_event = 1, sample_regs_user = 0xff0fff, sample_stack_user = 8192 +# CPU_TOPOLOGY info available, use -I to display +# NUMA_TOPOLOGY info available, use -I to display +# pmu mappings: cpu = 4, amd_iommu_0 = 12, breakpoint = 5, ibs_fetch = 10, ibs_op = 11, kprobe = 8, msr = 13, power = 14, software = 1, tracepoint = 2, uprobe = 9 +# CACHE info available, use -I to display +# time of first sample : 924.754662 +# time of last sample : 925.152255 +# sample duration : 397.592 ms +# MEM_TOPOLOGY info available, use -I to display +# bpf_prog_info 2: bpf_prog_7cc47bbf07148bfe_hid_tail_call addr 0xffffffffc0314954 size 110 +# bpf_prog_info 5: bpf_prog_e3dbd137be8d6168 addr 0xffffffffc0314b30 size 313 +# bpf_prog_info 6: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314c9c size 58 +# bpf_prog_info 7: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0314d2c size 58 +# bpf_prog_info 8: bpf_prog_0ecd07b7b633809f addr 0xffffffffc0316e30 size 777 +# bpf_prog_info 9: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0317158 size 95 +# bpf_prog_info 10: bpf_prog_6deef7357e7b4530 addr 0xffffffffc03171d8 size 95 +# bpf_prog_info 11: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031eb10 size 675 +# bpf_prog_info 12: bpf_prog_ee0e253c78993a24 addr 0xffffffffc031f018 size 671 +# bpf_prog_info 13: bpf_prog_8b9c33f36f812014 addr 0xffffffffc03214a4 size 1109 +# bpf_prog_info 14: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321914 size 95 +# bpf_prog_info 15: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0321994 size 94 +# bpf_prog_info 19: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328bdc size 94 +# bpf_prog_info 20: bpf_prog_6deef7357e7b4530 addr 0xffffffffc0328c48 size 95 +# bpf_prog_info 21: bpf_prog_28a890580b33b0dc addr 0xffffffffc032b50c size 872 +# bpf_prog_info 22: bpf_prog_ee0e253c78993a24 addr 0xffffffffc032d4d0 size 673 +# bpf_prog_info 29: bpf_prog_6be0d743674e5faa_syscall__execve addr 0xffffffffc034c570 size 5360 +# bpf_prog_info 30: bpf_prog_a43530058ef998f5_do_ret_sys_execve addr 0xffffffffc034daa0 size 589 +# btf info of id 5 +# btf info of id 176 +# cpu pmu capabilities: max_precise=0 +# AMD systems uses ibs_op// PMU for some precise events, e.g.: cycles:p, see the ''perf list'' man page for further details. +# missing features: (null) BRANCH_STACK GROUP_DESC AUXTRACE STAT CLOCKID DIR_FORMAT COMPRESSED CLOCK_DATA HYBRID_TOPOLOGY +# ======== +# +# +# Total Lost Samples: 0 +# +# Samples: 1K of event ''cycles:P'' +# Event count (approx.): 963528052 +# +# Children Self Command Shared Object Symbol +# ........ ........ ............... ......................................... ......................................................... +# + 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe + | + ---entry_SYSCALL_64_after_hwframe + | + --45.91%--do_syscall_64 + | + |--36.78%--__x64_sys_read + | + | + |--6.88%--syscall_exit_to_user_mode + | | + | --6.86%--arch_do_signal_or_restart + | | + | --6.86%--get_signal + | + --0.53%--__x64_sys_openat + do_sys_openat2 +']. + + fileSimple := memoryFS / ('perf_stock_simple.txt'). + testParserSimple := PerfTreeParser fromFile: fileSimple. + testNodeSimple := (PerfTreeParser parseFile: fileSimple) first. + + fileSamePercentage := memoryFS / ('perf_stock_same_percentage.txt'). + testParserSamePercentage := PerfTreeParser fromFile: fileSamePercentage. + testNodeSamePercentage := (PerfTreeParser parseFile: fileSamePercentage) first. + + fileMultipleChildren := memoryFS / ('perf_stock_multiple_children.txt'). + testParserMultipleChildren := PerfTreeParser fromFile: fileMultipleChildren. + testNodeMultipleChildren := (PerfTreeParser parseFile: fileMultipleChildren) first. + +] + +{ #category : 'running' } +PerfTreeParserTest >> tearDown [ + + fileSimple ensureDelete. + fileSamePercentage ensureDelete. + fileMultipleChildren ensureDelete. + super tearDown +] + +{ #category : 'tests' } +PerfTreeParserTest >> testDifferenceOfSpacesAt [ + "Classical case" + + self + assert: (testParserSamePercentage differenceOfSpacesAt: 6) + equals: 1. + + self + assert: (testParserSamePercentage differenceOfSpacesAt: 1) + equals: 8. + + self assert: + (testParserSamePercentage differenceOfSpacesAt: 5) isZero +] + +{ #category : 'tests' } +PerfTreeParserTest >> testFindChildrenIndexOfIndex [ + "The last has no children" + + self assert: (testParserSimple findChildrenIndexOfIndex: + testParserSimple numberOfFunctions) isEmpty. + + "Case where 2 functions have the same number of spaces." + self + assert: (testParserSimple findChildrenIndexOfIndex: 3) + equals: { 4 } asOrderedCollection. + + "Classical case" + self + assert: (testParserSamePercentage findChildrenIndexOfIndex: 5) + equals: { 6 } asOrderedCollection. + + "Multiple children" + self + assert: (testParserMultipleChildren findChildrenIndexOfIndex: 3) + equals: { 4. 5. 8 } asOrderedCollection +] + +{ #category : 'tests' } +PerfTreeParserTest >> testFindChildrenOfIndex [ + + | aNode | + aNode := testParserMultipleChildren findChildrenOfIndex: 3. + + self + assert: aNode first name + equals: (testParserMultipleChildren nameOf: 4). + self + assert: aNode second name + equals: (testParserMultipleChildren nameOf: 5). + self + assert: aNode third name + equals: (testParserMultipleChildren nameOf: 8) +] + +{ #category : 'tests' } +PerfTreeParserTest >> testFindChildrenOfRecursive [ + + | aNode | + aNode := testNodeMultipleChildren firstChild firstChild. + + self + assert: aNode children second children first name + equals: (testParserMultipleChildren nameOf: 6). + self + assert: aNode children second children first children first name + equals: (testParserMultipleChildren nameOf: 7) +] + +{ #category : 'tests' } +PerfTreeParserTest >> testIsLeaf [ + + | example | + example := testNodeSimple firstChild firstChild. + + self assert: example isLeaf not. + self assert: example isNotLeaf. + + self assert: example children first isLeaf. + self assert: example children first isNotLeaf not. +] + +{ #category : 'tests' } +PerfTreeParserTest >> testLastSpaceAt [ + + self assert: (testParserSimple lastSpaceAt: (testParserSimple numberOfFunctions)) equals: 37. + + self assert: (testParserSimple lastSpaceAt: 1) equals: 86. + + self assert: (testParserSimple lastSpaceAt: 2) equals: 15. +] + +{ #category : 'tests' } +PerfTreeParserTest >> testNameOf [ + "Three cases where the parsing is different." + + self + assert: (testParserSimple nameOf: 1) + equals: 'entry_SYSCALL_64_after_hwframe'. + + self + assert: (testParserSamePercentage nameOf: 4) + equals: '__x64_sys_read'. + + self assert: (testParserSamePercentage nameOf: 6) equals: 'vfs_read' +] + +{ #category : 'tests' } +PerfTreeParserTest >> testNodeAtIndex [ + + "Needs more tests!!!" +] + +{ #category : 'tests' } +PerfTreeParserTest >> testNodes [ + + | example | + example := testNodeMultipleChildren firstChild firstChild children. + + self assert: example first name equals: '__x64_sys_read'. + + self assert: example second name equals: 'syscall_exit_to_user_mode'. + + self assert: example third name equals: '__x64_sys_openat' +] + +{ #category : 'tests' } +PerfTreeParserTest >> testNodesSameParent [ + + | example | + example := (testNodeMultipleChildren firstChild firstChild) children. + + self assert: testNodeMultipleChildren children first children first equals: example first parent. + + self assert: example first parent equals: example second parent +] + +{ #category : 'tests' } +PerfTreeParserTest >> testNumberOfFunctions [ + + self assert: (testParserSimple numberOfFunctions) equals: 4 +] + +{ #category : 'tests' } +PerfTreeParserTest >> testNumberOfSpacesAt [ + + + self assert: (testParserSimple numberOfSpacesAt: (testParserSimple numberOfFunctions)) equals: 27. + + self assert: (testParserSimple numberOfSpacesAt: 1) equals: 4. + + self assert: (testParserSimple numberOfSpacesAt: 3) equals: 16 +] + +{ #category : 'tests' } +PerfTreeParserTest >> testPercentageOf [ + "Cases with 4 digits." + + self assert: (testParserSimple percentageOf: 3) equals: 45.91. + + self assert: (testParserSimple percentageOf: 1) equals: 46.02. + + self + assert: + (testParserSimple percentageOf: testParserSimple numberOfFunctions) + equals: 36.78. + + "Case with 3 digits." + self + assert: (testParserMultipleChildren percentageOf: 8) + equals: 0.53. + + "Case with same percentage" + self + assert: (testParserSamePercentage percentageOf: 4) + equals: (testParserSamePercentage percentageOf: 5). + + self + assert: (testParserSamePercentage percentageOf: 5) + equals: (testParserSamePercentage percentageOf: 6) +] + +{ #category : 'tests' } +PerfTreeParserTest >> testReadLine [ + + self + assert: (testParserSimple readLine: 3) + equals: ' --45.91%--do_syscall_64'. + + self assert: (testParserSimple readLine: (testParserSimple numberOfFunctions)) equals: ' |--36.78%--__x64_sys_read' +] + +{ #category : 'tests' } +PerfTreeParserTest >> testRealPercentageOf [ + + self + assert: + (testParserSimple realPercentageOf: + testParserSimple numberOfFunctions) + equals: + (testParserSimple percentageOf: testParserSimple numberOfFunctions). + + self + assert: (testParserSamePercentage realPercentageOf: 3) + equals: 9.13. + + self + assert: (testParserSamePercentage realPercentageOf: 4) + equals: 0.0 +] + +{ #category : 'tests' } +PerfTreeParserTest >> testTime [ + + self assert: testParserSimple time equals: 397.592. + + self assert: testParserSimple root time equals: 397.592. +] + +{ #category : 'tests' } +PerfTreeParserTest >> testTraces [ + + | traces multipleTraces | + traces := (PerfTreeParser findTraces: fileSimple) first. + multipleTraces := testNodeMultipleChildren traces. + + self assert: traces size equals: 1. + + self assert: traces first name equals: '__x64_sys_read'. + + self assert: traces first weight equals: 146.24. + + self assert: multipleTraces size equals: 3. + + self assert: multipleTraces first name equals: '__x64_sys_read'. + + self assert: multipleTraces last name equals: 'do_sys_openat2'. + + self assert: multipleTraces last weight equals: 2.11 +] + +{ #category : 'tests' } +PerfTreeParserTest >> testWeight [ + + | example | + example := testNodeMultipleChildren firstChild firstChild. + + self assert: example weight equals: 6.84. +] + +{ #category : 'tests' } +PerfTreeParserTest >> testWeightOf [ + + self + assert: + (testParserSimple weightOf: testParserSimple numberOfFunctions) + equals: 146.24. + + self assert: (testParserSimple weightOf: 3) equals: 36.31 +] + +{ #category : 'tests' } +PerfTreeParserTest >> testWithoutSpaces [ + + self assert: (testParserMultipleChildren withoutSpaces at: 6) equals: '--6.86%--arch_do_signal_or_restart' +] diff --git a/src/PerfTreeParser-Tests/package.st b/src/PerfTreeParser-Tests/package.st new file mode 100644 index 0000000..312597f --- /dev/null +++ b/src/PerfTreeParser-Tests/package.st @@ -0,0 +1 @@ +Package { #name : 'PerfTreeParser-Tests' } diff --git a/src/PerfTreeParser/PerfTreeNode.class.st b/src/PerfTreeParser/PerfTreeNode.class.st new file mode 100644 index 0000000..b336edf --- /dev/null +++ b/src/PerfTreeParser/PerfTreeNode.class.st @@ -0,0 +1,125 @@ +Class { + #name : 'PerfTreeNode', + #superclass : 'Object', + #instVars : [ + 'name', + 'weight', + 'children', + 'parent', + 'totalTime' + ], + #category : 'PerfTreeParser', + #package : 'PerfTreeParser' +} + +{ #category : 'accessing' } +PerfTreeNode >> children [ + + ^ children +] + +{ #category : 'accessing' } +PerfTreeNode >> children: aCollectionOfTreeNode [ + + children:= aCollectionOfTreeNode +] + +{ #category : 'accessing' } +PerfTreeNode >> collectLeavesIn: leaves [ + + (self isLeaf and: [ self isNotAlreadyLeafIn: leaves ]) ifTrue: [ + leaves add: self ]. + + self children do: [ :child | child collectLeavesIn: leaves ]. + + ^ leaves +] + +{ #category : 'accessing' } +PerfTreeNode >> firstChild [ + + ^ self children first +] + +{ #category : 'accessing' } +PerfTreeNode >> isLeaf [ + + ^ self children isEmpty +] + +{ #category : 'accessing' } +PerfTreeNode >> isNotAlreadyLeafIn: leaves [ + + (leaves anySatisfy: [ :leaf | + leaf name = self name and: [ leaf weight = self weight ] ]) + ifTrue: [ ^ false ] + ifFalse: [ ^ true ] +] + +{ #category : 'accessing' } +PerfTreeNode >> isNotLeaf [ + + ^ self children isNotEmpty +] + +{ #category : 'accessing' } +PerfTreeNode >> name [ + + ^ name +] + +{ #category : 'accessing' } +PerfTreeNode >> name: aString [ + + name := aString +] + +{ #category : 'accessing' } +PerfTreeNode >> parent [ + + ^ parent +] + +{ #category : 'accessing' } +PerfTreeNode >> parent: aTreeNode [ + + parent := aTreeNode +] + +{ #category : 'accessing' } +PerfTreeNode >> time [ + + ^ totalTime +] + +{ #category : 'accessing' } +PerfTreeNode >> time: aFloat [ + + totalTime := aFloat +] + +{ #category : 'accessing' } +PerfTreeNode >> traces [ + + | leaves | + leaves := OrderedCollection new. + self collectLeavesIn: leaves. + + ^ leaves collect: [ :l | + PerfTreeTrace new + name: l name; + weight: l weight; + yourself ] +] + +{ #category : 'accessing' } +PerfTreeNode >> weight [ + + ^ weight +] + +{ #category : 'accessing' } +PerfTreeNode >> weight: aFloat [ + + weight := aFloat +] diff --git a/src/PerfTreeParser/PerfTreeParser.class.st b/src/PerfTreeParser/PerfTreeParser.class.st new file mode 100644 index 0000000..28f6eec --- /dev/null +++ b/src/PerfTreeParser/PerfTreeParser.class.st @@ -0,0 +1,247 @@ +Class { + #name : 'PerfTreeParser', + #superclass : 'Object', + #instVars : [ + 'content', + 'totalTime' + ], + #category : 'PerfTreeParser', + #package : 'PerfTreeParser' +} + +{ #category : 'instance creation' } +PerfTreeParser class >> findTraces: aFile [ + + ^ (self parseFile: aFile) collect: [:tree | tree traces ] +] + +{ #category : 'instance creation' } +PerfTreeParser class >> fromFile: aFile [ + + ^ self new + content: aFile asFileReference; + yourself +] + +{ #category : 'instance creation' } +PerfTreeParser class >> parseFile: aFile [ + + ^ (self fromFile: aFile) roots +] + +{ #category : 'accessing' } +PerfTreeParser >> content [ + + ^ content +] + +{ #category : 'accessing' } +PerfTreeParser >> content: aFile [ + "Extract the interesting content from the file." + + | fileContents i | + fileContents := aFile contents lines. + i := 1. + + [ (fileContents at: i) beginsWith: '# sample duration' ] whileFalse: [ + i := i + 1 ]. + + self time: ((((fileContents at: i) last: 10) first: 7) copyWithout: Character space) asNumber. + + content := fileContents reject: [ :line | line beginsWith: '#' ]. + content := content select: [ :line | + ((line copyWithout: Character space) copyWithout: $|) + isNotEmpty ] +] + +{ #category : 'accessing' } +PerfTreeParser >> differenceOfSpacesAt: aNodeIndex [ + "Gives the difference of the number of spaces between 2 followed lines." + + ^ (self numberOfSpacesAt: aNodeIndex + 1) + - (self numberOfSpacesAt: aNodeIndex) +] + +{ #category : 'accessing' } +PerfTreeParser >> findChildrenIndexOfIndex: aNodeIndex [ + "Gives an ordered collection of every indexes of the lines where there is a child." + + | lastNodeIndex everyChildren newBranch spaces nodeSpaces | + lastNodeIndex := self numberOfFunctions. + everyChildren := OrderedCollection new. + + (self percentageOf: aNodeIndex) = (self percentageOf: aNodeIndex + 1) + ifTrue: [ + everyChildren add: aNodeIndex + 1. + ^ everyChildren ]. + + newBranch := false. + nodeSpaces := self numberOfSpacesAt: aNodeIndex. + aNodeIndex + 1 to: lastNodeIndex do: [ :i | + spaces := self numberOfSpacesAt: i. + spaces <= nodeSpaces ifTrue: [ newBranch := true ]. + ((spaces - nodeSpaces between: 1 and: 11) and: [ + (self percentageOf: i) ~= (self percentageOf: i - 1) and: [ + newBranch not ] ]) ifTrue: [ everyChildren add: i ] ]. + + ^ everyChildren +] + +{ #category : 'accessing' } +PerfTreeParser >> findChildrenOfIndex: aNodeIndex [ + "Gives the nodes representing every children at the node aNodeIndex." + + | collectionOfChildrenIndexes | + collectionOfChildrenIndexes := self findChildrenIndexOfIndex: aNodeIndex. + ^ collectionOfChildrenIndexes collect: [ :childIndex | + self nodeAtIndex: childIndex ] +] + +{ #category : 'accessing' } +PerfTreeParser >> lastSpaceAt: aLine [ + "Gives the index of the last space or '-' at a given line." + + | lastSpace line | + line := self readLine: aLine. + lastSpace := 0. + + 1 to: line size do: [ :i | + ((line at: i) = Character space or: [ (line at: i) = $- ]) ifTrue: [ + lastSpace := i ] ]. + ^ lastSpace +] + +{ #category : 'accessing' } +PerfTreeParser >> nameOf: aNodeIndex [ + "Gives the name of a function at aNodeIndex." + + | aLine | + aLine := self readLine: aNodeIndex. + ^ aLine last: aLine size - (self lastSpaceAt: aNodeIndex) +] + +{ #category : 'accessing' } +PerfTreeParser >> nodeAtIndex: aNodeIndex [ + "Gives the node representing the node at aNodeIndex." + + | node | + node := PerfTreeNode new + name: (self nameOf: aNodeIndex); + weight: (self weightOf: aNodeIndex); + time: self time; + yourself. + node children: (self findChildrenOfIndex: aNodeIndex). + node children do: [ :child | child parent: node ]. + ^ node +] + +{ #category : 'accessing' } +PerfTreeParser >> numberOfFunctions [ + "Gives the number of functions present in the file." + + ^ content size +] + +{ #category : 'accessing' } +PerfTreeParser >> numberOfSpacesAt: aNodeIndex [ + "Gives the number of spaces at a certain line." + + | count lineRead | + + lineRead := self readLine: aNodeIndex. + count := 1. + + [(lineRead at: count) = Character space or: [ + (lineRead at: count) = $| ] ] whileTrue: [ count := count + 1 ]. + ^ count - 1 +] + +{ #category : 'accessing' } +PerfTreeParser >> percentageOf: aLine [ + "Gives the percentage from a function at a given line" + + "In the case of multiples functions for one percentage, it gives the same percentage to every functions." + + | stringLine numbers | + (aLine between: 1 and: self numberOfFunctions) ifFalse: [ ^ 0 ]. + + stringLine := ((((self readLine: aLine) copyUpTo: $%) last: 5) + copyWithout: $-) copyWithout: Character space. + + stringLine = ((self readLine: aLine) last: 5) ifTrue: [ + ^ self percentageOf: aLine - 1 ]. + + numbers := stringLine asNumber. + + ^ numbers +] + +{ #category : 'accessing' } +PerfTreeParser >> readLine: anIndex [ + "Reads a line from the file without the commentaries." + ^ self content at: anIndex +] + +{ #category : 'accessing' } +PerfTreeParser >> realPercentageOf: aNodeIndex [ + + | nodePercentage children childrenPercentage | + nodePercentage := self percentageOf: aNodeIndex. + children := self findChildrenIndexOfIndex: aNodeIndex. + childrenPercentage := 0. + children ifEmpty: [ ^ nodePercentage ]. + + + childrenPercentage := children sum: [ :child | + self percentageOf: child ]. + + ^ nodePercentage - childrenPercentage roundUpTo: 0.01 +] + +{ #category : 'accessing' } +PerfTreeParser >> root [ + "Gives the first parent of the nodes" + + ^ self nodeAtIndex: 1 +] + +{ #category : 'accessing' } +PerfTreeParser >> roots [ + + | roots | + roots := OrderedCollection new. + + 1 to: self numberOfFunctions do: [:i | + (self numberOfSpacesAt: i) <= 5 ifTrue: [ roots add: (self nodeAtIndex: i) ] ]. + + ^ roots +] + +{ #category : 'accessing' } +PerfTreeParser >> time [ + + ^ totalTime +] + +{ #category : 'accessing' } +PerfTreeParser >> time: aFloat [ + + totalTime := aFloat. +] + +{ #category : 'accessing' } +PerfTreeParser >> weightOf: aNodeIndex [ + + | newPercentage | + newPercentage := self realPercentageOf: aNodeIndex. + + ^ totalTime * (newPercentage/100) roundUpTo: 0.01 +] + +{ #category : 'accessing' } +PerfTreeParser >> withoutSpaces [ + "Gives a line without the characters that are not interesting." + + ^ self content collect: [ :c | + (c copyWithout: Character space) copyWithout: $| ] +] diff --git a/src/PerfTreeParser/PerfTreeTrace.class.st b/src/PerfTreeParser/PerfTreeTrace.class.st new file mode 100644 index 0000000..f369927 --- /dev/null +++ b/src/PerfTreeParser/PerfTreeTrace.class.st @@ -0,0 +1,34 @@ +Class { + #name : 'PerfTreeTrace', + #superclass : 'Object', + #instVars : [ + 'name', + 'weight' + ], + #category : 'PerfTreeParser', + #package : 'PerfTreeParser' +} + +{ #category : 'accessing' } +PerfTreeTrace >> name [ + + ^ name +] + +{ #category : 'accessing' } +PerfTreeTrace >> name: aName [ + + name := aName +] + +{ #category : 'accessing' } +PerfTreeTrace >> weight [ + + ^ weight +] + +{ #category : 'accessing' } +PerfTreeTrace >> weight: aFloat [ + + weight := aFloat +] diff --git a/src/PerfTreeParser/package.st b/src/PerfTreeParser/package.st new file mode 100644 index 0000000..294aff7 --- /dev/null +++ b/src/PerfTreeParser/package.st @@ -0,0 +1 @@ +Package { #name : 'PerfTreeParser' } diff --git a/src/TreeParser-Tests/TreeParserTest.class.st b/src/TreeParser-Tests/TreeParserTest.class.st deleted file mode 100644 index ad2c4ad..0000000 --- a/src/TreeParser-Tests/TreeParserTest.class.st +++ /dev/null @@ -1,205 +0,0 @@ -Class { - #name : #TreeParserTest, - #superclass : #TestCase, - #instVars : [ - 'memoryFS', - 'fileSimple', - 'fileSamePercentage', - 'fileMultipleChildren', - 'treeSimple', - 'treeSamePercentage', - 'treeMultipleChildren', - 'testNodeSimple', - 'testNodeSamePercentage', - 'testNodeMultipleChildren' - ], - #category : #'TreeParser-Tests' -} - -{ #category : #running } -TreeParserTest >> setUp [ - - super setUp. - memoryFS := FileSystem memory. - memoryFS / ('perf_stock_simple.txt') writeStreamDo: [:stream | stream nextPutAll: '# To display the perf.data header info, please use --header/--header-only options. -# -# -# Total Lost Samples: 0 -# -# Samples: 1K of event ''cycles:P'' -# Event count (approx.): 963528052 -# -# Children Self Command Shared Object Symbol -# ........ ........ ............... ......................................... ......................................................... -# - 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe - | - ---entry_SYSCALL_64_after_hwframe - | - --45.91%--do_syscall_64 - | - |--36.78%--__x64_sys_read -']. - - memoryFS / ('perf_stock_same_percentage.txt') writeStreamDo: [:stream | stream nextPutAll: '# To display the perf.data header info, please use --header/--header-only options. -# -# -# Total Lost Samples: 0 -# -# Samples: 1K of event ''cycles:P'' -# Event count (approx.): 963528052 -# -# Children Self Command Shared Object Symbol -# ........ ........ ............... ......................................... ......................................................... -# - 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe - | - ---entry_SYSCALL_64_after_hwframe - | - --45.91%--do_syscall_64 - | - |--36.78%--__x64_sys_read - | ksys_read - | vfs_read - | | - | --36.46%--ext4_file_read_iter - | | -']. - - memoryFS / ('perf_stock_multiple_children.txt') writeStreamDo: [:stream | stream nextPutAll: '# To display the perf.data header info, please use --header/--header-only options. -# -# -# Total Lost Samples: 0 -# -# Samples: 1K of event ''cycles:P'' -# Event count (approx.): 963528052 -# -# Children Self Command Shared Object Symbol -# ........ ........ ............... ......................................... ......................................................... -# - 46.02% 0.00% pharo [kernel.kallsyms] [k] entry_SYSCALL_64_after_hwframe - | - ---entry_SYSCALL_64_after_hwframe - | - --45.91%--do_syscall_64 - | - |--36.78%--__x64_sys_read - | - | - |--6.88%--syscall_exit_to_user_mode - | | - | --6.86%--arch_do_signal_or_restart - | | - | --6.86%--get_signal - | - --0.53%--__x64_sys_openat - do_sys_openat2 -']. - - fileSimple := memoryFS / ('perf_stock_simple.txt'). - treeSimple := TreeParse fromFile: fileSimple. - testNodeSimple := treeSimple asNodes. - - fileSamePercentage := memoryFS / ('perf_stock_same_percentage.txt'). - treeSamePercentage := TreeParse fromFile: fileSamePercentage. - testNodeSamePercentage := treeSamePercentage asNodes. - - fileMultipleChildren := memoryFS / ('perf_stock_multiple_children.txt'). - treeMultipleChildren := TreeParse fromFile: fileMultipleChildren. - testNodeMultipleChildren := treeMultipleChildren asNodes. - -] - -{ #category : #running } -TreeParserTest >> tearDown [ - - fileSimple ensureDelete. - fileSamePercentage ensureDelete. - fileMultipleChildren ensureDelete. - super tearDown -] - -{ #category : #tests } -TreeParserTest >> testClassify [ - - | family example | - family := testNodeMultipleChildren classify. - example := (family firstChildrenOf: 2) children. - - self assert: example size equals: 3. - - self assert: example first name equals: '__x64_sys_read'. - - self assert: example second name equals: 'syscall_exit_to_user_mode'. - - self assert: example third name equals: '__x64_sys_openat'. - - self assert: example first parent equals: example second parent -] - -{ #category : #tests } -TreeParserTest >> testEstimatePercentage [ - - | family example | - family := testNodeSamePercentage classify. - example := family firstChildrenOf: 2. - - self assert: example estimatePercentage equals: 9.13. - - self assert: example children first estimatePercentage equals: 0.0 -] - -{ #category : #tests } -TreeParserTest >> testFindChildrenOf [ - - self assert: - (testNodeMultipleChildren findChildrenOf: 3) first equals: 4. - - self assert: - (testNodeMultipleChildren findChildrenOf: 3) second equals: 5. - - self assert: - (testNodeMultipleChildren findChildrenOf: 3) third equals: 8 -] - -{ #category : #tests } -TreeParserTest >> testFindParentOf [ - - self assert: (testNodeMultipleChildren findParentOf: 4) equals: 3. - - self assert: (testNodeMultipleChildren findParentOf: 6) equals: 5 -] - -{ #category : #tests } -TreeParserTest >> testNameOf [ - - self assert: (testNodeSimple nameOf: 1) equals: 'entry_SYSCALL_64_after_hwframe'. - - self assert: (testNodeSamePercentage nameOf: 4) equals: '__x64_sys_read'. - - self assert: (testNodeSamePercentage nameOf: 6) equals: 'vfs_read' -] - -{ #category : #tests } -TreeParserTest >> testNumberOfFunctions [ - - self assert: (testNodeSimple numberOfFunctions) equals: 4 -] - -{ #category : #tests } -TreeParserTest >> testNumberOfSpaces [ - - self assert: (testNodeSimple numberOfSpacesAt: 1) equals: 4 -] - -{ #category : #tests } -TreeParserTest >> testReadALine [ - - self assert: (treeSimple readLine: 3) equals: ' ---entry_SYSCALL_64_after_hwframe' -] - -{ #category : #tests } -TreeParserTest >> testSimplePercentageOf [ - - self assert: (testNodeSimple percentageOf: 3) equals: 45.91 -] diff --git a/src/TreeParser-Tests/package.st b/src/TreeParser-Tests/package.st deleted file mode 100644 index 54eece2..0000000 --- a/src/TreeParser-Tests/package.st +++ /dev/null @@ -1 +0,0 @@ -Package { #name : #'TreeParser-Tests' } diff --git a/src/TreeParser/TreeChildren.class.st b/src/TreeParser/TreeChildren.class.st deleted file mode 100644 index 797575c..0000000 --- a/src/TreeParser/TreeChildren.class.st +++ /dev/null @@ -1,82 +0,0 @@ -Class { - #name : #TreeChildren, - #superclass : #Object, - #instVars : [ - 'parent', - 'children', - 'name', - 'percentage' - ], - #category : #TreeParser -} - -{ #category : #accessing } -TreeChildren >> children [ - - ^ children -] - -{ #category : #accessing } -TreeChildren >> children: aTreeChildren [ - - children := aTreeChildren. -] - -{ #category : #accessing } -TreeChildren >> estimatePercentage [ - - | sumOfPercents | - sumOfPercents := 0. - self children collect: [ :n | - sumOfPercents := n percentage + sumOfPercents ]. - - percentage - sumOfPercents >= 0 - ifTrue: [ percentage := (percentage - sumOfPercents) roundUpTo: 0.01 ]. - ^ percentage -] - -{ #category : #accessing } -TreeChildren >> firstChildrenOf: aNumber [ - "Gives the children of the first children of each generations a given times." - - | child | - child := self. - 1 to: aNumber do: [:c | child := child children first ]. - ^ child -] - -{ #category : #accessing } -TreeChildren >> name [ - - ^ name -] - -{ #category : #accessing } -TreeChildren >> name: aName [ - - name := aName -] - -{ #category : #accessing } -TreeChildren >> parent [ - - ^ parent -] - -{ #category : #accessing } -TreeChildren >> parent: aTreeNode [ - - parent := aTreeNode -] - -{ #category : #accessing } -TreeChildren >> percentage [ - - ^ percentage -] - -{ #category : #accessing } -TreeChildren >> percentage: aFloat [ - - percentage := aFloat -] diff --git a/src/TreeParser/TreeNodes.class.st b/src/TreeParser/TreeNodes.class.st deleted file mode 100644 index 696a389..0000000 --- a/src/TreeParser/TreeNodes.class.st +++ /dev/null @@ -1,169 +0,0 @@ -Class { - #name : #TreeNodes, - #superclass : #Object, - #instVars : [ - 'nodes' - ], - #category : #TreeParser -} - -{ #category : #accessing } -TreeNodes >> classify [ - "Establish every parent/child links given by the file." - - ^ (self classify: 1 withParent: nil) -] - -{ #category : #accessing } -TreeNodes >> classify: aLine withParent: aParent [ - "Establish the parent/child links, starting from a given line and optionally a parent." - - | child | - child := TreeChildren new - name: (self nameOf: aLine); - percentage: (self percentageOf: aLine); - parent: aParent. - child children: ((self findChildrenOf: aLine) collect: [ :c | - self classify: c withParent: child ]). - ^ child -] - -{ #category : #accessing } -TreeNodes >> differenceOfSpacesAt: aNode [ - "Gives the difference of the number of spaces between 2 followed lines." - - self assert: aNode < self numberOfFunctions. - ^ (self numberOfSpacesAt: aNode + 1) - (self numberOfSpacesAt: aNode) -] - -{ #category : #accessing } -TreeNodes >> findChildrenOf: aNode [ - "Gives an ordered collection of every indexes of the lines where there is a child." - - | lastNode everyChildren | - lastNode := self numberOfFunctions. - everyChildren := OrderedCollection new. - - aNode = lastNode ifTrue: [ ^ everyChildren ]. - - ((self differenceOfSpacesAt: aNode) <= 10 and: (self differenceOfSpacesAt: aNode) >= 0) - ifTrue: [ everyChildren add: aNode + 1. - ^ everyChildren ]. - - aNode to: lastNode do: [ :i | - (self numberOfSpacesAt: i) - (self numberOfSpacesAt: aNode) = 11 - ifTrue: [ everyChildren add: i ] ]. - - ^ everyChildren -] - -{ #category : #accessing } -TreeNodes >> findParentOf: aNode [ - "Gives the index of the line where the parent is." - - | i | - i := aNode-1. - [ i > 1 and: [ ( (self numberOfSpacesAt: aNode) - (self numberOfSpacesAt: i) ) < 0 ] ] - whileTrue: [ i := i - 1 ]. - - (i > 0) - ifTrue: [ ^ i ] - ifFalse: [ ^ nil ] -] - -{ #category : #accessing } -TreeNodes >> lastNodeOf: aNode [ - "Give the index of the last node starting from a given node." - - | lastNode | - lastNode := self numberOfFunctions. - aNode to: lastNode - 1 do: [ :i | - (self differenceOfSpacesAt: i) < 0 ifTrue: [ ^ i ] ]. - ^ aNode -] - -{ #category : #accessing } -TreeNodes >> lastSpaceAt: aLine [ - "Gives the index of the last space at a given line." - - | lastSpace line| - line := nodes at: aLine. - lastSpace := line size. - - 1 to: (line size) do: [:i | ( ( (line at: i) = Character space) or: ( (line at: i) = $-) ) - ifTrue: [ lastSpace := line size - i ] ]. - ^ lastSpace -] - -{ #category : #accessing } -TreeNodes >> nameOf: aLine [ - "Gets the name of a function at a given line." - - | line | - line := (self withoutSpaces at: aLine) copyWithout: $-. - ^ line last: (self lastSpaceAt: aLine) -] - -{ #category : #accessing } -TreeNodes >> nodes [ - - ^ nodes -] - -{ #category : #accessing } -TreeNodes >> nodes: aContent [ - - nodes := aContent select: [ :c | - ((c copyWithout: Character space) copyWithout: $|) - isNotEmpty ] -] - -{ #category : #accessing } -TreeNodes >> numberOfFunctions [ - "Gives the number of non-unique functions mentioned in the file." - - ^ nodes size -] - -{ #category : #accessing } -TreeNodes >> numberOfSpacesAt: aNode [ - "Gives the number of spaces at a certain line." - - | count spacesAreEnded | - count := 0. - spacesAreEnded := false. - (self nodes at: aNode) do: [ :c | - (c ~= Character space and: c ~= $|) - ifFalse: [ spacesAreEnded ifFalse: [ count := count + 1 ] ] - ifTrue: [ spacesAreEnded := true ] ]. - ^ count -] - -{ #category : #accessing } -TreeNodes >> percentageOf: aLine [ - "Gives the percentage from a function at a given line" - - "In the case of multiples functions for one percentage, it gives the same percentage to every functions." - - | line numbers | - self assert: aLine >= 1. - line := self nodes at: aLine. - - numbers := (((self withoutSpaces at: aLine) copyWithout: $-) first: 5) - select: [ :c | - { $0. $1. $2. $3. $4. $5. $6. $7. $8. $9 } asSet - includes: c ]. - (aLine > 1 and: numbers isEmpty) ifTrue: [ - ^ self percentageOf: aLine - 1 ]. - numbers size >= 4 - ifTrue: [ ^ ((numbers first: 4) asInteger / 100) asFloat ] - ifFalse: [ ^ ((numbers first: 3) asInteger / 100) asFloat ] -] - -{ #category : #accessing } -TreeNodes >> withoutSpaces [ - "Gives a line without the characters that are not interesting." - - ^ self nodes collect: [ :n | - (n copyWithout: Character space) copyWithout: $| ] -] diff --git a/src/TreeParser/TreeParse.class.st b/src/TreeParser/TreeParse.class.st deleted file mode 100644 index 024306a..0000000 --- a/src/TreeParser/TreeParse.class.st +++ /dev/null @@ -1,34 +0,0 @@ -Class { - #name : #TreeParse, - #superclass : #Object, - #instVars : [ - 'content' - ], - #category : #TreeParser -} - -{ #category : #'instance creation' } -TreeParse class >> fromFile: aFile [ - self assert: aFile class = FileReference. - ^ self new - content: aFile. -] - -{ #category : #accessing } -TreeParse >> asNodes [ - - ^ TreeNodes new nodes: content -] - -{ #category : #accessing } -TreeParse >> content: aFile [ - - content := aFile contents lines reject: [ :line | - line beginsWith: '#' ] -] - -{ #category : #accessing } -TreeParse >> readLine: pos [ - - ^ content at: pos -] diff --git a/src/TreeParser/package.st b/src/TreeParser/package.st deleted file mode 100644 index 9748a44..0000000 --- a/src/TreeParser/package.st +++ /dev/null @@ -1 +0,0 @@ -Package { #name : #TreeParser }