pax_global_header00006660000000000000000000000064144542003310014506gustar00rootroot0000000000000052 comment=ccc532618599949f271aa260255eeca406a12c60 pcm-202307/000077500000000000000000000000001445420033100123645ustar00rootroot00000000000000pcm-202307/.bdsignore.all000066400000000000000000000006071445420033100151130ustar00rootroot00000000000000.bdsignore.all Makefile Doxyfile LICENSE README ChangeLog TODO Debug Release Release64 Backup Intel_SSA .vs My Inspector XE Results - pcm .gitignore .gitattributes .github .*\.txt .*\.pdf .*\.docx .*\.rtf .*\.vcproj .*\.xcodeproj .*\.sh .*\.htm .*\.bat .*\.strings .*\.vcxproj .*\.sln .*\.sdf .*\.vcxproj.user .*\.log .*\.vcxproj.filters .*\.md .codedocs .cproject .project makefile sources pcm-202307/.codedocs000066400000000000000000000115261445420033100141550ustar00rootroot00000000000000# CodeDocs.xyz Configuration File # # Rename this example to '.codedocs' and put it in the root directory of your # repository. This file is optional, documentation will still be generated # without it using sensible defaults. #--------------------------------------------------------------------------- # CodeDocs Configuration #--------------------------------------------------------------------------- # Include the Doxygen configuration from another file. # The file must be a relative path with respect to the root of the repository. # If any of the options in this doxyfile include a path (ie, INPUT), these # paths will be considered relative to the root of the repository, not the # location of the DOXYFILE. DOXYFILE = Doxyfile INPUT += README.md USE_MDFILE_AS_MAINPAGE = README.md # Specify external repository to link documentation with. # This is similar to Doxygen's TAGFILES option, but will automatically link to # tags of other repositories already using CodeDocs. List each repository to # link with by giving its location in the form of owner/repository. # For example: # TAGLINKS = doxygen/doxygen CodeDocs/osg # Note: these repositories must already be built on CodeDocs. # TAGLINKS = #--------------------------------------------------------------------------- # Doxygen Configuration #--------------------------------------------------------------------------- # Doxygen configuration may also be placed in this file. # Currently, the following Doxygen configuration options are available. Refer # to http://doxygen.org/manual/config.html for detailed explanation of the # options. To request support for more options, contact support@codedocs.xyz. # # ABBREVIATE_BRIEF = # ALIASES = # ALLEXTERNALS = # ALLOW_UNICODE_NAMES = # ALPHABETICAL_INDEX = # ALWAYS_DETAILED_SEC = # AUTOLINK_SUPPORT = # BRIEF_MEMBER_DESC = # BUILTIN_STL_SUPPORT = # CALLER_GRAPH = # CALL_GRAPH = # CASE_SENSE_NAMES = # CITE_BIB_FILES = # CLASS_DIAGRAMS = # CLASS_GRAPH = # COLLABORATION_GRAPH = # COLS_IN_ALPHA_INDEX = # CPP_CLI_SUPPORT = # DIAFILE_DIRS = # DIRECTORY_GRAPH = # DISABLE_INDEX = # DISTRIBUTE_GROUP_DOC = # DOTFILE_DIRS = # DOT_FONTNAME = # DOT_FONTSIZE = # DOT_GRAPH_MAX_NODES = # DOT_IMAGE_FORMAT = # DOT_TRANSPARENT = # DOXYFILE_ENCODING = # ENABLED_SECTIONS = # ENABLE_PREPROCESSING = # ENUM_VALUES_PER_LINE = # EXAMPLE_PATH = # EXAMPLE_PATTERNS = # EXAMPLE_RECURSIVE = # EXCLUDE = # EXCLUDE_PATTERNS = # EXCLUDE_SYMBOLS = # EXPAND_AS_DEFINED = # EXPAND_ONLY_PREDEF = # EXTENSION_MAPPING = # EXTERNAL_GROUPS = # EXTERNAL_PAGES = # EXTRACT_ALL = # EXTRACT_ANON_NSPACES = # EXTRACT_LOCAL_CLASSES = # EXTRACT_LOCAL_METHODS = # EXTRACT_PACKAGE = # EXTRACT_PRIVATE = # EXTRACT_STATIC = # EXT_LINKS_IN_WINDOW = # FILE_PATTERNS = # FORCE_LOCAL_INCLUDES = # FORMULA_FONTSIZE = # FORMULA_TRANSPARENT = # FULL_PATH_NAMES = # GENERATE_BUGLIST = # GENERATE_DEPRECATEDLIST = # GENERATE_LEGEND = # GENERATE_TESTLIST = # GENERATE_TODOLIST = # GENERATE_TREEVIEW = # GRAPHICAL_HIERARCHY = # GROUP_GRAPHS = # GROUP_NESTED_COMPOUNDS = # HIDE_COMPOUND_REFERENCE= = # HIDE_FRIEND_COMPOUNDS = # HIDE_IN_BODY_DOCS = # HIDE_SCOPE_NAMES = # HIDE_UNDOC_CLASSES = # HIDE_UNDOC_MEMBERS = # HIDE_UNDOC_RELATIONS = # HTML_COLORSTYLE_GAMMA = # HTML_COLORSTYLE_HUE = # HTML_COLORSTYLE_SAT = # HTML_DYNAMIC_SECTIONS = # HTML_EXTRA_FILES = # HTML_EXTRA_STYLESHEET = # HTML_FOOTER = # HTML_HEADER = # HTML_INDEX_NUM_ENTRIES = # HTML_STYLESHEET = # HTML_TIMESTAMP = # IDL_PROPERTY_SUPPORT = # IGNORE_PREFIX = # IMAGE_PATH = # INCLUDED_BY_GRAPH = # INCLUDE_FILE_PATTERNS = # INCLUDE_GRAPH = # INCLUDE_PATH = # INHERIT_DOCS = # INLINE_GROUPED_CLASSES = # INLINE_INFO = # INLINE_INHERITED_MEMB = # INLINE_SIMPLE_STRUCTS = # INLINE_SOURCES = # INPUT = # INPUT_ENCODING = # INTERACTIVE_SVG = # INTERNAL_DOCS = # JAVADOC_AUTOBRIEF = # LAYOUT_FILE = # MACRO_EXPANSION = # MARKDOWN_SUPPORT = # MAX_DOT_GRAPH_DEPTH = # MSCFILE_DIRS = # MULTILINE_CPP_IS_BRIEF = # OPTIMIZE_FOR_FORTRAN = # OPTIMIZE_OUTPUT_FOR_C = # OPTIMIZE_OUTPUT_JAVA = # OPTIMIZE_OUTPUT_VHDL = # OUTPUT_LANGUAGE = # PLANTUML_JAR_PATH = # PREDEFINED = # PROJECT_BRIEF = # PROJECT_LOGO = # PROJECT_NAME = # PROJECT_NUMBER = # QT_AUTOBRIEF = # RECURSIVE = # REFERENCED_BY_RELATION = # REFERENCES_LINK_SOURCE = # REFERENCES_RELATION = # REPEAT_BRIEF = # SEARCHENGINE = # SEARCH_INCLUDES = # SEPARATE_MEMBER_PAGES = # SHORT_NAMES = # SHOW_FILES = # SHOW_GROUPED_MEMB_INC = # SHOW_INCLUDE_FILES = # SHOW_NAMESPACES = # SHOW_USED_FILES = # SIP_SUPPORT = # SKIP_FUNCTION_MACROS = # SORT_BRIEF_DOCS = # SORT_BY_SCOPE_NAME = # SORT_GROUP_NAMES = # SORT_MEMBERS_CTORS_1ST = # SORT_MEMBER_DOCS = # SOURCE_BROWSER = # SOURCE_TOOLTIPS = # STRICT_PROTO_MATCHING = # STRIP_CODE_COMMENTS = # STRIP_FROM_INC_PATH = # STRIP_FROM_PATH = # SUBGROUPING = # TAB_SIZE = # TEMPLATE_RELATIONS = # TREEVIEW_WIDTH = # TYPEDEF_HIDES_STRUCT = # UML_LIMIT_NUM_FIELDS = # UML_LOOK = # USE_MDFILE_AS_MAINPAGE = # VERBATIM_HEADERS = # pcm-202307/.gitattributes000066400000000000000000000001711445420033100152560ustar00rootroot00000000000000src/version.h export-subst .cirrus.yml export-ignore .gitlab-ci.yml export-ignore .travis.yml export-ignore * text=true pcm-202307/.github/000077500000000000000000000000001445420033100137245ustar00rootroot00000000000000pcm-202307/.github/workflows/000077500000000000000000000000001445420033100157615ustar00rootroot00000000000000pcm-202307/.github/workflows/ci-clang-scan.yml000066400000000000000000000011031445420033100210760ustar00rootroot00000000000000name: clang-scan on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-clang-scan if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build scan-build cmake -B ${{ github.workspace }}/build - name: Scan build run: | cd ${{ github.workspace }}/build scan-build --exclude src/simdjson --status-bugs make -j pcm-202307/.github/workflows/ci-cmake-options.yml000066400000000000000000000074161445420033100216560ustar00rootroot00000000000000name: test cmake options on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-gcc10 if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: CMake default install run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake Release install run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=Release cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake Debug install run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=Debug cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake RelWithDebInfo install run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=RelWithDebInfo cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake Custom build install run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DCMAKE_CXX_FLAGS_CUSTOM:STRING="-O2 -g" -DCMAKE_BUILD_TYPE=CUSTOM cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake Include systemd unit run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DLINUX_SYSTEMD=TRUE -DLINUX_SYSTEMD_UNITDIR=${{ github.workspace }}/build/systemd cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake User-flags build install run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DCMAKE_CXX_FLAGS="-O2 -g -fPIC" cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake env var option build install run: | cmake --version rm -rf ${{ github.workspace }}/build export CXXFLAGS="-grecord-gcc-switches" export CFLAGS="-fstack-protector-strong" export LDFLAGS="-Wl,-z,now" cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DCMAKE_CXX_FLAGS_CUSTOM:STRING="-O2 -g -fPIC" -DCMAKE_BUILD_TYPE=CUSTOM cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: CMake env var option build install (no custom flags) run: | cmake --version rm -rf ${{ github.workspace }}/build export CXXFLAGS="-grecord-gcc-switches" export CFLAGS="-fstack-protector-strong" export LDFLAGS="-Wl,-z,now" cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=CUSTOM cd ${{ github.workspace }}/build export VERBOSE=1 make install -j$(nproc) - name: Diagnostic run: date pcm-202307/.github/workflows/ci-cov-linux-report.yml000066400000000000000000000013311445420033100223300ustar00rootroot00000000000000name: coverity-linux-and-python-report on: [workflow_dispatch, workflow_call] jobs: build: runs-on: ci-kw-linux if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: coverity-python run: | ci-cov-python.sh - name: coverity-linux run: | mkdir build cd build cmake .. ci-cov-linux.sh - name: coverity-linux-and-python-report run: | ci-cov-linux-report.sh PCM.linux.and.python - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: coverity-linux-and-python-report-${{ github.sha }} path: "*-Report.pdf"pcm-202307/.github/workflows/ci-cov-linux.yml000066400000000000000000000006061445420033100210230ustar00rootroot00000000000000name: coverity-linux on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-kw-linux if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: coverity-linux run: | mkdir build cd build cmake .. ci-cov-linux.sh pcm-202307/.github/workflows/ci-cov-python.yml000066400000000000000000000005231445420033100212030ustar00rootroot00000000000000name: coverity-python on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-kw-linux if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: coverity-python run: | ci-cov-python.sh pcm-202307/.github/workflows/ci-cov-report.yml000066400000000000000000000004441445420033100211770ustar00rootroot00000000000000name: coverity-report on: workflow_dispatch jobs: linux-report: uses: intel-innersource/applications.analyzers.pcm/.github/workflows/ci-cov-linux-report.yml@main windows-report: uses: intel-innersource/applications.analyzers.pcm/.github/workflows/ci-cov-windows-report.yml@main pcm-202307/.github/workflows/ci-cov-windows-report.yml000066400000000000000000000017651445420033100226760ustar00rootroot00000000000000name: coverity-windows on: [workflow_dispatch, workflow_call] jobs: build: runs-on: ci-kw-windows if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1.1.3 - name: coverity-windows run: | mkdir build cd build cmake .. c:\pcm\ci-cov-windows.ps1 - name: coverity-windows-cs run: | mkdir build-cs cd build-cs cmake .. c:\pcm\ci-cov-windows-cs.ps1 - name: coverity-windows-msr run: | chdir ${{github.workspace}}\src\WinMSRDriver c:\pcm\ci-cov-windows-msr.ps1 - name: coverity-windows-report run: | c:\pcm\ci-cov-windows-report.ps1 PCM.windows-all - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: coverity-windows-all-report-${{ github.sha }} path: "*-Report.pdf" pcm-202307/.github/workflows/ci-cov-windows.yml000066400000000000000000000014001445420033100213470ustar00rootroot00000000000000name: coverity-windows on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-kw-windows if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1.1.3 - name: coverity-windows run: | mkdir build cd build cmake .. c:\pcm\ci-cov-windows.ps1 - name: coverity-windows-cs run: | mkdir build-cs cd build-cs cmake .. c:\pcm\ci-cov-windows-cs.ps1 - name: coverity-windows-msr run: | chdir ${{github.workspace}}\src\WinMSRDriver c:\pcm\ci-cov-windows-msr.ps1 pcm-202307/.github/workflows/ci-cpack.yml000066400000000000000000000024131445420033100201560ustar00rootroot00000000000000name: CPack check on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: job-build1: runs-on: ci-gcc9 if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build - name: Build and Install run: | g++ --version cd ${{ github.workspace }}/build make -j$(nproc) - name: CPack run: | cd ${{ github.workspace }}/build cpack job-build2: runs-on: ci-test if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build - name: Build and Install run: | g++ --version cd ${{ github.workspace }}/build make -j$(nproc) - name: CPack run: | cd ${{ github.workspace }}/build cpack pcm-202307/.github/workflows/ci-cppcheck.yml000066400000000000000000000006201445420033100206530ustar00rootroot00000000000000name: cppcheck on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-cppcheck if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: cppcheck_script run: | sh ${{ github.workspace }}/scripts/cppcheck.sh . `getconf _NPROCESSORS_ONLN` pcm-202307/.github/workflows/ci-gcc10.yml000066400000000000000000000012151445420033100177710ustar00rootroot00000000000000name: g++ 10 build on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-gcc10 if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build - name: Build and Install run: | g++ --version cd ${{ github.workspace }}/build make install -j$(nproc) - name: Diagnostic run: datepcm-202307/.github/workflows/ci-gcc48.yml000066400000000000000000000015201445420033100200030ustar00rootroot00000000000000name: g++ 4.8 build on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-gcc48 if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 - name: install simdjson uses: actions/checkout@v3 with: repository: simdjson/simdjson path: src/simdjson - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build && mkdir ${{ github.workspace }}/build cd ${{ github.workspace }}/build cmake -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build .. - name: Build run: | g++ --version cd ${{ github.workspace }}/build make -j$(nproc) - name: Install run: | cd ${{ github.workspace }}/build make install -j$(nproc) pcm-202307/.github/workflows/ci-gcc5.yml000066400000000000000000000017301445420033100177170ustar00rootroot00000000000000name: g++ 5 build on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-gcc5 if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 - name: install simdjson uses: actions/checkout@v3 with: repository: simdjson/simdjson path: src/simdjson - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build && mkdir ${{ github.workspace }}/build cd ${{ github.workspace }}/build cmake -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build .. - name: Build run: | g++ --version cd ${{ github.workspace }}/build make -j$(nproc) - name: Install run: | cd ${{ github.workspace }}/build make install -j$(nproc) - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: PCMforLinuxGCC5 path: build/bin/* pcm-202307/.github/workflows/ci-gcc7.yml000066400000000000000000000015631445420033100177250ustar00rootroot00000000000000name: g++ 7 build on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-gcc7 if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 - name: install simdjson uses: actions/checkout@v3 with: repository: simdjson/simdjson path: src/simdjson - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build && mkdir ${{ github.workspace }}/build cd ${{ github.workspace }}/build cmake -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build .. - name: Build run: | g++ --version cd ${{ github.workspace }}/build make -j$(nproc) - name: Install run: | cd ${{ github.workspace }}/build make install -j$(nproc) - name: Diagnostic run: datepcm-202307/.github/workflows/ci-gcc9.yml000066400000000000000000000011451445420033100177230ustar00rootroot00000000000000name: g++ 9 build on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-gcc9 if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build - name: Build and Install run: | g++ --version cd ${{ github.workspace }}/build make install -j$(nproc) pcm-202307/.github/workflows/ci-test.yml000066400000000000000000000043121445420033100200540ustar00rootroot00000000000000name: tests on: push: branches: [ '**' ] pull_request: branches: [ '**' ] jobs: build: runs-on: ci-test if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Configure CMake run: | cmake --version rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build - name: Build run: | g++ --version cd ${{ github.workspace }}/build make -j$(nproc) - name: Test run: | set -o pipefail sh ${{ github.workspace }}/tests/test.sh 2>&1 | tee test-log.txt - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: test-log-${{ github.sha }} path: test-log.txt - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: test-log-raw-tr-wo_ext-${{ github.sha }} path: build/bin/raw_tr_wo_ext.csv - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: test-log-raw-tr-wi_ext-${{ github.sha }} path: build/bin/raw_tr_wi_ext.csv - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: pcm-csv-${{ github.sha }} path: build/bin/pcm.csv - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: pcm-memory-csv-${{ github.sha }} path: build/bin/pcm-memory.csv - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: test-log-raw-tr-wi_ext-single_header-${{ github.sha }} path: build/bin/raw_tr_wi_ext_single_header.csv - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: test-log-raw-edp-${{ github.sha }} path: build/bin/raw_edp.txt - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: test-log-raw-json-${{ github.sha }} path: build/bin/raw_json.json - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: test-log-raw-edp-offlined-cores-${{ github.sha }} path: build/bin/raw_edp_offlined_cores.txt pcm-202307/.github/workflows/ci-windows-ip.yml000066400000000000000000000003301445420033100211710ustar00rootroot00000000000000name: windows-ip on: [workflow_dispatch, workflow_call] jobs: build: runs-on: ci-windows if: ${{ github.repository != 'intel/pcm' }} steps: - name: windows-ip run: | ipconfig pcm-202307/.github/workflows/ci-windows.yml000066400000000000000000000017571445420033100206010ustar00rootroot00000000000000name: MSVC Windows build on: push: branches: [ '**' ] pull_request: branches: [ '**' ] env: BUILD_TYPE: Release jobs: build: runs-on: ci-windows if: ${{ github.repository != 'intel/pcm' }} steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Configure CMake run: | if (Test-Path ${{github.workspace}}\build){ Remove-Item ${{github.workspace}}\build -Recurse } cmake -B ${{github.workspace}}\build - name: Build run: | cmake --build ${{github.workspace}}\build --config ${{env.BUILD_TYPE}} --parallel - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1.1.3 - name: Build MSR driver run: | chdir ${{github.workspace}}\src\WinMSRDriver msbuild MSR.vcxproj /p:Configuration=Release,Platform=x64 /t:Clean,Build /m - name: upload-artifact uses: actions/upload-artifact@v3.1.1 with: name: PCMforWindows path: build/bin/**/* pcm-202307/.github/workflows/clang_scan.yml000066400000000000000000000026121445420033100205750ustar00rootroot00000000000000# This is a basic workflow to help you get started with Actions name: clang_scan # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the master branch push: branches: [ master ] pull_request: branches: [ master ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on runs-on: ubuntu-20.04 if: ${{ github.repository == 'intel/pcm' }} # 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@v3 with: submodules: recursive # Runs a set of commands using the runners shell - name: preparation run: | sudo apt-get update -qq sudo apt-get install -qq -y make clang clang-tools perl g++ cmake - name: cmake run: | rm -rf ${{ github.workspace }}/build scan-build cmake -B ${{ github.workspace }}/build - name: scan-build run: | cd ${{ github.workspace }}/build scan-build --exclude src/simdjson --status-bugs make -j pcm-202307/.github/workflows/cppcheck.yml000066400000000000000000000021541445420033100202660ustar00rootroot00000000000000# This is a basic workflow to help you get started with Actions name: cppcheck # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the master branch push: branches: [ master ] pull_request: branches: [ master ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on runs-on: ubuntu-20.04 # 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@v3 with: submodules: recursive # Runs a set of commands using the runners shell - name: cppcheck run: | sudo apt-get update -qq sudo apt-get install -qq -y cppcheck sh ${{ github.workspace }}/scripts/cppcheck.sh . `getconf _NPROCESSORS_ONLN` pcm-202307/.github/workflows/docker.yml000066400000000000000000000030121445420033100177470ustar00rootroot00000000000000name: Docker Build on: push: branches: - master jobs: build: runs-on: ubuntu-20.04 steps: - name: Get current time uses: 1466587594/get-current-time@v2 id: current-time with: format: YYYY-MM-DD--HH - name: Checkout code uses: actions/checkout@v3 with: submodules: recursive - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Cache Docker layers uses: actions/cache@v2 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx- - name: Login to DockerHub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Log in to the Container registry uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v2 with: platforms: linux/amd64 push: true tags: | ghcr.io/intel/pcm:latest opcm/pcm:latest cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache pcm-202307/.github/workflows/freebsd_build.yml000066400000000000000000000011441445420033100212750ustar00rootroot00000000000000name: FreeBSD build on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: macos-12 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: build in FreeBSD VM id: build uses: vmactions/freebsd-vm@v0 with: usesh: true sync: sshfs prepare: | pkg install -y curl gmake cmake run: | pwd ls -lah whoami env freebsd-version cmake -B build -DCMAKE_INSTALL_PREFIX=. cd build && gmake install pcm-202307/.github/workflows/freebsd_scan_build.yml000066400000000000000000000012731445420033100223040ustar00rootroot00000000000000name: FreeBSD clang-scan build on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: macos-12 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: clang scan build in FreeBSD VM id: clang-scan-build uses: vmactions/freebsd-vm@v0 with: usesh: true prepare: | pkg install -y curl gmake cmake devel/llvm llvm run: | pwd ls -lah whoami env freebsd-version scan-build cmake -B build -DCMAKE_INSTALL_PREFIX=. cd build scan-build --exclude src/simdjson --status-bugs gmake pcm-202307/.github/workflows/linux_make.yml000066400000000000000000000034371445420033100206470ustar00rootroot00000000000000name: Linux make on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - name: cmake run: | rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} - name: make run: | cd ${{ github.workspace }}/build make install -j build-systemd-unit: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 - name: cmake run: | rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} -DLINUX_SYSTEMD=TRUE -DLINUX_SYSTEMD_UNITDIR=${{ github.workspace }}/build/systemd - name: make run: | cd ${{ github.workspace }}/build make install -j build-system-simdjson: runs-on: ubuntu-20.04 container: ubuntu:22.04 steps: - uses: actions/checkout@v3 - name: install dependencies run: | apt update apt -y --no-install-recommends install build-essential cmake libsimdjson-dev - name: cmake run: | rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} - name: make run: | cd ${{ github.workspace }}/build make install -j build-source-simdjson: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: cmake run: | rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} - name: make run: | cd ${{ github.workspace }}/build make install -j pcm-202307/.github/workflows/macos-scan-build.yml000066400000000000000000000011341445420033100216240ustar00rootroot00000000000000name: Mac OS X scan-build on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: macos-12 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: cmake run: | rm -rf ${{ github.workspace }}/build $(brew --prefix llvm@15)/bin/scan-build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} - name: make run: | cd ${{ github.workspace }}/build $(brew --prefix llvm@15)/bin/scan-build --exclude src/simdjson --status-bugs make -j pcm-202307/.github/workflows/macosx_build.yml000066400000000000000000000007541445420033100211630ustar00rootroot00000000000000name: Mac OS X build on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: macos-12 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: cmake run: | rm -rf ${{ github.workspace }}/build cmake -B ${{ github.workspace }}/build -DCMAKE_INSTALL_PREFIX=${{ github.workspace }} - name: make run: | cd ${{ github.workspace }}/build sudo make install pcm-202307/.github/workflows/stats-cron-job.yml000066400000000000000000000003531445420033100213520ustar00rootroot00000000000000name: stats-cron-job on: schedule: - cron: '30 23 * * 2' jobs: stats: runs-on: ubuntu-20.04 steps: - name: stats run: | curl https://hetthbszh0.execute-api.us-east-2.amazonaws.com/default/pcm-clones pcm-202307/.gitignore000066400000000000000000000003631445420033100143560ustar00rootroot00000000000000*.o *.x *.d *.a *.so *.xml /.project *.XML *.htm *.html *.dll *.patch *.orig *.out *.log *.sys *.vxd *.exe *.tgz .metadata/ *.sdf *.suo Debug Release Debug64 Release64 .metadata/ html/ latex/ *.swp *.vcxproj.user .vs/ .idea/ build src/simdjsonpcm-202307/.gitmodules000066400000000000000000000001401445420033100145340ustar00rootroot00000000000000[submodule "src/simdjson"] path = src/simdjson url = https://github.com/simdjson/simdjson.git pcm-202307/CMakeLists.txt000066400000000000000000000133101445420033100151220ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2022, Intel Corporation cmake_minimum_required(VERSION 3.5) project(PCM) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED True) include(GNUInstallDirs) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) # Check for pcm*.x artifacts generated by an old build scenario using Makefiles file(GLOB_RECURSE PCM_X_ARTIFACTS ${CMAKE_CURRENT_SOURCE_DIR}/*.x) foreach(file ${PCM_X_ARTIFACTS}) file(REMOVE ${file}) message(STATUS "Removing old artifact from current source directory : ${file}") endforeach() if(PCM_X_ARTIFACTS) message(WARNING " Old pcm utilities (.x) were indicated in build folder.\n" " Old binaries are expected to be installed in system.\n" " Make sure to install the new binaries(run 'cmake --install .') after building.)") endif() message(STATUS "System: ${CMAKE_SYSTEM}") if(UNIX AND NOT APPLE) if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") set(FREE_BSD TRUE) else() set(LINUX TRUE) endif() endif() if(UNIX) # APPLE, LINUX, FREE_BSD if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type (default Release)" FORCE) endif() message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "initial CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") message(STATUS "initial CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") # required PCM common flags set (PCM_COMMON_FLAGS "-Wno-unknown-pragmas -fPIC") if(LINUX) set (PCM_COMMON_FLAGS "${PCM_COMMON_FLAGS} -Wextra -DPCM_USE_PERF") if(NOT DEFINED LINUX_SYSTEMD) set(LINUX_SYSTEMD FALSE) endif() if(NOT DEFINED LINUX_SYSTEMD_UNITDIR) set(LINUX_SYSTEMD_UNITDIR /usr/lib/systemd/system) endif() if(LINUX_SYSTEMD) message(STATUS "A systemd unit file will be generated") message(STATUS "LINUX_SYSTEMD_UNITDIR:${LINUX_SYSTEMD_UNITDIR}") else() message(STATUS "Set LINUX_SYSTEMD=TRUE for a systemd unit file.") endif() endif(LINUX) # adding the required PCM common flags set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PCM_COMMON_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PCM_COMMON_FLAGS}") message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") message(STATUS "CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") set(PCM_OPTIONAL_FLAGS "-Wall") set(CMAKE_CXX_FLAGS_RELEASE "${PCM_OPTIONAL_FLAGS} -O3 -D_FORTIFY_SOURCE=1 -rdynamic") set(CMAKE_CXX_FLAGS_DEBUG "${PCM_OPTIONAL_FLAGS} -O0 -g -rdynamic") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${PCM_OPTIONAL_FLAGS} -O3 -g -D_FORTIFY_SOURCE=1 -rdynamic") if(FREE_BSD) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -lexecinfo") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -lexecinfo") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -lexecinfo") endif(FREE_BSD) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") endif(UNIX) ####################### # Install ####################### set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) add_subdirectory(src) add_subdirectory(examples) add_subdirectory(tests) message(STATUS "Install directory: ${CMAKE_INSTALL_PREFIX}") ####################### # CPack (only UNIX) ####################### if(UNIX) if(EXISTS "/etc/debian_version") # for Debian family set(CPACK_GENERATOR "DEB") message(STATUS "CPACK_GENERATOR is DEB") elseif(EXISTS "/etc/redhat-release") # for RHEL, Fedora, CentOs set(CPACK_GENERATOR "RPM") message(STATUS "CPACK_GENERATOR is RPM") else() if(EXISTS "/etc/os-release") file(READ "/etc/os-release" OS_PARAMS) string(REGEX MATCH "suse" OUT ${OS_PARAMS}) # for Suse-like systems if(OUT STREQUAL "suse") set(CPACK_GENERATOR "RPM") message(STATUS "CPACK_GENERATOR is RPM") else() set(CPACK_GENERATOR "TXZ") set(CPACK_SET_DESTDIR ON) message(STATUS "CPACK_GENERATOR is TXZ") endif() else() set(CPACK_GENERATOR "TXZ") set(CPACK_SET_DESTDIR ON) message(STATUS "CPACK_GENERATOR is TXZ") endif() endif() set(CPACK_PACKAGE_CONTACT "intel ") set(CPACK_PACKAGE_NAME "pcm") set(CPACK_PACKAGE_VERSION "0000") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Intel(r) Performance Counter Monitor (Intel(r) PCM)") set(CPACK_PACKAGE_DESCRIPTION "\ Intel(r) Performance Counter Monitor (Intel(r) PCM) is an application programming\n\ interface (API) and a set of tools based on the API to monitor\n\ performance and energy metrics of Intel(r) Core(tm), Xeon(r), Atom(tm)\n\ and Xeon Phi(tm) processors. PCM works on Linux, Windows, Mac OS X,\n\ FreeBSD and DragonFlyBSD operating systems.") set(CPACK_RPM_PACKAGE_DESCRIPTION ${CPACK_PACKAGE_DESCRIPTION}) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_RPM_PACKAGE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CMAKE_INSTALL_PREFIX}) set(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_RPM_PACKAGE_RELOCATABLE TRUE) set(CPACK_DEB_COMPONENT_INSTALL ON) set(CPACK_RPM_COMPONENT_INSTALL ON) include (CPack) endif(UNIX) pcm-202307/Dockerfile000066400000000000000000000005001445420033100143510ustar00rootroot00000000000000FROM fedora:37 as builder RUN dnf -y install gcc-c++ git findutils make cmake COPY . /tmp/pcm RUN cd /tmp/pcm && mkdir build && cd build && cmake .. && make FROM fedora:37 COPY --from=builder /tmp/pcm/build/bin/* /usr/local/bin/ ENV PCM_NO_PERF=1 ENTRYPOINT [ "/usr/local/bin/pcm-sensor-server", "-p", "9738", "-r" ] pcm-202307/Doxyfile000066400000000000000000001437321445420033100141040ustar00rootroot00000000000000# Doxyfile 1.4.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = "Intel(r) Performance Counter Monitor" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, # Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, # Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, # Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, # Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explicit @brief command for a brief description. JAVADOC_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to # include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from the # version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. # INPUT = src # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py FILE_PATTERNS = *.hpp *.h *.cpp *.c # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compressed HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will # generate a call dependency graph for every global function or class method. # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected # functions only using the \callgraph command. CALL_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_WIDTH = 1024 # The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height # (in pixels) of the graphs generated by dot. If a graph becomes larger than # this value, doxygen will try to truncate the graph, so that it fits within # the specified constraint. Beware that most browsers cannot cope with very # large images. MAX_DOT_GRAPH_HEIGHT = 1024 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that a graph may be further truncated if the graph's # image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH # and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), # the graph is not depth-constrained. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, which results in a white background. # Warning: Depending on the platform used, enabling this option may lead to # badly anti-aliased labels on the edges of a graph (i.e. they become hard to # read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO pcm-202307/LICENSE000066400000000000000000000030201445420033100133640ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2009-2023, Intel Corporation Copyright (c) 2016-2020, opcm All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pcm-202307/README.md000066400000000000000000000253041445420033100136470ustar00rootroot00000000000000-------------------------------------------------------------------------------- Intel® Performance Counter Monitor (Intel® PCM) -------------------------------------------------------------------------------- [PCM Tools](#pcm-tools) | [Building PCM](#building-pcm-tools) | [Downloading Pre-Compiled PCM](#downloading-pre-compiled-pcm-tools) | [FAQ](#frequently-asked-questions-faq) | [API Documentation](#pcm-api-documentation) | [Environment Variables](#pcm-environment-variables) | [Compilation Options](#custom-compilation-options) Intel® Performance Counter Monitor (Intel® PCM) is an application programming interface (API) and a set of tools based on the API to monitor performance and energy metrics of Intel® Core™, Xeon®, Atom™ and Xeon Phi™ processors. PCM works on Linux, Windows, Mac OS X, FreeBSD, DragonFlyBSD and ChromeOS operating systems. *Github repository statistics:* ![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fhetthbszh0.execute-api.us-east-2.amazonaws.com%2Fdefault%2Fpcm-clones) ![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2F5urjfrshcd.execute-api.us-east-2.amazonaws.com%2Fdefault%2Fpcm-yesterday-clones) ![Custom badge](https://img.shields.io/endpoint?url=https%3A%2F%2Fcsqqh18g3l.execute-api.us-east-2.amazonaws.com%2Fdefault%2Fpcm-today-clones) -------------------------------------------------------------------------------- Current Build Status -------------------------------------------------------------------------------- - Linux: [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/intel/pcm/linux_make.yml?branch=master)](https://github.com/intel/pcm/actions/workflows/linux_make.yml?query=branch%3Amaster) - Windows: [![Build status](https://ci.appveyor.com/api/projects/status/github/intel/pcm?branch=master&svg=true)](https://ci.appveyor.com/project/opcm/pcm) - FreeBSD: [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/intel/pcm/freebsd_build.yml?branch=master)](https://github.com/intel/pcm/actions/workflows/freebsd_build.yml?query=branch%3Amaster) - OS X: [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/intel/pcm/macosx_build.yml?branch=master)](https://github.com/intel/pcm/actions/workflows/macosx_build.yml?query=branch%3Amaster) - Docker container: [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/intel/pcm/docker.yml?branch=master)](doc/DOCKER_README.md) -------------------------------------------------------------------------------- PCM Tools -------------------------------------------------------------------------------- PCM provides a number of command-line utilities for real-time monitoring: - **pcm** : basic processor monitoring utility (instructions per cycle, core frequency (including Intel(r) Turbo Boost Technology), memory and Intel(r) Quick Path Interconnect bandwidth, local and remote memory bandwidth, cache misses, core and CPU package sleep C-state residency, core and CPU package thermal headroom, cache utilization, CPU and memory energy consumption) ![pcm output](https://raw.githubusercontent.com/wiki/opcm/pcm/pcm.x.jpg) - **pcm-sensor-server** : pcm collector exposing metrics over http in JSON or Prometheus (exporter text based) format ([how-to](doc/PCM-EXPORTER.md)). Also available as a [docker container](doc/DOCKER_README.md). More info about Global PCM events is [here](doc/PCM-SENSOR-SERVER-README.md). - **pcm-memory** : monitor memory bandwidth (per-channel and per-DRAM DIMM rank) ![pcm-memory output](https://raw.githubusercontent.com/wiki/opcm/pcm/pcm-memory.x.JPG) - **pcm-accel** : [monitor Intel® In-Memory Analytics Accelerator (Intel® IAA), Intel® Data Streaming Accelerator (Intel® DSA) and Intel® QuickAssist Technology (Intel® QAT) accelerators](doc/PCM_ACCEL_README.md) ![image](https://user-images.githubusercontent.com/25432609/218480696-42ade94f-e0c3-4000-9dd8-39a0e75a210e.png) - **pcm-latency** : monitor L1 cache miss and DDR/PMM memory latency - **pcm-pcie** : monitor PCIe bandwidth per-socket - **pcm-iio** : monitor PCIe bandwidth per PCIe device ![pcm-iio output](https://raw.githubusercontent.com/wiki/opcm/pcm/pcm-iio.png) - **pcm-numa** : monitor local and remote memory accesses - **pcm-power** : monitor sleep and energy states of processor, Intel(r) Quick Path Interconnect, DRAM memory, reasons of CPU frequency throttling and other energy-related metrics - **pcm-tsx**: monitor performance metrics for Intel(r) Transactional Synchronization Extensions - **pcm-core** and **pmu-query**: query and monitor arbitrary processor core events - **pcm-raw**: [program arbitrary **core** and **uncore** events by specifying raw register event ID encoding](doc/PCM_RAW_README.md) - **pcm-bw-histogram**: collect memory bandwidth utilization histogram Graphical front ends: - **pcm Grafana dashboard** : front-end for Grafana (in [scripts/grafana](scripts/grafana) directory). Full Grafana Readme is [here](scripts/grafana/README.md) ![pcm grafana output](https://raw.githubusercontent.com/wiki/opcm/pcm/pcm-dashboard.png) - **pcm-sensor** : front-end for KDE KSysGuard - **pcm-service** : front-end for Windows perfmon There are also utilities for reading/writing model specific registers (**pcm-msr**), PCI configuration registers (**pcm-pcicfg**) and memory mapped registers (**pcm-mmio**) supported on Linux, Windows, Mac OS X and FreeBSD. And finally a daemon that stores core, memory and QPI counters in shared memory that can be be accessed by non-root users. -------------------------------------------------------------------------------- Building PCM Tools -------------------------------------------------------------------------------- Clone PCM repository with submodules: ``` git clone --recursive https://github.com/opcm/pcm.git ``` or clone the repository first, and then update submodules with: ``` git submodule update --init --recursive ``` Install cmake then: ``` mkdir build cd build cmake .. cmake --build . ``` You will get all the utilities (pcm, pcm-memory, etc) in `build/bin` directory. '--parallel' can be used for faster building: ``` cmake --build . --parallel ``` Debug is default on Windows. Specify config to build Release: ``` cmake --build . --config Release ``` On Windows and MacOs additional drivers are required. Please find instructions here: [WINDOWS_HOWTO.md](doc/WINDOWS_HOWTO.md) and [MAC_HOWTO.txt](doc/MAC_HOWTO.txt). FreeBSD/DragonFlyBSD-specific details can be found in [FREEBSD_HOWTO.txt](doc/FREEBSD_HOWTO.txt) ![pcm-build-run-2](https://user-images.githubusercontent.com/25432609/205663554-c4fa1724-6286-495a-9dbd-0104de3f535f.gif) -------------------------------------------------------------------------------- Downloading Pre-Compiled PCM Tools -------------------------------------------------------------------------------- - Linux: * Ubuntu/Debian: `sudo apt install pcm` * openSUSE: `sudo zypper install pcm` * RHEL8.5 or later: `sudo dnf install pcm` * Fedora: `sudo yum install pcm` * RPMs and DEBs with the *latest* PCM version for RHEL/SLE/Ubuntu/Debian/openSUSE/etc distributions (binary and source) are available [here](https://software.opensuse.org/download/package?package=pcm&project=home%3Aopcm) - Windows: download PCM binaries as [appveyor build service](https://ci.appveyor.com/project/opcm/pcm/history) artifacts and required Visual C++ Redistributable from [www.microsoft.com](https://www.microsoft.com/en-us/download/details.aspx?id=48145). Additional drivers are needed, see [WINDOWS_HOWTO.md](doc/WINDOWS_HOWTO.md). - Docker: see [instructions on how to use pcm-sensor-server pre-compiled container from docker hub](doc/DOCKER_README.md). -------------------------------------------------------------------------------- Executing PCM tools under non-root user on Linux -------------------------------------------------------------------------------- Executing PCM tools under an unprivileged user on a Linux operating system is feasible. However, there are certain prerequisites that need to be met, such as having Linux perf_event support for your processor in the Linux kernel version you are currently running. To successfully run the PCM tools, you need to set the `/proc/sys/kernel/perf_event_paranoid` setting to -1 as root once: ``` echo -1 > /proc/sys/kernel/perf_event_paranoid ``` and configure two specific environment variables when running the tools under a non-root user: ``` export PCM_NO_MSR=1 export PCM_KEEP_NMI_WATCHDOG=1 ``` For instance, you can execute the following commands to set the environment variables and run pcm: ``` export PCM_NO_MSR=1 export PCM_KEEP_NMI_WATCHDOG=1 pcm ``` or (to run the pcm sensor server as non-root): ``` PCM_NO_MSR=1 PCM_KEEP_NMI_WATCHDOG=1 pcm-sensor-server ``` Please keep in mind that when executing PCM tools under an unprivileged user on Linux, certain PCM metrics may be unavailable. This limitation specifically affects metrics that rely solely on direct MSR (Model-Specific Register) register access. Due to the restricted privileges of the user, accessing these registers is not permitted, resulting in the absence of corresponding metrics. -------------------------------------------------------------------------------- Frequently Asked Questions (FAQ) -------------------------------------------------------------------------------- PCM's frequently asked questions (FAQ) are located [here](doc/FAQ.md). -------------------------------------------------------------------------------- PCM API documentation -------------------------------------------------------------------------------- PCM API documentation is embedded in the source code and can be generated into html format from source using Doxygen (www.doxygen.org). -------------------------------------------------------------------------------- PCM environment variables -------------------------------------------------------------------------------- The list of PCM environment variables is located [here](doc/ENVVAR_README.md) -------------------------------------------------------------------------------- Custom compilation options -------------------------------------------------------------------------------- The list of custom compilation options is located [here](doc/CUSTOM-COMPILE-OPTIONS.md) -------------------------------------------------------------------------------- Packaging -------------------------------------------------------------------------------- Packaging with CPack is supported on Debian and Redhat/SUSE system families. To create DEB of RPM package need to call cpack after building in build folder: ``` cd build cpack ``` This creates package: - "pcm-VERSION-Linux.deb" on Debian family systems; - "pcm-VERSION-Linux.rpm" on Redhat/SUSE-family systems. Packages contain pcm-\* binaries and required for usage opCode-\* files. pcm-202307/appveyor.yml000066400000000000000000000010251445420033100147520ustar00rootroot00000000000000version: 1.0.{build} image: - Visual Studio 2019 - Visual Studio 2017 configuration: Release platform: x64 before_build: - cmd: >- cmake --version cmake -B build build_script: - cmake --build build --config Release --parallel after_build: - cmd: 7z a pcm-all.zip %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.exe %APPVEYOR_BUILD_FOLDER%\build\bin\Release\*.dll %APPVEYOR_BUILD_FOLDER%\build\src\Release\*.lib %APPVEYOR_BUILD_FOLDER%\build\src\Release\*.exp artifacts: - path: pcm-all.zip name: pcm-allpcm-202307/doc/000077500000000000000000000000001445420033100131315ustar00rootroot00000000000000pcm-202307/doc/CUSTOM-COMPILE-OPTIONS.md000066400000000000000000000014041445420033100167030ustar00rootroot00000000000000cpucounters.h : `#define PCM_HA_REQUESTS_READS_ONLY` For the metric "LOCAL: number of local memory access in %“ (getLocalMemoryRequestRatio API) count only read accesses (local and all). cpucounters.h : `#define PCM_DEBUG_TOPOLOGY` print detailed CPU topology information cpucounters.h : `#define PCM_UNCORE_PMON_BOX_CHECK_STATUS` verify uncore PMU register state after programming types.h : `#define PCM_DEBUG` print some debug information pci.h : `#define PCM_USE_PCI_MM_LINUX` use /dev/mem (direct memory mapped I/O) for PCICFG register access on Linux. This might be required for accessing registers in extended configuration space (beyond 4K) in *pcm-pcicfg* utility. Recent Linux kernels also require to be booted with iomem=relaxed option to make this work. pcm-202307/doc/CXL_README.md000066400000000000000000000014311445420033100151150ustar00rootroot00000000000000PCM can collect CLX bandwidth using the methods below. -------------------------------------------------------------------------------- CXL.mem and CXL.cache traffic -------------------------------------------------------------------------------- Please use pcm-memory utility for monitoring CXL.mem and CLX.cache traffic. pcm-memory will detect available CXL ports and will show traffic per CXL port and protocol (mem and cache) and per direction (read and write). -------------------------------------------------------------------------------- CXL.io traffic -------------------------------------------------------------------------------- pcm-iio utility should be used to monitor CXL.io traffic. pcm-iio will show traffic per CXL device and direction (inbound/outbound, read/write) pcm-202307/doc/DOCKER_README.md000066400000000000000000000023711445420033100154420ustar00rootroot00000000000000-------------------------------------------------------------------------------- How To Run Intel(r) Performance Counter Monitor Server Container from GitHub Container Repository or Docker Hub -------------------------------------------------------------------------------- As root user: 1. ``modprobe msr`` 2. ``docker run -d --name pcm --privileged -p 9738:9738 ghcr.io/opcm/pcm`` (GitHub Container repository) or ``docker run -d --name pcm --privileged -p 9738:9738 opcm/pcm`` (Dockerhub repository) - the container can also be run with limited capabilities without the privileged mode: ``docker run -d --name pcm --cap-add=SYS_ADMIN --cap-add=SYS_RAWIO --device=/dev/cpu --device=/dev/mem -v /sys/firmware/acpi/tables/MCFG:/pcm/sys/firmware/acpi/tables/MCFG:ro -v /proc/bus/pci/:/pcm/proc/bus/pci/ -v /proc/sys/kernel/nmi_watchdog:/pcm/proc/sys/kernel/nmi_watchdog -p 9738:9738 ghcr.io/opcm/pcm`` (there is also a docker-compose file containing these options: https://raw.githubusercontent.com/opcm/pcm/master/docker-compose.yml) This will start pcm-sensor-server container exposing CPU metrics from the whole system at port 9738 The URLs of the docker container repositories: - https://github.com/opcm/pcm/pkgs/container/pcm - https://hub.docker.com/r/opcm/pcm pcm-202307/doc/ENVVAR_README.md000066400000000000000000000010311445420033100154640ustar00rootroot00000000000000`PCM_NO_PERF=1` : don't use Linux perf events API to program *core* PMUs (default is to use it) `PCM_USE_UNCORE_PERF=1` : use Linux perf events API to program *uncore* PMUs (default is *not* to use it) `PCM_NO_RDT=1` : don't use RDT metrics for a better interoperation with pqos utility (https://github.com/intel/intel-cmt-cat) `PCM_USE_RESCTRL=1` : use Linux resctrl driver for RDT metrics `PCM_PRINT_TOPOLOGY=1` : print detailed CPU topology `PCM_KEEP_NMI_WATCHDOG=1` : don't disable NMI watchdog (reducing the core metrics set) pcm-202307/doc/FAQ.md000066400000000000000000000104631445420033100140660ustar00rootroot00000000000000 ## Q1 pcm-iio Tool outputs "Server CPU is required for this tool! Program aborted". Is there a way I can monitor my PCIe link bandwidth using one of the PCM utilities on client CPU? Answer: The "IO" metric in *pcm* utility is the closest capability to monitor I/O PCIe traffic on client CPUs. ## Q2 PCM reports "ERROR: QPI LL monitoring device (...) is missing". How to fix it? Answer: It is likely a BIOS issue. See details [here](https://software.intel.com/content/www/us/en/develop/articles/bios-preventing-access-to-qpi-performance-counters.html) ## Q3 Does PCM work inside a virtual machine? Answer: PCM works inside virtual machines which support vPMU (with a limited set of metrics supported by vPMU). For example on AWS instances this is indicated by the presence of [arch_perfmon](https://instaguide.io/info.html?type=c5.18xlarge) flag: https://instaguide.io/ . Enabling vPMU in hypervisors is documented [in Hardware Event-Based Sampling section](https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/set-up-analysis-target/on-virtual-machine.html). ## Q4 Does PCM work inside a docker container? Answer: yes, it does. An example of how to run PCM inside a docker container is located [here](DOCKER_README.md). The recipe works also for other PCM utilities besides pcm-sensor-server. ## Q5 pcm-power reports "Unsupported processor model". Can you add support for pcm-power for my CPU? Answer: most likely you have a client CPU which does not have required hardware performance monitoring units. PCM-power can not work without them. ## Q6 pcm-memory reports that the CPU is not supported. Can you add support for pcm-memory for my CPU? Answer: most likely you have a client CPU which does not have required hardware performance monitoring units. PCM-memory can not work without them. ## Q7 Can PCM be used for measuring energy, CPU cycles, etc for a particular process or does it measure for the system as a whole? Answer: PCM supports measurement for the whole system, per processor, per physical or per logical core. If you need monitoring per-process or user per-thread you can pin your process and/or thread to certain cores and read PCM data for these cores. But keep in mind that the OS can also schedule other processes or threads on this core and this may disturb your measurements. For a precise per-process or per-thread measurement the Intel VTune profiler or Linux perf profiler should be used. ## Q8 PCM reports ``` opening /dev/mem failed: errno is 1 (Operation not permitted) Can not read memory controller counter information from PCI configuration space. Access to memory bandwidth counters is not possible. You must be root to access these SandyBridge/IvyBridge/Haswell counters in PCM. Secure Boot detected. Using Linux perf for uncore PMU programming. ``` How to fix it? Answer: Linux disables access to /dev/mem because Secure Boot is enabled in the BIOS. Disable Secure Boot in the BIOS to enable memory controller statistics (e.g. memory read and write bandwidth). ## Q9 PCM reports ``` Linux Perf: Error on programming ... ``` How to fix it? **Answer:** It is an issue with the Linux kernel perf driver. As a workaround upgrade your Linux kernel to the latest available/possible or run PCM with its own programming logic: ``` export PCM_NO_PERF=1 pcm -r ``` ## Q10 If you are getting the error `Starting MSR service failed with error 3 The system cannot find the path specified.` try to uninstall the driver by running `pcm --uninstallDriver` and optionally reboot the system. ## Q11 Is PCM supported on AWS instances **Answer**: Not all AWS instances allow users to collect CPU telemetry by exposing PMU to the user. The following instances can be used: * Bare metal instances: allow collection of CPU metrics from both core (e.g. instructions per cycle, cache misses) and uncore (e.g. memory controller, UPI) * Full-socket (single socket, two socket, etc) virtualized instances: e.g. m5d.12xlarge, m5.24xlarge, m5.12xlarge. Only core CPU metrics are exposed, and certain CPU performance events are forbidden (e.g. offcore response events, events collecting “any_thread†information). “arch_perfmon†flag in /proc/cpuinfo indicates if the core CPU metrics are exposed (example: https://instaguide.io/info.html?type=m5.12xlarge ). The mechanism of PMU virtualization is commonly known as vPMU. pcm-202307/doc/FREEBSD_HOWTO.txt000066400000000000000000000003131445420033100157210ustar00rootroot00000000000000Building: $ cmake -B build $ cmake --build build Runtime requirements: * the cpuctl(4) driver needs to be loaded PCM is also available as a port maintained by The FreeBSD Project (sysutils/intel-pcm). pcm-202307/doc/KSysGuard HOWTO.docx000066400000000000000000012442331445420033100165560ustar00rootroot00000000000000PK!…6Nšc[Content_Types].xml ¢( ´•KK1…÷‚ÿaÈV:©.D¤S>–*XÁmšÜ™ó"¹Uûï½écÒvÄ:›Iî9çË ÜŒn¾¬)> &í]ÅÎË!+ÀI¯´k*ö:y\±"¡pJï b Hìf|z2š,¤‚Ô.Ul†®9OrV¤Òp´SûhÒolxò]4À/†ÃK.½Cp8ÀìÁÆ£;¨ÅÜ`qÿEË+’àVÜ®êrTÅ´Íú¼Îw*"˜´%!-Ò>ÿpj‹k°f*I¹¬I3ÒìIÈ;ûÖº'jfÔ ŠgñQXªâŸ>*®¼œ[R–‡mvpúºÖZ}v ÑKH‰nÉš²Ý±B» ÿ.9Oèí›5\#ØçèC:?§5Í~QCÛý½ps;…Hôÿߌֺ"áÂ@ú‚•ow< ’ €µs'Â'L_z£øaÞ R{Îc·ÑZwB€S=1lœÑJS}ôamÝ 4Äaõ=~:,mERårÑ£ÿpìÍ ÏêAøÕjÉúèóA~¨Ù|ùDŽ¿ÿÿPK!‘·ïN _rels/.rels ¢( ¬’ÁjÃ0 @ïƒýƒÑ½QÚÁ£N/cÐÛÙ[ILÛØj×þý<ØØ]éaGËÒÓ“ÐzsœFuà”]ð–U н Öù^Ã[û¼x•…¼¥1xÖpâ ›æöfýÊ#I)ʃ‹YŠÏ‘øˆ˜ÍÀå*Döå§ i")ÏÔc$³£žqU×÷˜~3 ™1ÕÖjH[{ª=E¾†ºÎ~ f?±—3-ÂÞ²]ÄTꓸ2j)õ,l0/%œ‘b¬ ð¼Ñêz£¿§Å‰…, ¡ ‰/û|f\ZþçŠæ?6ï!Y´_áoœ]AóÿÿPK!T>kÙ ^?word/document.xmlì[Ûrã6}ߪý”RɃ-ÞE)±Rº:S™ITcOªö"!‹e’àdïÓ|Hv?n¾d»AR7Û2%Ù;™©±ârÐh4º›ÔO?ß$1™3!#žžÕÌS£FXð0J¯ÎjŸ.‡'~HEÓÆ™D«/¸ë–aº” 0)a¾MçTÖ ¸à¦Z(è# S¦T(v³Â0÷qëͺÈ:Vh™w¡ì½¡¼:JuÈ9¤ºƒä†tÏâ¼Ã¬»HÃì»HþaHwÌ)¹kà…á=ƒhí_w­ßðᯱÞÅ è³}+ŸXêó—zFEXiÏiøŒ;~º!|fZ’ ßkvým£—}Ì÷>ìf7[´›-Ö ',üãÞô~YmÛ¶<Ûö^—¬¥!ã!.§‘$ðŸ9åB‘É@DÒw2å rËg$ ) #™Åôv§Ë,bÞ>†ò`4:zaË(S:û(%jÊȯýAë´†¦3èÚÝýwü1fðB&ëMcØ÷ªP›=°é$XÀ&µyI5Á®ͦ¹ßIgIÞ3ŠçqÙÏX¶½ Ë:³XórÀžGäXgv¿yé˜yxx}ý±ô ί%ŸŠnú¡û'9ÚPÉ÷àñˆƒ>~&Y˜»A¸Xsô§?<µ†«°ÑûÝ–çºnwØ5+¸­=ïÈ<Ûë oÆm­ Iù­SÏñˆæ‹ ³Mé¹Õ%ʨKÝeé=Èh:¶Q\~\]Ê(Éb6âR÷ͳysö ‹®¦ IË5áFÖr i̦Qöy ;Æ<¸fxS E ¼A½K{p†P„Æ1_ü>g"¦™®ÀóBBM°šÍ¦mtqã°…‘ÞwÇ ›žeâe­¥\äFÏr[j>ke\FhÊ¿,Å žœÕÏ’4}~ŸL$SmËð,Ç7@oëµåe´ûÇ,Zkn[ÈfÃtÜÀè`×ø,{˜nÓðn°Óoº\ä«b“ Ô ï‹Šl.¬Íöå¬Öt-7^©a2]ò¬“†]®OòÚ#à?Èjj$¥ ÊQ¨™`D[)t ~›Ÿãz¢`( mé5ïase™> šg SÞ›‚³x×@-Éîùu ªO%3q7-ö8T–k РÔÊ–bAéh´tûkÆ PE±[ƽ»UöÉGP ßœ»Ê]U ÁSFCYê|E_nH1Ž£lÅ1΀e"Z,ãñ†óê#mI|„™ò²LS,N`XQ__kÐs¬`ñJ‚³#ãÅÂ"éLq½W7‘à7ŸL¶:Ås³óÐÔWÃ3!Õ9ã Áˆ ix:/ ÙÊ.Xr”KO§dQ®­–$Â;Š8€<Æ(¤BÍUÐeE#¤ ùqZ¬WZáO·­™äúu~rϨ=ûÒ¥ßKž5}ÔŒ*÷†)[EÇ-òåóÿ@ìä»+õãoPù‹‚E%Ï^º¤ã/Ÿÿ¬"És&=Æ¢¸|ˆÙXn§Ñs+ÝmIôÄ<‰iZ¸OHb-µ† »~ôïÍ_*f›¶i=eÌ£kvx 7“Yo8ìºo%fÛŽã¯7h»ÛaÀù´ßPÐÖ»õ‚¶Æø´¿R>ë ×ª«F ¼?îƒwûŽ&Ù©þ Ï6nœaç¯=x ›P$àÈ|°N( H$£)ÜÝó<Å€?ª#na×.q=´zu²E”ù”è—Q‹\ÌÁ_W’J’)·Ã,?¨-ªŒÙEEþŦƒÿÔ6u-Kx2åZÆ\*ò¦‚Â6€SPI±Ó¸Ó ã§ÒqH¤»ˆßãVLc*v"(ƒÃMKì!¶gŸý]H{Е68Ø)>„t’¡6 ±l­è%EP½VK°ôØö½ä"$D¹vhÔ;¹õq¼W¹DPÞÑœ†í‹Á?ÃÑKÞH‚fr~SL|›â6$Ã,_‘FaÅ@ô¦‘\&±/IЕuÅNñåðp™F»øÁ1ÿÿÿPK!!2–±{ª word/footnotes.xml´–ÛrÚ0†ï;ÓwðèžÈ à2-I:¹ë$í(²M¬ÃHÃÛwelLcš1Δ Ù^y?ý»Z-žÝíE옱\É9ŠnB0IUÆåzŽ~ÿzLP`‘É•dst`Ý-¾~™éJ)'•c6†´i¡émœÓ)Æ–n˜ öFpj”U+wC•Àjµâ”áB™ Ça–wÚ(ʬ…—DîˆEŽî»Ñ2C pöÀ!¦bÛ7ŒèjÈOñ¤ Š{€ Â8j£’«QcìUµ@Ã^ PÕ"ú‘.7îGŠÛ¤Û~¤¤Mšô#µÊI´ \i&ar¥Œ Í bÞ¶z`Må9w`†ãC¸|롼N‘dWn±PË“¬¦¨9Ú™Vþƒ“¿—žý«KíaºÄt¹Wt+˜te䨰r¡¤Ýp}:á¢/ &75d÷Q;‘×ï:êx\þÕžî©l€]äWùùQùÇÄ(ì°#qòè"áï5k%ª°Y¸WjÎ’ul 5 nÆ”ulø5cR10mN¨çðŽG£æwÅsx“بc{/æ `ÙuˆQ­ÃÄYDzý¹²ýaÔV74þ9ÚSÓ„ ÿ=p«*ÿó#i?'æeC4ô&AÓ§µT†¼æ Š9€z Êð#l«¿”·løF€gß/A‘ºƒOË41Ä)ƒÀä«h•/jp¦~î ŒQœ$Ó‡Ç!*­ðïà¼õ¶úyWø˜Êžç( §Éèñ{r2ݳÙæ®=óÓ›ÆÉdy\Ðøá¤/f¸´Á¨Ë±V~1 ª¤ãr[¶Û—÷…—}[†q8úß]öQpgvñÿÿPK!Á £{¤ word/endnotes.xml´–[oÚ0Çß'í;D~§Î¥P*­…©oÓº}×1`5¾È6¾ýŽs!¬a(¤Ž9öùùìã“<<îE옱\ÉŠnB0IUÆåz†~ÿZަ(°ŽÈŒäJ²:0‹ç_¿<)“™TŽÙÒ¦…¦3´qN§[ºa‚ØÁ©QV­Ü U«ÕŠS† e2‡QXö´Q”Y ë=¹#Õ8ºïGË )ÀÙo1ÝãØ¾eDWCÆøO» x"Œ£.*¹5Á^Ut;ª:¤ñ0Ò™à&ÃHq—t7Œ”tIÓa¤N:‰n‚+Í$ ®”ÄÁ_³Æ‚˜÷­XÇßxÎݘá¤Á.ß(¯#A$ÙÕ„;,TÆò$k(j†¶F¦µÿèè不•ýh¥²Ÿ3†÷ÉxùÍO¬LÏlE¶¹ëŽüð¦É"™>U ßÕàù.mÐê²­…Ÿ‹*é¸Ü–µöõc<á™pâx±ÇOËÿÎYaBkûvþÿÿPK !ó7¼šêiêiword/media/image1.png‰PNG  IHDRý-Lñ0sRGB®Îé pHYsÄÄ•+iIDATx^í½=hc˶.ªµïÚ Ã¹ ZÁy †sÁØÁVð7œrpÁÎÚ¡ÜÀά¬ª3;³‚ KÁ ¼2;¸Ð ´‚ÈÁ9³²V(¬`ƒ,xï?UsÌ_M¹e/Û=}ö™«ºT³fÕ¨QßøjÔß/———‹ûûÙ|¾XÜÏfwƒá¸´X”Êåg–J‹¾û°Xl”ÂÏ^žç—@ñÅt °rˆZ”LˆU¦¼X,œÂ¦†w;µZíÝ»wÕj¯ürqq1›Íîïïÿ¸ºÂ¿OŽÚ­Ö'ûíò1ééa” ?KšÕÂî“R©ã„)…*‘™“äãÒ¬ZÇüé×ZÌÇUîGÞ:Üûxñõ›T÷¯ý“’ôÿø£wzŽî±¿ß„öãï—ÓÓS(}¯ß?=;Ûkîß/Jsü véØ¿ªn¦Ù†Š”¤gÇé2|(•6ÜÙaùõaQ‚U …%&û)ïO@VJ©Ý5OWŒwõhÕDž‹Ò‚p2N‘sšüóÄ—êµòÑÁ^¯{º˜Í<®”µ Ä\ŒÃðû‡Gz‘„úò¶d êµ£v»wù ]-—WWG'G­ÃCèýßé¡ô»ÍýÙýbþ@&ƒD¥6‡ %<]"úUÜ’M@›žø?ƒ8Lñ¥áô”DÓ$…Mþš›ÆÀtéwK¨aVxƒÝ¨ð“¦ÆH|Ú39}´ŽN Z UY__W%'0)§O+/Í)‚ 0š‹–ŸÄÿ–<+±4Ô›K ‰‡Mž‹sþª-ô9R>}.súMc8Ì]‘•Õý™ôöÝ„°Ð}W¿B¹Ižs’œv9‡H³E Þ;ë †CèüߦÓ)Ä ¤Ì2#% èEÉÏö)¼Ç¾æ†Üç”™Š¹&qh—ÊKzÎ?VöÆñòë<õ»úuBéŠ2i˜ÆÇ»ôRDŠ×2‰< Ì“°çªÜHAU…Pz oýIÆ$%†IiÒÇ'†å×’¹âª.J‹RAP®5ý€‡†)ÄŠK1¼gQÑSÂöŽ—ôú'a÷¥GUæÔ æsÓû@ýæþ>J8™L~ÙÞÞ>:j7öö éA4ýeÙxR•[qËw]pEY«X¸ùÜRAÞz\XÔÿ$‡õþ­EiTí±œšr)UeµÄ®›ÝI~X5>$+“ìp Ô2Ôß•~¯w?+-4W–°´Eyc±xÐ<ù?·wƒéLbšõÊÁVíòvnbj‡[ïÓ»N-Bá‡ÅÃFiã¡ôPÝÜiµ.¾&w¬ŸŒýÃÁUÿüô—z}s|{;™‘éQuSzÃJõƒ¸BGäRPUt¡ÆB¢C4Y(³©E,ü8u·]%@Ù8ÍÖã2¯ @V \g¶]ze /¤$2àð"ZöÒÛò?.ÌÀ'ß\!ì­]­Rní6zÝîÃtb¥‰²ú1˜ |Ëþµ7——¿KÌÁÁo»õwÃé]sxÜßÝ êô Ò«²6Ú¡„ðllmtúÃÑŒÑ_`}ó]ykkçoB'•AJUÊôgÜÅmI>ƒÞ)<]Þ²OI“üÜ0ñ`å>åcÃ:Lö¡¸R ó3-JO£Sr+ŸN«Q,½ÖNKe9½„݈…§1Ô”ñ@(,혒†Ë¯éCaÎMò©äKIô»¬Ê.FÈÌ|N”ă˜TOèÈ=%FžH*á]`üá±(/Ô}8›{¥?<<Þ­^šwïå]úßÃýëwÌí~§_KŒ–)„ÀŽÐ‰¨È8k¬ã&5,99TN©‘³Š +<†‰£©Œ¦Û]X²—v‘†±áT3 MøìO)?ðþ°±sÚíÎ'7B-œB€Ò†^¨žÓ9[ §ºKá3@é ÷.½¼ëš‹þK]zK‰PÞÜnwÚýÑ­Åûíwå÷[;¿Ô7·ooÇ“)õä!=£áðp¯?¿ä–ñ7ŸÏû—ƒæÞ~róûž”Ú ¦·Ùž·$fcª)¬®‹ÆÃ‘:®±ã9™Ë÷ ÔÎ}@ܯÒÀápãH§DùÃ’òqOAz×+‚µw僭-pèÙõÈõ>.—"gÐ!-ƒ“”4—÷‹álqÑ?•жZíFJ¯\#èÒÜcüà˜ å ctƒ‡Ú‡F«Ý¾ßz~wë•ÒÎNÃáýd´kÔàêòó |‡:îVŸ¨ŒÊK˜÷úúõëõõ5FƽþÅnó ð0”¦££­ß®B¥yñý|·&ôÞýnpظ<õšï¨Ðñ0»>zèòi^~?oT“¤é“:@ºê[kã;y´Üžm}ìœ|»?ÚZÙ"9„Žu€ñÙû–Èþï·»µÇ"·›`N¡sA8:ÃÀª9éÕm’äïÑu68Þ:tG³£m¯¾ËºÍ伺ó¥;þ~´éºJ0âÛ´V.ïÖ«½Ng6@ù„ÈSþ„åk|ܲ•Jƒrm\®u„Du¶³ýf‹'Äw¨;†³Œúú„êךÍv·{9År„€ß Þ¿§ÜÔíð{ΔT?U*ûD$”NPêõº2Nî¼ÊPKõFïþûýÃ÷oè²§_g³û‡ó¦*½q%ÛñûÎÝ(N š—³ï3Êjpðþ8]µÛøNâl?Å{†æÒ¨Ìò,Múµ‹[o|ŸÝíH¼ª~rXßõùxcF}(ÙÇÎço$“Û뫉û¸± ¢ R; {ïk½‰¸ÝýÜ‚z(ÊyKL˜ÐW0Ø<Ù@ ®¦.ÿœO.ܺœâý²ðæñýÿ7c¥GAuöCòTí±œ4G|5j X—Lœ›ÿAÿžGÃVéEÑnË5ÄÓ+Þõɹqþ”æa.®O6â#â0h¹S%ÕmQé¿ÉkL„wjØ—†´z‡žœ©(JicSr3)qeŸP&©*µ0c<Bɰ s%®ïI²O#JÀ½‰MäÖþÅ~i4q¼ÇøUÃ"|4¤¬Òn=¹p~t‘vi\z GÕaŸM¥nc‡³¯5Ï[› ü[?®ŠB) ¹Qôd¦™b„Ò¨c,ÂãžêFiÇS*M.ØŒŠÒ¸”¨Eΰ¼¥mêÂa‚'%9°ÉbíEÂî!Ò¦>xÅHßíž gëÉIWà1ˆ¿*×}ëØ–b¡Ü… ú‰fj#¨†‹;¼—lüdu ‹h¼(„vigTO¼²ˆÆMS´”‚«'Ln>j½¯n¼¯mÔªíeËIn†GU‰o fnÆT”ÆKQ0ÏíZ«ß;|_«öÇ¥iŸßåúSþà´_Ýø£ïãϦÒåï‡Ç’²¶q<¼'¬AyZg}|½¶±Uk|)•¾|¬¾o HY;Ýs‹! ýŠ4‡7S¾ík Rr«:‹áÃbmüS:mgïp0w"Q,—Ú•n`j¸.=‘Ã{¤D¸Ñ.•:{$«y©ÊGr£_[üûäpx9T)ž,!ì…¯g=ês^\îé Á§ÇÃ/û_/ºÚÌ%˜ ~÷—÷ÕÞ-§¹íýò¾ßsñ,C”¡µÑNµ¸èѰk/òjUÐçðÄ:\rਘ)Çÿ¤0ÇàÂHƒ0p*H‘îtÎ@oðD1ˆÇ¯”RÞ¥'>NOΓ\~Ä7ðE~’úT*¬Ô å–×x/ ¨F„+$ÚÎïmn.vvƬZ¥°žz}¶½n¶RáÉ\ÎØI?ó2`P¹Ti\€ð€BŒ/÷¯ ¤ŒƒÎèq£‹ýÁaûëL8«X€?ôy˜®ûûu*Á¢tõe|8šÝ·jƒV£MüaZ5úÜÙ9ÍÃ:ÅWúÖès©ó±wS*ßô·¶%åíeéšOƒ¯ßÜ¢”¥Ïßî¿÷›©;~¾íí쀧ÝÎîG¥Ãöàî~øÛÇáï·ƒ”¢ÝÞ2¤¢~©ö¡÷ý÷òÁNu£µÂK;û¿7Û¤‡OF_š—T—ÑÅ-Jòðý¢Y©}%vQ‹ÓF½àpgD4iöý¢tÐ@Ä@­Ñìák·ÔiüRûXBQ¿u¯ÿ¸FñÙ󦨌O¾Ûl5¯ú“b¦Ã^»y°ùŽ+ mþzȬýžèè×nûcKŠU*µÛ#p³Q·Ô>K‚ÿÄKFßõ–‡~VcÀò¤­Ž0i+„³Q®R—įª¸Ä˜%LÏju«DЧ;)}iÖ,Ï·J÷s$1÷šŠˆ «’e.>gnz°ºÓw p¹PÒF^Ã=Þ{Ÿ½C}Á{t5¢`‹‡éôa2!uÇ»;;ÜùKs|ãZí¿g’#;Ž‚Xàý°Åˆ»CFVDƒaÞe·Yƒ@ëÍÎçÒÕÝ\ÜBô® ‚‡ï Ik/öê’çþïÝ&ó›‹Áþ×½-l\îú7(R|lR>Û GãÙŽàœñã¹&ùëM|ÖSí›þòœ\c$ÚÙc¼o`|=˜ÞWw¿m]m+[¤_`(Ì–Ä<{ú”m·—>npç¬í´ö(mi2l7[ øÏmëb«à–º³·Bä0RAl h ?˜Î…f|>Ú…6?ð8І$åê&ê{O¸.2uá|µÝt‰«›ûÉÅÕéI³Nï#Íl2À k/í|úJ݃,:ÝŒl£ôáC—¾ˆÉHC…åóó{V,÷­VÊpjk²Ýx"®úŽVP¹'iib*Õ ³_+wª³nõÏ}2 •ýê‚bjC$éù-JOa|ïV+œ¥Š0jŽïRÄDâü>@}éðŒè÷Óéüþ«ôçÓébss Jw‡Î@¿ ý¢”‚ôn (/Ë/ŒÜßä|k¯tùx6#$“p“°5 ´ žfÇÔüp‰•híH>Òœ]ˆ€ ÒÈ8A‰™æ£_á®HØIxOè~ßÚÒ” ´\ KyИÐPjPZTwÏ¿•ÑÞ·†wŽß—·Ž\I‰çQÝaŒH’ë»Ù¢ú­Vê‡ÍÁÅx<èuö÷7+ô+å\úå™á­Ãbµh‰šîoÕqýHÀ5•߽˿Rê:ô·3:ÿcoÐÝ­Ë M)!)Ä×}Û¹2˜ö‚ל[Šë²*¬_ˆ®TA3 ð %DBèIá²>™¢T€ñB~8 .䈤pÚ“óaJ£ïJþ[œ'H:6¢1 #DpìQßó{5LTrî‚÷Õ*t”DT6›O&‹ÉÚŽð=úÍp8ÁÉ<¥W}–´ÆHw{˜M'%40yuÆC0WþW{Ã)YƒùèôK©» /Jx1°v.{¬YèœeªCFŸ }3<4à§¢Œ¿ôFwôÝÉåáU³µ]Ùj€ðìá‘ïRÙ<çóaÁ{g¨Ê¤(_`&:nÑɦòb«²­½Y ¦ôƒnïÓó§Êd ˆÖQö“ëvi›èZ½yÔ½úí#œ‰m¸5EJ¥­£ï·„¸SÅ©õvùØÃ¼` YquâÆ[m ª£÷ö0ÞSö;­‹ýö—Ž¢;«{ù¡¶y¸uØιE¦ƒƒÁ~k§Fú ²âZ;¹‘ìaÀûÿ˜”àœ­õÆx«·ñ¾wã½p;Û&Óieg§Z«3•Ñ ‰ßÑ9…+EeŠ'œäÖ0lBbXйÁJTèÉyò 9“=ÙÀkµÊæÊ€’°âP[³€UÏÕ¬yÔÁ·€@½þ—=QD‰(ˆåWÐ{àÇíµ+}ßb”Õ¡ü§ß&ï·ü Ê cÔØ`ÏöþÅí a$Ï 3–耘„ÄSRnI¬ªnžë¾ÿøþ½Ä›wk‹Å‚ÍE¿QÛ£Èîè;¼þ¥Úá·î—÷âCöÏNªÆ> *{û Ú೜=šG—Ÿ;«àH’ÿíÁì·êë<h°Jȼ¯yêLp€1['—CPÉåôrÖxÇù3?iþ°MßÂ`š˜äüðŠŒ1Àû½Z›¦>ŽÆ½ ¬ùöР€Ú=‰c8Î#–“ÄÇ_@±_ZnÂJ8gâÆýQ·ÚØÑy’Óo÷»hZñ r>Þ2(¿WŒМU3ûñEàã§³Þñnãápg»&Û)V€¾®¯k¡U[z$UV˜*™ÛƒJSÏ%Ö…õ£›ÑÕÅÕ9-ÎQ¤Àk±ˆÌ×Lh8¸Ä´•Htéæ­š‡ÑùZG¬\ù•¯Ë,£ºáb[=¤¾vz+2Õås3’¡®ââ†?g§ÍØŠÄ‹!c‘Æ0¾yöTÍ`< Îøò–Œ$žB²ÚÔ¬¦ §7¦VSì긷õ±t‰©#×ÊÜ»#úà_°ñNÙtìÄepáôé§æeåc2››ŽÌõjïãäXXtÑnMno vVw+ÜÆG—+8{%iì¯þ-‘•ä8Q¸¬õJyskûà´µÇ4ŒöTc¡Ji+˜¯µës8»Á`°]¯È´”se²ÉV愱‚:›7váÜÐÞ_°mI§v/*bÃ9•/I5‘ãä¢Ö€7çh'èÆNMy ¬ã»Ö^–nR;ÈD‚´Ù·ø›øbi—¬6•©niñtÔÚúcÿö|6S1Ë#w'E>C¥ØdÇ×ùHó{êÊ,±ÛäéZÉi2 É©— -Ôq G¼@>’zÄøPµŽ†Ñå •ˆ.%¯ÏáDŒ`‹Åpø•,WªMÁ¦»@©B›BFJ€€ù´ææØÆg‡cˆ›ŽÇ4/_þöp¸•€ÓQK„Q¹y pXItò2é¸n$©¾Ù{à”~|¶µ×7i·©"˜ˆFþl§ZÖÌÒð+[ŒåªÏ^¸%±VÅ¢;ŠèÐWËï•Ûog‰¨»;S”°º›«gçˆÂOOÊ“&­g' A 6p‡ óEÏ• Þhk)‡Ž—ãÖ’YÂx`½ >ÂYsLŠER¤\†î"4™„dg噉îvœ£#„ö‡ÛÔcHORüñyô-ž&à÷)x¯üÞ¢ÅãÞZŒlLãÄ!LŠ ¾Áéï—†Š!½m?µ6‹úIÀ–ä‘ÖàQ(çè¶-Bv >Káî”CJÝðä÷”?#[ki£R ·Wòè%Îõ¼O§·õ]U'Ù/Ǫ‘Íï-Z<®‡é[!#¦<µ—§¡‚kE)k|%6Z€íŽå»äõÍ›ŠUãqë‘Ö Ï˜aÅ4ÚFÆB†dîËlÊO Ÿ³îizïQ?qÜź™ŒñfD§2L#¥ÙC[ßUu’ßåv|~Ÿ€X1Æ¿kÀâö¨œfÇ ³§´Xá¾iü8Õ¤pk›ÏºÂ¾î ÈGw£Ó!Ì–ø˜|\´¦“3L¾›†ôfD§Krs'+ý[Ë œÁ104Îôçß ¶=žßãu93ðžçJ˯ë~¢$OT&ã<7üá‰x¦Xþ“ô’fÙß<%Œî~B-Ÿ'êÿ"ñÚ œFÞÅ31 ~ÊÊgY™óþîä@'ॄ¥ÕD’T~«Ì×ÝšQ ‰é’vÜ2à÷nß­Xð}'<»¬Ç㘾Yýýtz,Ý!g#…žHô7ÿÆÛzÜv˜Lý<éž÷N»t߉ìvë1ýyi:ìÉ^lÖ*­ãvÿ´“lzbF Z/§2Œ>÷Ÿ®E>lãCaµÒbßʾÌë Ëãy®+^*»¾24¹Yñ¦Éü©Û^ÆÓédcG*SÛ-}uN¡÷²þ^Š'ëïÍú"9ÿ½ñè[ï¾ ?<Üé‘ܱö?O$"9{žâ ƒ$Å ÷Q"¡ ˜®‹7¸Hù}×5á*8%ã|Tá8ç¿r,ޮ܌­âŒ{0´´q/‡ñ]°×YK’'œP~ë ‰­^Ôôš&Ú¾Vž"ÕlüM ÆÓ¬Ø¾«è5ùƒ\Þ¶õÅy+xOO§iÞÛ¾ §#îv¹·xÌ…O””iiR-†Å{g‘¸2ÜQWG}­]¨^¢dqŒ3ÈåVbuô]ÎSóåiÊš?I©WŠU·È…覭S“Ú"œæÑºau-EŽ\+˜ƒ•8uc«k<–a¼ð{‹¬Iaô9¹ÕÐ÷{ùŒÄ¬Nµ&ÿ¥i,2Y4Š#S6’Ù_CáØêOH ×d´¶ˆBßËCiò ÔB6Áàt<Þ–3­S<îydHòLCèíµªnØô-´Îã½´l†E’®'4ß§ î¯U]ÎÛ”ÕQnïµGz}ãþò‡õµ°Rd“ôZù5) Î²4,‡Ð®"ûn€sÑ1G£¾^e-ʦ…íX"„Ê9P?4ˆ2l7p‹É|âõÒ¶ö3¬aYå‘a¢ü­,iÇGé½Äe¶x/%I¶6ÌïõSôì ¸¿–ó ­¿ç˜Ôž„|䂟›ú˜|œKž™FQ„ÓØ°¼´Âøa•ô^Œ4‡‡‡’']¨áòDʈo$(ƒE2kyÂå‘VÓö…³ëbßæ“GžiiÖªV—ï‰êd"½o;z×!½Û¶ÜW.ÍL=3à¦i¼ÐÅüž›Çÿ­7lÑ"!l( iÒQ*ªÙ49Ó î›â m Â¥ÒÅÅ…J˜ gNôJEäïÓ‹Òû– ÇKžÜÖš¿ §•?9}y¦¥y:}x¿÷“•,‹÷жh—Ÿž‚ßÛžê噸’Šîö­d–Y€„4žSò»À~¤Áš)Ö€ÕWÑíqØjíln¶Z‡ç·6ñïJ;J¼ §åMŸ&·Ü­ÖŽ?¿:¿'¢P}vx/è Ð2¤›ÀæÆ{o‚lnë »ù6B;›2ÿÜ‚¼•5£rsïj'IBe²¸¥|2™áB`ƒÜÝn·ÙÜÝܶNOéRE?ÞÈ.OZlüjá4¹eKÞhѺÚ=šÏêüÞZÅŸ£-«üÞõBF^¥ñwJ1€ÂÌïÕ7¼&Ÿ $%÷ã  ›híåšÞ¼`¦"i‚Dá4Ïr0epa/Q2aí>ìd÷ipð.®Ç+—Æp8’Ø<¥‚º‡ÂieK«KZ>Vr«Éùyt Ìï£:™®·¬-ŠýŒ÷Fz$dÆïfÑ«W'öüÞLHI#ú&{òpÚ÷lÌt‚öx-Ÿy9œ&ø!_¼©sxî«©ZÏã}WáGù»ŸÍŸíÍM˜‚@v¡<í·Ò¾›'>-ŸxÛq»'´ës·µk5¾7Wçk³ôÓÉÜTÃhï©øîÌrüä| Ë{’á÷^iøû´ñé¨oÐδ\:j‰By&!+’šŸ€Äx‰¹»ÉÓýÃÆû0nÙ¾¹™Ìp±;ÝJz¿ݳÊðõµÈG¶ÏƒôÔý”ßÙð–0ÎD<ê³>“ ¤úôççðÍ5®W§ {õ ¿§&N@„4¤Xc¼aáqNiŠ._*Ò´õ ¥·Ö Í2h EõýŸŸëÀ .‰÷ivww///±:r<·éjòtK•£ ©å·åI®¯‹¥¯¸/™°-Öó¶»ÌŽÇü÷,íd½Eú˜¶þ{¹TPÿðû„šöMƦ<¶Æöt¤Rå´^==³d—z½^ ^›ÃCÁ*ùët:£Ñ¨±½Ýn·ÑüÇr}+E$¶.©uL‘`*ŠÛ­a†Æwÿ Æmú~/27òtüžw›»=)¯Šß[d ‡ ¶ÌÙßãØ`1•°ih“„‚º |8ŒL%xŒbáÎGاA‹¢?Œ'üÔl6ý‡WŸ˜2[e×°EñX}Mš°çL~X&˧OÃx¿¿Jý÷¨ŠÞ+$7Ôå›­¤¼Ì|­ol}ž4ò•4¼³ê'M‚úD¢òÔjy.ù˜rž|ÒÓ䩯•^žš=Sšd~o¼^(és0w¸ Ha•м÷—9ÒPÀŒ|}û³‰ß‹Ÿ›¬õª`ø—¦wŽÜÕ(­Ž¹‘0Ïçò ªåæú ¿—ÃËnÇ8¿ÏÐO®¡ò{‰ cCx¯—ñJRï¿ÌLšù£Þ¼/uÌmHþ‡Èiˆ”­3Ï`/ޏ4iuY%u¦ý‘òX ¼Ì6IEÊææku}N¦~¯Ž"}*Þ r‰ÿ~YO Öc’zZäxKa‘þ£`dÍ“fÕò¼¥¶`x%~Ïk–ê§÷ß Òëåí¼'u¯Ž»;é¥'ë1}¿Œ£ÈkIDÚù‘2naâïæI“QžD«ûÚå.¿ò{Ö½¥úÉlAù½(}ïÍÍón­ŸÂ\ê'ðû7†.ùì˜zåÓê¾*BÛô±<³¾åGYo·Çï½Ò‡ø=ÉYNT¼§É¡Y.+—  üž^ –½ý$aµM«;£Žá©9¹cy.ùÖ›—2¿OÖOo*<Ò‡ð^]†³â°Œ|õ“Ã~¾6ÌïƒeoÔYì)²Æ3AÑørr“ñË!™ßgêªaö¢ÐÆŸÃh.gJ…½7Y=)Xɯý$èîõ=¢¾ihý³£xný‰ñûL¤ó{ŸÒÖõ˜Ü›˜Þp×â°ôŒ¬žäù½º2’Yô*Â"ÄBk‘Bš¯uûk—²¿±ZµÚœ‡Lª/~zJ¹2¿·ˆU„ÅúrX¿xoŸÃþÖã̱(£xâúA#Az†ìÇóûÕÖ‚j~ PÈ3*Ï•ù½â½ð—8Þ+Ò³ãA¸þÒždö[i‡*NÇ9Ò¯éYÇÁïï­>ëïÁ×AsÁʋ“<ŽßËxU ׋οÿ~;¾›/0í‹õJ‹Z¥|øÛq¿Ûñ¦ÁsÅX“q{{‹•âr>­n.ß³{âú,¾ÕÙckŠ`!D ÀTbSN•owHШR ç!cöÖÖ–=M?Ô=ퟟΕä“úVJ¥­ÝwòýûíônAsZ¤úÄspïC¯Kû}²ÿ0¦¾›N· ¨µ È  ôtZ†“_–mñûÏ,`èl6ƒêcä×(¹S»s T2´ÍþÃ}'ýÓ.ÚšwåÒû­÷ׂæÂ{ÕûŒž$¨Ÿ÷PúwïÞÕjµBï—5Pñ»JÐ ½¿»»£³†ÂÜA7ï—êçI÷ô‚ñÞï-¼×ó1Ao£¬þ<ˆâ}Ø#†# ésòûHu³½™K}‰„ oáNSŒ„ðßÞ~úôéàà@¾ ¥‡uooƒoüáð=Ô!?á-9šØþ]]]--ÃÊ X¼ðX ¬Âï3t•˜‹õˆJë›ûNüíÊï—ö¤`Ï‹°.類®³çúÀæ?þø.XüÁ‹TˆÁ¯‚ÜR2Ái(±×E;pñÛñÂPôÉx,¿â˜Õ££#ôá‹ûŸ>ŸŸËOø'ÜþÄðÙ”»Åhä±Zºþ÷"º¤ÐœÌï3tÕ-µLå÷b täûx~bfaix Æ— s8 Œ3’Šn‚wÈ"8ýýûw(.Ô×)k‚p# õýÐhHºÑpè±?ò&Þ±M_¾|ññgggøV÷ë×ßGåè•Þ£~(Æùs°’ïn¼w»„å%]‰ß[¤×p Þ£{}þü5ûP-Ö‡ƒ}>8 < $uEÅò øýÖ¸JßPÌ$ŠßÂÄÍ»ÝÝ!O®Õ` ¼”–®ÿ¥]z4¿çÒ,Ÿ-†úïýzR/u ­Ìï-Òk8 V"™Z»9.•@éÕ[˜ì¿_ÂïÐäæ5üÞî·"Õ+zB~oµË‡-Ö‚åóHõüOñ>IT,ŽYMÃ{Äã,¾á`ÀgO···ŠÙ†¥ ùL ù½´QÌ¿„ß{¼wkñƒû­˜ÙËÎró{{-¯Ý§QoÙŠc§å÷^x^Ñ@<ü@s·Ùì÷ûHssss||,‰u2¡A¥ÀàÅKƒ?Œ¤}Vþ­ƒÃC $ÀõµkxÿLZ½ä3ªôF—T¯ïÝ}†9ù=ƒ<1Š÷nͽ#9¾XvéO4ìækE)áÉá§ G*çÑÔ’ †‚b*#ÑÓÓSø×ß¿¯A#ý ©iü>M„èH_¿~…¿RGF›!²v7äO®žý}×µ ¼Šo½7V¯HÓ¸€nQZ–~Цk‹†ý9ºSj2½¡ÿkýÖ>û|„¼U¼¯+–q!‚ªªŽY)¢"7™…V’< äÚ6º$aqIƒÀc¬ÇLÑO, ;9ë_œÓzL?lE>X¾¿Ö”FG‰ØEž)a³t!Xî çûåJµ-ðˆè’(}ÀCrè'ë°ãÆþœÉl>›Ïù¹À}¬À¼þÁ¬‚ˆ‡åÒfFzzúÞ騋Jó¨ \H MÐ`½ò°^pû«<—é'ëpê<›Óÿ$ 9ðýV€wDÝ/îé9ts˜Øh¯JÛ3 %ᬅ‡ñ¨\È!¡Cų@¢¼¶ø×¢zåV(×ÈÔOÖaÒ7h<þs?WðX¿¿ý~;º¹ó‹¶jå“vÇñ{Þƒ¥ü)Þ£kbøèU¹À°BO*¡:ðûa’‘U?K?…ߟvæe•Îö;Ïï™:éÞFjÃï³P?Xº 4{=´ŠÅúMа¥Ãr9¸õÂ!Nê«?'XšÌ׊¯è9)=ïºZß{f/cJaiÆBk”ƒºËu¯_öøSø½[sï°=˜¯ugæÈ/ØÈ¾ ¿wÃA,3Ê.Š^…LÖ§â2aE[>þ~ïWæð<€™¯uH/¨¼§_3=9âíá=/ÚŸª^ …ñáB>ëÓâv¿U¦®z¿Pèüöß;~Oƒ8îWä÷Jl–¬—x‡òxŠ4Ù¶ŸC>â)”‰Ô¥¨oø½ cãüžñ~U~Y‡#»•Ða!ø…Ö%¦&¤ÊŠ÷ËYÉR~/«kVå÷~l .Ç*Î"òþBV«ëŒ 5éÙŠüžXIœß ³×ž´¿wûÛ þº>þZŒ‘Ä+˜"ñ:.Gz£ú•ÅÔÉœoGö×*ÒëjÊUù½›ÞÍÉ·òp²"M1JÕÑ·Uù½[yã÷sâþ$ømYT<ìÎÏ1+y¤ÏBO&a¬š™­ŸŠ÷2ÄOç¼´xïF»rvû  ‹JËù9fLí–+ËrÀÀŠ°Ê³‰èÕÈAUS)d맬ǴەÂþ{7Fö#þ{îI¼2'3ìÖHäMŸ'Ï"M¶Ìnù°†ê~«åúÌ× è+êóù÷·ãñ;ÎN½rÔÆyÈzÄ’.ýá׊°ŠAL/dÂàø‚uç!÷NO's]ƒ¼¯WJ;z²97T›ú„å÷yzU‘¦°x/SÈï×y~ïñ~î7 îÔßµï_zo~ÉHS”í%èàýt®­Æ{·DÍ6͇‰ôeöàY‹vɯn¾–=0Q¼Nî(šíAÁï_8g-,I~Ká÷ÐðMËïU”âÏ)øý2/Va _ÇÉὨ¸.>vþÅ{þéQüžÝØŸKRžöÒÅE¸ÉÚt½ñsRÞóþÚÛ[Ñ{Ì„•Ë›µ Ž#î~>"ß<ﬥyÙô°Ì™á´¹k®ø+$ðDÀfnœÈ„Ìå¼´<úÙÁþÚnw†³Ø«Bå’ýµÿ¥Zû—£ÿñ?¦ÿï?õë¯ÇóÿøçòÿúÏÁÿ¿>Rª¿#æáïç7’Âø<Aÿú¯ÿJk9çó?K¥üƒ²úóÿ(Â…Ö«VÀ«a­3µËôó?ÿ÷ÿþïÍÿ¸»W¥ÁaTýþÿ­û­d̓,dX‰ßCé妆9 Á“/p–#xŠp!‡õêtL˜ùÜóŽÁ˜ßoÞ3£‘]WÁúBtáæ Qô_&6º !=ŒŠ¡¢…Ùë‰VEØoê,dBе6Ý€¾á„@êNùô3Ø_ Õî·rH¯+ݨO°ÿÞ­‚È Ïqg“Ó¶îUô‰"\Èaí:À', u9õ3XŸ~ÏxnðÞ!=µd ø~«`½›C}F„ã9¹¿ÓJöÂ8„+Âzfh!¶~kÑYEÆüž(y’Nºušª«¬é<¨ϨïÎS OŽøs”æë|í²^%œÉc|ô…•{Zpç± ½'¯Î2VÂ;É™ß õû­˜ß—qœ¥òû=éAü¨ª­ÉÖ…ˆo;Ú +xOpa¤—_yý=¯¹~/òÑû­Üºdêðçäå÷®÷ØS‘ ~_ŒmžN„ß“Î3בUdY¨/ç;ÉAšQŽŒs‰9I§(ø}”ân +O\›…?e¶îéÞÜ·EWŽ>é·^‘Å~4¿—mBuBüž×xTðû(Oż¸fÏoß¾ìÿöÛÓ¡ZÄbÈU_…wHHŽâýJü^ D:¿'Ç=m ~Ÿ†‚˜7i4vÇ··â¹‚ÀeѸaÌ¢µpµ Âb €Ü¸‚VWwy+_OŽŽÂ)K’ï"OÉYPŸŸô-Î?òÖ÷v5›»HÓÜÝ•ËߟΠýå–áüÞ÷Êô>ÄïÕŸC£à÷KæîG£Q£±#¨ ÝžÍf¸=a\zŽ=l0 ;;;{„†jÂJà‚:¹!]R6vw‘·çzŸ®-Å?aU#Æ"=ÞÂEÖá·Ê¸¤Ë©ÙévDïß°ex¿—ã˜Å{Žóäþû ¤ôü~w÷#Pùô”.|f³K—ªK׬ãvð„ ztHÃJàÖDtAnüŠn€V” ÙýwÑm2ÊàÞ*›·J˜/G¶0°B¸òmÏŸ<ŽßûEœa~Ïž{ƉD~¯{[xÔ Ëù§ðß{~?Ž ^².Šð¾T’JßßëÉøŠè%#KJªÕ X±ø6a³¦³éRR<þ™Öú¿æóûÇ+‡‡t*÷«·<‡Äï3ô“ÎK㣎?‡yŽóß3Þ3ñqþ}ËïifÀÍÔFÂŽ-)§Tß*{Xš°ø¤¾ÐlBžH§k(]¼p}ܼ^¯£‡hJ\U9™NÐ&77ÆgÊ yÒ[7üÖ„ßZ,`ˆ:6z#Ü>v[ñþÊ?ßgè§Üg(¤ž]™ üž‰ú÷ÿž{’xIé#a¦NÂq ¥Ì݆?OØÖ}w—®Y‡Pðñ2Á?¡ú\]Š7w›<$(¡ÛN€ñN†è!Ó±|ÅoŽÇÈ•Þ*Ñ[6¿U.·Ž ô0!´@ìn aç ßãKôÓ“zá÷ê¥à½øï ºÿ>»'a¶ŒûpÜãi~š§­;\4€a¸Vá{9lÕë?~†'''"™£“ v‘[|>|ø—X¯×oµŽÈso$‰·&î­m÷ÖîÞîi·»½Ý@V4êxÓ’WëJ:L{¯R™H°–Œô9Îïu¿Õàfê&µ6¶ëåv§{Êû­xW‹lºJÃàÂC‡.(Èa¿_¬&Óa?u«Wy~¯>ÕP TÐqîB>?Šúq~¿õy¼êIŽN]™õ˜‚ônÑšÙo•Õ«œ7T½økíÙRªO¬°F¯âóµKPšìçkƒU™¼þ¾à÷ÿ~-éqüžÜ÷¢ô2Œ ñ{í‚ß»Yˆe_¤õ~¿OÅ{îÖŸSðûÂãg`^Ö˜äüÞ­/Žá=³saö„÷2G˜íÉQ¿~Áï Ÿ§ ùX~}ö‡šºùÚ¿—µ"¹ý÷d<‘Ÿ`ƒ…÷,EVÆf'1! ÜÏfØ4¹‰µ,þ¼ 좄âù±¢Õ"$ÃöìcÆ}%Žßgi)î;9;=½Á?)öî}À&æ„õ÷›õ2öçóú{^y/}%9Œ=ÿ³èú{^¥©….žÇ™ÌîïØ•[ÜïðÀìBVÕéO0Q–õ÷5Uå,ý,Éúû)]’¢:Üàõ÷„÷ØøC÷ü°9@&Û›µUð~‚ÍuÞ¯dÙ ô8Ä;' ˜K’„ÞãRÙbA}‡÷C‘^6Á¯'Ó³sà½~Çã½®¿W~ïÎÏ´þ{Yï&\*öþ{Ëï¤÷…Ö=¸ïÿÉÓ@\rÃÏüÍÆ.xì’ºá„ÔÝèOœßgè'ùï¿WP×Çr2¸ÿžéúïÃònàÏñ¦\×x:F[Ä 89ìrZ‚€3ÞúB’ãªùªoôÇé-\ªŸrÖ‹õ߇ü9Ô‡éÅ«£ës²{ϸyÇÑ?>5û™‘uDZŠP3è% á¸4ReèôÓï·Jd"Á¼ùïEŸ•ß‹†‡×ß+êËú·Üjýá0½õ߃ÚõÝM0q™¼=½ÇYn8.z¼µõþ·ß~“!_âÎ}@<28~ 4Zøå ÅØ?eöq¦óßGt2¢·ì¿WYÂúœ$~/®~aöÉa9,Á¯6_µ¬˜ÛÞÜäƒõè¹ËgËù^6ÍÏ~{zÿÇàà IG£1=ÅQk$›ÏïÐ= ô88΄g³9â“T?ؽíWb*/rþû ý¤å9üžé•ç÷¼TAÖc º ËO §ð{vb²kÿ†Oö~û†3ñp|©á¸š†ÍÙÏ~{zÿûï¿ãÄOÔ ÎÙÏŸ?ǬB0è$ÃE çÁ]ƒœ „³D5>,”DÝP~ÏJ³T?Cüž3­Ç ø}°þ>»'¿ŸŒ)=2Å«èÙ¸]¬ Aª#³æMò€ ò#âÆ$Åþ~ö™\^^HÊV‹âëµoOï}Ð.8Ò‡a%²vØÃVËþŠyOèCÄ>„ù½Ñ+·ÊËÍ×.a%Äì…߇çk-¿×ŸÜúœ,¤ @xâ÷ÉÈÓR¯¯¯qp¶ô` ͇e7Æ™˜ƒߎñëˆÏ–çí-™KüŠI4>çz‚Y…ëëáÊýþÅ«¶oUïAZÐj}´k¯—XGØ `ä'ÄЩæ/Ìï^y~ïý9™¬$Äïãx¯;­Bþûâ÷@bÏïùˆ<œ!|.=ÐaÙ¨ã§Oû£ïàÔÈá`ˆ_‡£ÑÞÞž Å@¿onF° '€¦Z•4kó"ãߪޣ™Àppž!<^G4+FzpÛÛ^0εE|Ä¡éÇŠÖª Y¢œ×ÉïÝ¡±ëå÷0d`~ºâ¢Tºçe?Ðæ*¨Å~³9à `>>ÁUE è ªxA‡ªÎa @–Æã·1x«z/üûœÈsH¿K%àNýG‚««+„Ѧí6'jþÖËï%ãd~/^üÀïn‰ð^¼iÃQ~ov]ÙÑ7*æïð@ÍüÙðàî2ò¨Öj83'ýò¡‡ =uÌh ¦Z­!=~m·;S Cããcéñ‚¯7üöô¾K¹mÌ4~_¡  òhwZ CHßA1Ÿ?wcJŸÐ¾Š÷¬^2þŒèdDW=¿—Ìõ€äÀ/þœˆÿžuTz‰xu"á0¿×åhHÃ¥òó¸ô=8­PC°–JÀ~¨8"ñÒÃ&ôû=0¦= ïíQ>¥^äë 8C!+˜3~-ñ!`{}^\C~æÕ‘»èÿÎÏÏ‘ƒ:`Y½VC1‘”ÂïU—Lûªðu2¢«–ß‹öJî|LAú0¿ÏîIÈ7ðßSމõ!FeŠÇð¾­vû=–L®}<–4Ðr¸vpå…wwæóãé]ŒýÑm0!€Ó®‰ßsz™ðóù¿¶0Wìmý¾~ý ~A*ô8{!Úq{û: N3G8Qm*š¶T?Ãþ{I¯Zêü9¬ô‚÷æþÚT¤—^åz¤ôËPï¼™L­}üÖÖÖÅÅ%r©à®<–4rX¶¿Çχ‘3fÁ1p5ìî6$=lE$û­~[ ÿ˜Ú Ás0~Ã3­‡¤·oàELc"¡xRLÒˆd¼‡Ò¯ƒß‡PßZ€"ì­Óc4åͽƒáo³¹Ÿ¸A˜l#¨¯Hÿ„üÞ-ç§^’ñ{·Eéé^ïÍM<19 HlÒ“BxËð„Áú2–¯¼9 _¹BÂa”)„õÁÅóµú)S«~}N2ÞGù=—Öï·J ‡ø=§—Þ âN·ávo~ÚpЃMza»Ú³‚°T Ðñô)?sˆ ޾ }@§pü^A9UWƒõ9yù=)`ÒK¼Ìì†XþO€Ök´Nhc´=±V‹»A†` ×ÓtɬÈÖOñÞÐú{^ªŒ÷1~¿¬'1(ùž÷ó õÏf^T}Å Á¥Ïf%ê¿w3b©þ{òê0ŠçéIiü~ˆøSñþBn‰œÞêÀcù½tÞtÿ=¹2å|Ìe=)ê¿ÿ xù‹B>é$?Õ¸È×wu~Ï3©ùü÷¿ÿ¹¼L¯ÆÚ<Žß3ÕI˜¯-øýÏŒ ¯¥î¿Oöïã,OÈë÷¶=¿'Vä¸~$œæ¿ÿiyçkAÍ×^NËï3ô“·FÉù˜«ó{ϊت IÂ…ÿþÕpâ×ô%w§T6ÆtR}ö6^ü÷ùùý’ž¤kâtæLg‡xX–™×B땃;‡o©~²·ž÷×®ä¿Ï@z?.–%DÂ)(ÂÁØ£ÉÓè†j]ºG,€®ÏYÁø;én ¿·Å„Yéݾ§ÕÖ‹j…<³ä¬ÏaÔO×Õ~ïÎC¾¹óëïõ<äÏ'z¾‚_¥ãÏw1ø$Vžaó,¯.äQ®òH&TEXö%Ékp‚@qþ=÷îÐèCøð A,°tµäø]UÓLrÖ£óï§¿u3ÿ=žþü{AGaÂÐtì¯ñçßÇëPÄ$Jë ‹óïÓtÞŸØ+褙Ñ畈[%C?ñzç¬Ççß §¿ðù÷7wÒg úäüû|x5ÆØ`FxÏ[ÝY[ }:ÒCLÅù÷€(` íìlÅ­%4Œî;ÁÆÜœxÿ¥Ççßk÷ˆž¯;­d«¡®ÏaÎÄTþ 'ñ{µdx,á¼E˜% ã«âüûRöù÷XŠœ®?Á®îlý$½]Ù/ë•Y×7RÂdhÂç&8¯½U„Å{—Cqþ}®óïY÷d÷ßgëêjþûL¤ š¯-Ð%–ÏÊ9ØÏ°÷oú§\çß§ÉÓxÓ˜H(þ1þûÌžÄ'ZEü÷Ò§ ”E/7òÆtÊ Gèd×+÷ù÷iº°Œ4&Ä/°ßJ†¿Ì\ÒÎÏY#¿ÇI!8ËáðiÈ¡Qiˆˆ-èù’sXY_dzOóß–âã™&3÷i«œŸÖv/˜ßËaß8ÿ Gá8¡»»r¤+þy¿wGe¤I{÷Ænµ·¤ùht´uƉQè+œo-dØZz–‘=þ$Ô'¼_i})›?%ñ{¹!‹ûÆùo|"Ææ{{»°€³{9ÿ~û¨ÕÂé÷’žGÈô®ÜÇ Ãààoœ ¶ÓhQú½>’°ä ¯N’ÂÁâH#GÌ""Æœ2Á¹´hÉóà€Îé•°³0Ï~Ëü>ïÑ%V8ÿ>­Vâ÷äÏÉ\Ÿ#“V~­à𤴰å÷’†O\£?èÜáá×Ç#¦×ëóù÷£Æ¶?š=µô®[í CM€%———8l°sr‚³Ó./¿"ì˜Ã[HðíÛ70+*(ßÅ, $ ”×8SŸó—£9myž?œ}†žÚ+ d×n…óï.EÛËx³õ“¸”ßûùZñßë~DáÒ²71Ö36yñ›à(Â`ó˜aˆÇ#“ÌŸö÷‘pÿ’†‡‚»ì) ‡QS»8G1 ñÆWô»‹´z}Ðlê ˜à€ÒÃeÖØÞîÂÚ¸²Ùrþ%álüJÕÝ;£vøi…óïÓÚ‹1RÏÏÉÔOYá°âú{õ9°?%lNÎYà4{®9ã4¸ Ÿû#aÏJþ §ß—Ë8ÿ^Òp× 0>¦òëiÌȶ„Ì)µ-Äa- Â|²¾ä‰ó7‘€®ùí·ñ'*kÙ"åù âߨ?GT?ïñSþóïÓÚ…Y†ÿJ–~’Þ®Îï³^,€¬D“ 4Å9·ƒ««D4¥óïé<ålq&WÎbÐ/ò?Ec ¬eXj%àPƒ• †­OŸp‚~Ä"ý%Hï­ÓkõŒòg[³üçß§µWˆe¤0‘€¡¬Îï—õ$îÙ¦ç…Ð#WÜÝðõë%läë`prr"=8~þ=_6¡ˆŽcÑ/W 3/_' ï ~_púW2ß²¿Çú¿üéø½:Heå´A©"¬gS2ùaÝ0,#ßúÞC"‡L¥øï•ßcüûÈõ÷z⎠› ‚au%2ùaÝ0«ö„DW0Þ;N9á>CÃï1þ]™ßˆ^ úóèÀð{]5†Üúœæ÷¢ˆþ<:°2¿÷{EܦYäð7òá¬Éÿ<=¾@ÖŸYÎ+óûÀßÇ{ÙT^ðû‚ÿ0ÿ~jÔ¿—õ?þäd„×¶>G6—9%p;½ïæs>uÎß|²zuq´[ÙªUk °_uý}÷ôt2›ó)¤ú8ëïÁy5···W£i™B¦¥ îü{ì„’Ó¬eyRBý'6c믥ÃI8ÄqÉWÄWFá9@éÛ_õ*Îbg’©òÕ³Üò˜ñt1½_œT¶*Pƨ.•ËU¨þh8¬ÕùüûLý„\;gýn§;¡SšôúÝÍòÖÖN*ÞŸ~ÆvØÔV~–?ÿþmï[/Êž\ÐN¼V³¾U‘£ÍŸó›õêdÊšòW”m<_Œf¥Åüì°»à}äü{I–¸+°ó¥'x/$¯ Þ‡÷[üþYøýd¶hl× ôï*%ü¯^)Kà…„¡ ÏPžÒâî¼Û^̦‘oíÔ+0ƒÌõ,¦È\Ъüž=údx|«ký?'´>§ðßgþ<…ç¨CMðRÿža¤¶ìü{!÷ɳþ«ùï乫àIÿÔü÷ÜØ«ó¨õ÷O=ŠKù‹béæ—§ýÏÐ)³Ï¿Ç´)©gŠgi5ÿ½?/ðâ“o'ðßs_°çc>bý½ j×*ÂÂ9cr¬;ãemŠæìõÎä‚Ä?œÁ½nÛhà$9ŸáFÑ;øU"ŸÚ5·ôü{‘OŠ.éΩüûk­¿ß‡×ËïÙ‡Ã=u{sLJŷ³½½É1Aš´pü]ÉsÕ|^NËIû.æ±¼<ùÓ䬣z’×Í'pü<ØHCë««+œ‰‹¤‘¢åx"GÊá¿"¬T`m1šQžóïU0N—ÂòÒ²Âù˜Äìc¨¿&~¢èÕÑŠp®¿:´£³,]çT8=‰Ôwmúüù K>ý8õ[J³—•Ç–-;œ³lODîùøùoihÝïÑ–p†àˆ+çB㉰Dâ×~¿GxÖÕëëk˜ˆ÷µžè0Î&,€)8‰Q‹¹m½Süõ°™!ežóïiY¤Ñ‡Hxu~Ïb³Kk}ü™†{§;á,@SÏØ€²@"ã³µ( ¶Ðî7qØ·‡Ö# ÚlàŒ!Ôï":t„­®PÁ!ú8áÇâ¸ÍH$½·hãÝÝø.¾. Í€|$ €#K‘ÕQ똎YvV eÀAþ8zÍÙŸÃ<—MN}“2°Iµ$OÊïÓ:O÷‡U!0™ŒQÎÉxŒ*‹jrä”ð>œê‚» ¾Ïfxz½ïv:8͇_ýiì_¾|Á‘½ãÛ[H˜âþAáòœoø}‚ Wç÷T jƒ„×ÇïèG>ohÞatƒ³ê¡@zþýÆÆY·Ûj#¢ôïâÐúOŸZ7£1Ž<íâTûL >td û:‹›ø¬ýó^Ï[ Ñ‘'Óyù‡fR;mlÜL&øù 7¤GIp; Ž.Ä] {d%ð-x@@!PfÄà[W«µxÙ$½”AÐ4^±rOÄï‰ãêÆ1@ ïFB@aÊ_Ì‘Á>ܾ(Ç©øðG[J<¸àœv:;Â0ëèäùà•ÓSM)NžóïCü>jŸ†ßKoxìúût÷ÜÚ£8ò?9:BûCp½Ü&£ëë¶q<24Ø£& ‚ß÷v›€+APkI†Ã”o}ØÞÆ^œÇ« f$>nµpõ Úéþ~¦;J¹‡IH†.„0Z|׋ápƒ®Q‰x dŒýKË&õJDý'â÷¢aiüg³{B‚€œD-g©k‡q‘‹ÒÈ@­?~üÎ#‰’S«a]Ít’ :í¡Å·òœ¿v~/ZmŸQ~Ï‚[Ýã÷$›HOuüùoTi 3Î 0„¬8Ó…½îÝéÝà¹yp€£Ô,rû±4Ê û}p}P|ÌÀo)rKy¸¥µl–ߣ$ @ø<Ês8%¨ª º(ÊŠ'ÂÈgiÙ²Æ \ë?Óø=Ó²løC@8ž0q‘È¿x£“Çc´…žã[*¡™ %h°¼7®‰eÖB¹º|I‰z~芞° <žú+/tÃnÀZAS_M¼u;™W8æ™I´> ÝtJ%D`7Ä‹íÇÑû+AßbÈßmîJúIJyK•á­"Åzÿ½hX­…(’¦~ú-‡ÄšMü¯ùéÓ'Dâ)£vüáW¡ì‹ÑÜß?étÀi€÷ÞñißÜ\#ÍŠ…‰ò Ü¿쇅DnhЈÆû.=ÿ~Ýüž¾Ìš€±†£þ{é +ð{™&*K\VFxâ{€‡ä4€¤Azb&äW¹†7FÒÂÂó [IÔ„ó qEP‹FÁOfó9âáU@nÓ['Gè-t?n» ^)ƒ|Kò÷yÚ0°çè¨-ȇöƒÆƒçP™™ÓSJêkZ/¡õP›Œ²ùô’g¼<:p”éòˆ†¦©ÉŠñJÒÜ[ð;ù  å¨ãh4\—?„‰?é ¤"á/¢“@ÈPh.7ÆÂ|¸ìnV²ö[õ>ŸHÉèUø +LõÏç:|Äè“:@ñ,$ð4€ÊAáá³Æ¬ÂjüÞ’¹~oWº)Þ êÏBO#ñÌË×ÌR¯'ÁT¬ì¯Õµœ?´>GÏÞaÇ9rxÞ[ÍS.dò:àh ²–<÷×ú…ìÿ$] Í×*¿¼ÿž¹¬öò",v¯Ãšå ïgrûï…ùU:æ¼4LÓ¸Û´W:“ìqz94à m‹°ÇûB&ëÓ ’¥YI/“l٨ϣßÀŸC·z†ö×âü]†J\hùL˜cWÎ8•¼ƒ¸—óiaït+d¢çe¯®~~5~便ð{‡÷êZÖ“\ÏãÅ,Õ¼ÿG7Tñ¼å¸ÏZô8 ’¾éÙÅ) ï£üž¢éÞ¯²>‡;²Ñ Dà‰W·*,/¿×ûBüÞÃÑ#ù½#3ªqP„+Âbñ 9¬C:§Äë¼o'¿÷£Ëï¥eÅïusšâ½úSû‹°ØÀB듃ø õ¨K7ÂÌ‹Š}à"¤ùïÙ)Lÿæã÷ºcMðLXW.äð4: sD”÷êüž_2þ{êžßõs¯¿—ùZ]'mfÎÂñ\ZÏ´ƒ òä {¬sÌÔòHV.å÷À{šÔÍu¿¡;¦»LÏÎãO-Ò,Ÿe\jisø­ßªœIßhëzù=‚Ÿ×Ï=X=ýyüýEš¼(•g'ÑÏš†´',õä8ùdñûÀù²¿ÔÑiùü©’19õc¡"œ¹UV+é›ñß'ð{³Æ`5~OŒ-wÏ+ЮÕsë@~/¨¿¿/Ы°`/[tœè¿ÀV7†nòâ¯Æïs¬ ËÏßü܈øÖÛ%‘ßëþÚÁÍÔS;5Ë‘ó~+·’‡¸²Â;\0¸®Lf\»ûV^ò'kpnv ™sH%çiYZ°#ÓlÎä¦Å8ÄÏö-ÝB©å\¶ííäãäÚ™ôöÞ©Ý·ùÅMËz­`îtq]rúpJÝŸíÖÕ„ò×2äÞ_;—±Ah-°½MüÕbÛ[y÷×Ë×Ü´n=á­†¡MÁ‘Ê»-çaay‘ŠX¶¥‡RèšhÊHšâŸ?,RÝȉyF0 ¤gõ R&¦1ÛÉyÛjhU¯f%Ý`0ûk5}©¹mö×Ft´ðû[µv§gíÒÞæ²ÌÛ;‹ôK÷42eÊ)=?g2÷«öƒýµLì¿§´’ÿ¾à÷…GëEë@ªÿ¥ö$‡FÀ²V˜+üÐ+û¡sÏ8²}¼l3ý÷¡9W>VÝž…¿¹ð®¼nÈöß³¯wð˜w¥õ9<%¾U W!‡©ºß*î¿'~Ogb%=±ü‚ß¿n„{ëþøU-°®ÇäѰß_+þûÑ`4sîÅîVþœÜþû¼#ëçñTàäB±?RÍ×.—+ÞÓ·ÔS!›å^]¼-dgH0“ë“u>¦÷ß‹ê¯à¿ÏçCÍãgýÁ4¢ ¸†Ç&x5A3*·å,;ó‘‹®0ýäÕTò‡ ’R>ݳçcÊ÷ÅOçç;~ÏÏ×ÇïIéé. (½ß8¬FÍô’cPr½d“½nÔ"I\çâ SQúWZÓ)¹“˜xîý!º1K-$Ï+»ósPå÷¬ú bù+¬o^•o­=ý|±¨ÕèTh§ óWÆ… ¨Kš|æó;¹ìÕd:÷êëû¸öÂ¥fRþ1X"¿¼§Þà=yu²P' þªxÜl㑞M™ç÷¯) %À¿tKO¢W„tŽ~wd!wçó›Vk´ÛîìŒvwñ¿Éq{án |u² ¤”Ó{¦þ{[d­Sz6²¯ËOêàÆynÀÇ^)\-<Ízý+½ÆB¹Z>OÏ`Τ§E5¹¸j¥zÍ..››óyµÑØlµê5\¶>â¶ø©Ü,ýlõZß·X*¥å¨¯þ{Õ{OÃüž±ÿuñ{wŽ ã|€”ö|•¼áétvðÛÙtQÞoÑÕ–²”îótö'O>ÁnÿDËé±*gž³‹K\± u¯moãúµëÓÓI¿?»¹Á?k{{“v›Uÿ™ë˜GKÒð‚EÛÖ9ÎÇt+}ø½ Ë«ã÷Þ#kø.[-ÆK~æ éÛgûŸš¸‚r«Ñhœg¼;™Lqc!.¬á>øît:iþÆWŽâÒ̬ò¸2Ë‚êd$3ñÔzKë8:éTp}\iãözt0mžœ }§xnooTÞÝÏhùm¹ZÓM½ÉeCÐiqY%´5…p¾|é,ýnž²ýPgÕÅú-_%°„ß ›Œ|UësЃý{³ ?“ ïƒÛæþîä®Ôîöz|p|¶³½]­”AuÐÌ—ÝöaZ>|înÕåQfù°…›ÀiÓã†çããv÷ôê’]SNB£4$ â™Ï-Ís1™àªÀÙx¼Õ:.×jÀõÉáay«Ž7áhzsƒ ±ïÆã4YáæqÔKnºEMÑÏÏÏq¥6É\ïÑ¥ÂuÜØŒšJyðOX6\‘ 38 ]½p zvñÞîÁƒœq'1dˆðÒº„êk0;Á*2¤‡â³ù½,Á”Þóºù½¹AQp…”ÉÙ„/‡ãÍ:µze2AõŽ¿Ô7ë•êÆì®ÂÙàòëU¯½MsCÑw%º©¸Ù´yâŽbDúôh`\þüûï¿“K~YyŒ]ÊÇï¹iÓÊæã1x-U7îç³ÍOŸXé*µjùvº¸ŸMÆ7Ínwãá~z7}˜N#òñåBÓÝËIßÂO¸pÑnávX—¦„¹7ü„[Ë»]¶ ‹EïôÑèæòòéå[x½ÿD&¸çyi]Be`¼_ß;¼päRÿ½ø-ï_•ÿÞ36ÇÞDY…É%„›µñÍÍl:Åž@Îx2®ÕßÕÈ%Œ…ƒ¯ƒßÏŽ7á&OzWò¼ŸÍª@P“¿\tì¿ †z0 ÒÊoÙg¿wxŸQGP°“Òýüþîn|p@ú=ϧ“ñd²{zZà ãHr?§fO‘j„z%ÊÊÚä;ʛͨ¯¦Áïí6,.¦×‘@¹|quu|Ü‚¶Á@Ýå[è0ƒÃ œÝ^QY1Þ¯ÀïÞ«WŽZró{Áû×ÌïQÃÝÎÑN½ÕĤèàõÍÝZe mŒž0øzõûYggs3í]‰Ç ÉmQÿ]€=Úx8ŒÇã¥å Ð.É,¿Ï“'­Â’¾˜Nª¸7tq7ž/vOÏë\]Uúªtk²¬ª®3Gå Yÿ@waÐ9hù<—¢y"‡rY$´ ¤\ÀTã6M³iv{EÞUÌv,¿à÷n´.x`¼ñði·½Y{7™’RnlTAï0¤¯Ï»PzЛÀs’J)–»oï d¿‹ÒœžŸét¬DOË_MñTx„c(_^G(4!:æuf˜È å”z6]ô{¸6K®oc÷ê•ø-°þ‹‹ ¡.å,d mL¸ÞÀäΦxž±¥íÏß{·~Ôÿvø½nÏ•©õ ÚÂñýóöf®›Q¥Rãá Úmì½’ÓÛø“““^¯?¸¼$:±X\ô¡ý££“È»hòîÙÜ2ÖŠ—!’~)’¹¢uI,s£Û—ßmÕï¥wåA©Üèt0i5íõ¦{¥Ù¬ü®<-•AôÓÊvÔ:Æ@e0¸Dñg³{t°yùL%bêÙõ:Üß—Ñ0 ,Ì | 0䆵F“ñ¸Ónç‘y8Úaìøÿåçß»yé7Ëïû³Ãý^§Z®Ž†ýË‹^¯û™”>ß»à9ÐôËÁÆ–z0öûubüÑï‚ìï –/)[*’9T¦A^ŽrVªU(úî’wåÒ¼ô¡¼¸?ïÞî6ÊýS çÊ¥›;pžÓ|À˜Ce«onžŸŸ_\\a%Üǯ¯¯1^•:ž|þ W x<*ˆƒ30àaÚì™wncb‚GÞ¡´;¸ :ŒäïÊÕ^òn¶Uį)þ{ÀêH†òÑõ÷—£ƒyuvw^Ùú{KøH(,Ūʒ0ÄÔ¿loÖwDé—¥Ò4LYË`Pº0“Ñ̯ÅGÍ0X¯¨#n´Ø¨¤òß\\;úbþ®Zª>”*廇fªÆ‹òÞé)üˆùeõ¤uO+<ž¼å ÆNè–V>iá´õ÷cx´’ÃYø@/çOõî0Aõ¥a„TÐ3GIZ‡M(}Îôyò|t”Ÿjánn’jxù“9g¶ÀÕÊ[Ghvk8|hî_—ë—‹rÿ¾4ªmÞïîÇø)>/A>® ÔÖΣŸã$‹<þ{ øs€œËGÊËxUîõãi (0µL:< ¨çÁO†?xœöDÛh¯^ðp”µ@]¤üqùoT«7××ÒÏó×±‚•½ÞÉxÜÞ͎†C,œÀLmv9Ÿ¢ŽùËœV6Yuk¥”GXœºF×óû_ȇóÒ†èCb)Kz­ó¥{úu:ÊÓxTòÃ(ðž ÒHAÄ×õG®éíJ{©¸÷Äå,»ð_Ê+­æ6 Zb¸AiÅU!ƒ ìœa±;™‘ñRgÏK½j~Ï´`Í`‡±x‹Õ‹ò*ÂpþÒ×à\‚¹uçx†÷¢å¸‚³»)6C{W¦÷¾¿¦ú>®TJu’’"=P)‰û’™ßŸÞLç’/în–wvÉxßýÒíž´^&º'÷rÑÒâa¾mËž¨ø*Â(±´¥2‡u½Ÿù*êøã킆 ¤´ éEOÒö×êyÈ8Ö»3;õÎë<“»´¦¿DJ–ešsœBµ| ¿´#-¥ yèÄ3¦ÉB÷8êëù˜ØT¡þ®rè|LÝW(~£Wµ>Çzœ^±Ò‡½7h¿åž´9Özú Î¬åò±÷[NÞðúæüL™ØŸ£ ô2<6¯Å³T”3wå¹Óûì¬yùÕûï#~î\H™M‹4ÄCs¯R¶~ž[H°›ñÕýµfº@þùï ”}n}m, Ñ/xïxÍ+ç÷ù9ß[@²Â"å³H‚›õ9¡õ÷¿/¬Ç[³Ùü^vZ©ðU­Ï)»°rY:°Œß‹?‡Ÿ¿J˜cmw‘æå[Hå÷²°5OÏïuõ«ößÈWX¿˜Ð&ñäèZ×øþZÁûÂ_ øËGñÜmDê.H/ª/aõß;~ϳY¿~ëÂÛ£Þ]€à=c¿_Ÿ3Å¿äTd¬CÆ®ŸñÍ^|9K§xx~ ÈòÐwͪ‚`Ý‘_hàWÒ»ºÄd{§y°õ˜‚ôø“õ9´óööö¬¿¯m”k謁÷Á^C¾J˜ŠÌçè# ¿øÎ#Î,)gZøÑµ°MøÔá¿^ڮݩíiæúžªKº‰$¼ü^Ÿ›Ý¢ºýî':ü^×ßÓöÞ£vmgwFÇLSRAý`QÒæG—†ªfïJ …±Î'©Úصxxž%ìrÞX¬õ)’–<ó‡ÓÚ'žOFiIæšy–ìÍó/(œVæ•ëTÝ3ìïUˆë‰@€Ä;7º€ŽŸHO:Æÿ!õÈmY»§1žÇÛAzüa›ÙíõUÿüôløÇ–Ëápt‹­åº4-f1–ÄS7pËb»”}Iï§P—pV".oH¥Û¸ðZ;Ì ÝÏ–AÊœ‰mA’çÑ|WQD)[ZxUTvMäò¢üU‰*;µNÞ7VîˆîIžÁ IWÔåªïŽ(—6kå½]ùêßp*ŒÙªUuÏ,ʆíjMviøq^› ô|'®U =^ž¨TF:=¬JÏi‚pº­@k{K’?,)³ŸRN[*¿”œŸŒëþIj' &*øa× é[ËÂáÒÚ’'‡¥üÒ¦á0J‚Â\uaÊ|¥H°c؆Žù5?ªè¬3™JO¨oö ö§<93œ‘U-_®Àh°yå¿üÇüŸÿTþ§ÞÿüŸÛÿößþýßÿíÏ?ÿþù?ðÉ?–~…&ÿÉŸ—§%»4%¤DxãWB}zÚðçâï¥_øYú“$ñçß¡çß9Æ?K*ê#&)\þ; ¢i‚pêÿjºDþ°¤Ì~Ä ¨oÊãkQâ:Êèøw¦è?CñON+CR¼-sž°”¿ô§«cü¡Å¥_Òü)O >ëIéWfÒøŽ¨ü*:ƒô¤QÿYþ¯ø&Eý‰ð¯¿"_(©üIØ>5~Qúç*Õÿ¹üÿüçÕÑÉÑ¿ïüÛ¿üË¿ü‚3>±U;ô†£1úÄÉQûððÓcùN:?²'•ÄÃn°p¥õ>ŸzVÞÒ[Ìâ×”so’yA¶ _~þ™¼&ÀUzñä„eÞG®@ìô>ã¿\õNOÑ‘¶·7Ap°O—ôžökb»2k?võÇGÄkÖļš²îP|÷eJ ™liCCÏ8ê„TöМôF”ŒèÿªÛTÔŠW¯oIEND®B`‚PK !’j•¤—¤—word/media/image2.png‰PNG  IHDR¤ aUásRGB®Îé pHYsÄÄ•+—IIDATx^í½=hcË·/¨ó}@†{A ZÁ tà ÈÁ+h¨á È0v0Ð }`^`Z¡:°3;¸pLà†°ƒ+¸Ð&P6Ì€ÅZ!RyÒ§ò1ºûþýÉ—/ÂÒýu×ߟœ9:þ|t°Öù°±Q­Vaõ~;ÜßO&ÇÇŸûýþf÷ÐÝì.©q=éHžùtd¬¡0´x'éuJ³ú¤iÍ$ÞÌuSõcâÍœàóií×¥»Ri%Ÿ¾+ÍVJšGéYi¥,)¥;¡³ôC<ÄJ¹ÌßJ=‘Ž/ ?ü]¦%§ür0xÚÿ?ןGØA¿ ²µ=+Fgr ¬ÙfYV>ó›Ôßìw¥òJiFrš•î"%Sã•WÊÈ™«!)ÆédöÕrye†¾¡¾iI‘ïzÚi ë‹üð¸Êö+ ëµ>¹EUDo¯‹ùªTGYàçlþHÃ-)+Z±à«^)ím¾ßß?œFäKâýÚi‚`OD„L³µQ,Œôw©¥{7n+s¨ !^¯oïí~™ÌfU`º³³ÞîöÖÖŒÝß0zý|6€¥[ßܸÁÒ‰§Ì’I «’Ç{¶8mÁJ {_Y_[oéè¯F 1‰XéøÊyÜ®)RvÔÑ/‹ª{JÓÎÒ‰MqVÆZ:ÑÏO.-FGùµéÑš‡ßR‹Æu—¿FÏ âh°”f)}Š ÝÝÝtzK‰ã[2Pü¼ߢ BÏ&Ñ_§J™Ž§íZy³»#e{7ÅSþ‰tüy(§ywÂï¢dú Óø"S¤ÀòIŒÏÄ^ñOÔè·zcíúú’¦&èßb³é‰÷‡Ô Q:·Hp{é®R®6[-‰ÁÉÛÏø–t …R¬sq´´£8n]¶Ytjͭݲ´·ÅlåeZÔÕ2²ïêåbÓq.Tí8basž®d[~f¨>³™‰Óâu¤¼ŠbR:#)ó’¹OÏâ>¯8ÝÓ± imgîQV$<¯B<¸H ÷¬'T$nïýW’CbN–ôS’Ý”vä Ýôv\æ±úªe‰æmÕ·yM¹‹fiçÌÕ ¦ÓþâÇŒ±y[™çUW³BØ\DãcvI6ýN-— 9±b :ÞO³ò°5«¡ÝŒÊ°óiø ú`½VÙj5÷ûýÙÍ•r©ÿQúœ(WÌÛéxr1žy'ëvwZ°ƒ´NÖ?­]lý½]]ʵ½^ïxx9ãoŠI×êå?V›¿Õëëo×W7Óµ27ƒAw³]AÌYzþïöööô|Øn·#íñ2‰ß‹6nÅD”ÓUÈvˆüÎ!vY{õCWN‰y|δ AaMz‹¥cýÇö¥$-àƒ~bãÒ];ÈöxÓÈ“¹K[Xky ¾N`íe,Xa±ƒ[üâz¨Ã$×xŒY:‡,b‰Obmb§%hÑiIO6F:,r'¬&1¸HÚ{“-ã;hÌ+DPŠ×öŠwieù™Xž§ãî.Ï=ªdÄÞÉx6²wΫ)Æ;3ÃÜ©}´üD´1!1Ÿ*2rZG]“jãVÄÜ5æß àfým·¹Šul·Ã¡Œ–¸¹ìèÍ6¿¨v$ä<—.&³“ã}©Owk¯]-wjn•­Á2æ“i‘°Q:j‹·­ÖÎÞÞÉå5œ³iåF­¼Ú„±SdGpÔâ‹ÁéîVwggg"#m)—댔•H¾ŒÝ_¿~}÷îÝÁÑqg³Kí꼇:‘˃Õõùöóáq»–ébÅ8¸°@6­ð")üpDÍ·«´MLËåÑêzét´Ý«ZW›¥/w[«óñ£ÈTÇž®W³°×Íõ>ãšÃáq‹+ÏÛˆ'Ø´¥â8.…:³|²õÕiëy}ôÇû½ÎÉ·}¹Ó‡ñ`¯Õô.®·Ö|{Îï!¥ÒÕq­uÔ¿¼Üj0Ï‚gù—»ˆ[=6`F®ùÉsJÆÞùùÙ||çl_´„2iãRøN¢™?q¬Ñó°ž`›‚Œ}L¬ËKç»–D!M[Ä9ÉÅqsñè ëžõǤ«ê¡©a`#ìì@`§Q«l6jû{½ñÅ©ŒùH\hdú¤:¤éM7rPª]–kGG}©Íöv¯9wJc1fvL`PÞ ñóð~.¾Öéìõû§£ñˆ^呸¬ô®Q²£˜÷IÕQ:q)]òmFÿ’'€qÏÑ_˜<ä¯×ë¬Ç+;4æûv°‰.·{>þ6¹»Ø™ëòÔ q×w,+­IƒãúúXž¬J‘ŽÊ1’j-|F½—8çžH_Ñfúȉã^ô­Àu:£Y{9ÅæÑ®®Z½«ß’A—ðà‚2j_´½Ørß„šñr;DÒðMá(°<¥ä)ÿëã ò¬T…¥;8 `$ߤÓËñüôU.çKéèÏɧ`@í/b߸¾„,R€EãÀø’E.ô/l)šæûø'àKõØÞÙuI”0ìwÏ>~¹ÛiRAuÀŒñÉzhrKÏNÑfî‰Tó3vš"tóia[òHtOi®’K×LšG°›Út3“ édãPŽæaiñÚ|Ý 2IOèÉhPê·¹îåÚÆþ‡†[£¤øŽY´4 Eg-؉KÜMÆéþ[IZÛN9~(FzÃk·6ªtsÚ=£:D­™²ë§­_'ÐÒT¦NoF¼)¾0sñòuÍÐÕa†ë5Eá¢9Gþ ·=—«5ïFó˜d)˜iâ)¦p¨ ^ëŵc†4Ýc=Éãqw»ôQW®¯Î_¹£M+jÕQâw‚z|,ÏÌÒF áÇ?×cåsSPòEþ–Ó:ý’KÑïª~ ¢!ÒÇ9Exs:ìË´)ì u>š3‚ßF*g™à£t.:ÒÿhÖX¿Eýÿ§LT„fi”/*õër½ßï ç»»}à(°wH”ëÄ?i¢èL…+ŠQ­¯Œh«“[¯*FZ>É™Cv€ ¾s?aå a²ƒj#ùü‹ÙNe†ÿŠÇxtVê·š~ Tì´ aåêJ­Š'a=Hk Ü´utˆôÒ»_ÇôÖÍñ @ígÉY;º”©ÔÑ`‡òP [ƒ1û“ÒèHò¬Ôºƒ¯Ç+ëZ¯Eå`üíñ °aC®^Fµ/þ¨UQ JøãèŠê _lqJ) ›°ƒÍfuåø-JbÎÀwf>~VïüÕé­o ¦ä€x^QÛºÆáI­z| ù㣫µêêÖÙôû=°¾^[ÙCô¢4nWå¯H9@‹µ­ããî*ø¬­œ€*twȾMZÐñ¶øWçààb$ yyqÐ9=ü¨‘”RéæˆJ–ÿßpž›Ã•ÕããcM<¸!ÑŒ‡Û+;ç#H ÅhñwKwW'’íêjí`Ä(âÇ'T›;Z SßÌ£õ ©9Ù4Ê‹¡ž¹ó¶æ]Ý· ±¿œÜ'w—'¥?›Gèòô­³½›­oß&ßN6κgWêíÝ}AÎË¥½£!†PWŸ[›k”r÷íú´Üí]Œa.ªï‡§(ðÛøîÛIçÝÖÝŒúûÃo““wU‹QÿÞzueµ^ƒ[­·? ×° ë½_&k?öÚ«•®›Ýæ9¥L¾m­UÚÇ—'¥Îéå˜b|Š¿Ø;Äð]«¸Ro_ÿU†u¨žŒH”忇ÃÎÞ1`ظ«OÓÍÚÅÞûá_—“ëo“ëãÊêÖõDgûçã»ývutÜü³1D¥®ÇßPNÿBôÙ§Ëîp<9ï—>½_i®—1uÞ?ûóìJð¸ÅžõÖvgp2"Ã3÷:ݵº`ÉÒd¸Õ:è1ľßö÷ºÛ¨3¯néí•¿à‹Ã¥Þ1ÒÄY`"°}ÝÄx“˜X[?é N¯nW·¾})¯w§<‚ã"7‰¯¸f…I½q®_º ß‘Õ'ýˆx㯪žÄPçÌN)ËÓÙ™ ;áiQ›®¢”§»¡•?‹ñã,ª‘›¿¦ZHðM"wÜ=¥fÅò4~çvÔ2â“"^É˜Ž¼…Ì]( ^¾ÚH*ù—¯¯ß9šÃĺGÒå^Ã2ÔoÉM;:š7WmQ]ñøN[Êutߥ"Ká;ý+åI9iON©T«Ö`êoë€ZåJ ð­Z©|UßbtX-¿Åz9<òˆ¦}ò„øª¥9…iy7‘ÎeÊ»Œ©´•*òcËùÛ›Ó2 eV®'¾~¿P);f'¸L˜n*çzßá¿Ãáøæöÿlš]ÐaŸôó£}Òn¼[ël CTŽ-Úl6:M­×:‚ý@Ë/ö{×ãŠÒHO-¥Ê»J7ÛÏ®ët¥ÌÌ®­Ñ€=2r‚ãįFsµ¦.’Í¡+ó-Œò‰4y„•'Ý3 ÷Ä)`ìöíKÖÃLªà;Á,Dsw&[ãWiSŠN¦qNDÓŽoä¯Àq¥Nƒ‚.µöÖdzõ̓[Í_ï^_¶º™*&b÷ß¶:XGŒOB_ü]CÎÅõ¢¯óÄ&«¶Öéœíô43»R¾:]¿ètëU°ÇÕƒbvËb½hö“±’‹Üe¬.ÎÀwÑî`•§HUsZZZD¤ížl $ÎÅ-ecgÄËåÉÌ,+¦ÛOBƒ Á†²P3ê EÊZÈþ»Ú Åï˜[écò”.Â`Áá;7ãb5ȦÓn½[smíf4ª¬6«µúJ•Ñc7ÚT!2¡mŠ"5“g~Jâ]pʈ¯ #WÃ×Á8áºp•'U ûËÅŠ“¿Õ1‹@"qÍæúd2h"/D˧D±|ôΣ¾½±w7\í¶šUÒ{Ãq­t·õíäò÷Õ=ÎÙ9¹ÞÅhjÊŒ%ZÊôq ¦[ÃýO­Öj_úÏ>­›Ûþö×ðͺ¬æ;½8j7º§Uä¡Õmmà j2n;iA2‡ŠãXLÀVû¼ÿcUf`ÉÂZÆkš+ÿº>!¼Öî}ì¶PÚîéx«é$È8Ž´!½þ¶¹}:¬6W™ÏÒþé·6†èPÚF{¿ôiïc»I*=½Økn^ðw4lA7*°J«ÝÕ~ûðrk¸û©Õ]åz•ËXŲŒ& ¡pßÌ¥ƒyý+µ±B³Yãº+v«·‡ÿhaé×±:îTîfcìÔÂò@Šq¾X[ßÛý³µ¾Êœwk­õÖîä[»«çz é›”$š#òç¦&\ÃúçâkÔ»–£í»2â#¬äÖ$3MeÚ‘,Æ&˜Ä¦“Üx]±Ãqq¦¡Ÿ±™{]b+à 7ºŒæjµÎùëò|äÎÍÕÆÎÈÓé.õƒìD÷t¶46'žï¢> ýTúƒ‹;¥hÉã¿B_$,¯Ãôv…õyÆÓÙ‡ƒ£½Nëns£¹Öªc$«‘\¹o©ŽqìØyÒ Æª¢A hW‚÷ÕòüÇ“Ùðêjpz²?‚æV´W‡sñ20ççàìtw»+=mþŸ;:>ùQ*qgS=YŠ!`…; "2o "óâIÓŽEyYصp´´äa‹c,T<ÇhÆSQGû­}Cþ­ýDzË¢gä[ÔO7GÍõÒ f=œ?tV8ò¥1»ìê¦üÄÞkï²=-öQÛ.š"ÜHONÑÎÏ飦P¤©Ê$HSuÙö–È7—I—f‹f阓’M£V£:™=O®1E=Àô3–åqó™èú­ö^AanÔv­ EÂÚÖ*<é«UÂ]ÔL<ÇÜ=Ö*)xak’C»ïZ¾Ó·Ø¦ÇõÓØOœ!V.îmÝ\^¦ñg´Pe–Ä)„aÇÊ"‡Mo‹\ßw–tvW¯T͵Íýc,e1êØk¥Ôx[nÒv±ØÞØÈ/XÝh4Ú;,O¹O;ë@Q;Y E-¤sIq­Ê6S^˜ó­žö˜½s¶/²tÎÆI¡1dkQ®¥õ\3—#¤ciµwZqe ¶$5B¬íx¸³z¶qí66(ȉL…ªFB#U×½'½Å£‰Åz_Ôº-Y¦ÊÇ4š@O7;Ãfº'´¸šÇ¡#r@ÎÛ>צÜvl‘ßeÙ>±†:§léìÞîð`¿KÄòlvvÍ­.ö8Ë[:¹cÝH";ƒ­qH"ê›I[Çwë$q_^z‚GL(\1Èéá)±!WÕ 4à:P4Ê|[Jí5üM¦“«á×ñt¬øÈZOÑWÑíö:i]?!ŸY–ä 2> tø« %ʳPqkÅòÈ®Çè¤ýÊÅhI}ÍÖqÕ1öwÆx›æoQº:jnöJí“ó¿h~I½œóØŒ=eL!8ÔÐqd=G㊡„”†k/3é¹ç!˜«òÿ˜‡éT6ƒIË2°Øƒ±ž)SÚ&†ïdol&¾3c;þ˜ƒï¬<“Ø'Ç'Åf3ò¤/#Y7?c]Ò¶Îóvq\æPU2MkRZ{2ÿ¦¼»‹Zx„aÆ7*T±ÅÒÓcmkÙbœß4{c§0+ZIäÈ n‹¨½MM²í]dØR$Fš÷H!¾,Ò:ùŒá5Ïrqå\«­ÑKcRÖm"v%XføûHØ»øxhl ŸAi™ÌYŒfyJŸý{ :ªöœhÄá l2äð88.& óð]úö‹äâ»…XoY|g5ÀG“$¾IÞÚ!;·ª!‰ÕnÍÙ·k>kËò,H~zqÌ•‡ÅòÒc£Ã´yÊ3Ú† ÃØÒjòÔ“âÈÓô1þhd÷#|g­ŒŸ»ôcþyViîQ\ôUK§WzIY(•ZTùyv*²;(gÁ/nfiUK1‹¢‰Ï(xn E+lòî¬pMáêHbŠhw΄8]]WüJ O;;!ØGǤytÔúlû¼ôÒ‘ÝŠP|}—‰Í=îó³NÀ®î*OaÝö¢xÏqC^RµÈ&£4ÑŒGÌec±$¾³q=/À<{g}ù˜F¼äbº˜ÍÍóÚ²rç7"MȘYÞªÚ±pÚ¦‡U6…‘]Ëï qS‘§Gž‚b–Žâb~Å#j}DÓÑ.¿›3’ôø“‚.&££XØÌÈ(Î díPÊ&©:³ ºP~¤¦ø½M2‡èfu>‘3ÑÞfw¿—L"Åž´X@g`dFΊé),X¤±›}J”]R\ÄiáMþ*ïϲÏYŸÜbÎö1'”“,Tšöw’‰¥s<+M<›{Ë Ò¾?Çd¨Z¡m!s£n©‚®ƒ#.³]mwÏü2Ï+gËnY¿æ’u멾‚žX«£}Ù†öó¡º/Hn‡½Û{«ëÝìÌ ×íH$,þùŒ”|´¯E'#ù¹Ñh•_Ôk¢ùÙøZ™ÝwRc›"°]0é”$sm2¢Ïh[1ÜÍ(G"¸5Aè¿yëƒ$}jV ­OÞ!¬åïNåëñ'}Ë¥­OðÇÿˆòÏ`A¨}Òh^iœ¬@ýŸSÀ>Û/yò©ÓúÛÈ~Ù'¾È,";=…¦§ žÝsÆtꉲ9ó íÞ¥S$¨4=\‚$Î5bþù„ }Æë5/]sŠLDi‘dLžÄ›mª)g±ÚêˆÜj8Ñ(ÉcŸyééœRÌŒ-ûÈL§'`¨ü*E¿£öe}ÿ$míi¿öSG²îÖ7÷Vú }Ù­Âsk•¸GPo®D1]‰êeêî´MäÉDÓ9váAÆDi¹‘T¹a£xY4¿aÓ•Vrþ{Ю­¥Å¹Lj yŠoÇ*K•¤à2õ´Šé8!ßå¥?F+TÀB„ïÙ ¾[aŒì°¥¸H™¥-Ž:Âw¬óf_-Ïæ»}52Áá0—›O÷Ц—{™ö[_jYé_2G¬ý+Âwv­Ÿ¯ˆfí}Év߅뛢PÚ´éÕÎpŸUk vPÆ…–öcÕHÜ[Åñ`>3#ˆ/MG¦HkevPPÏQ+c°›Ãqܯb˜NjÅoIºø(JRÜ$žGRÄ{j°´æw~ÀÙéRc[• £Hù¼ƒŒ”â<0§;DæòDè̦xZêÂÀÑ£ K{±Œ†?0ÚZ‘'Ù Â8J£ Åt­LY}ÒˆuÂelÇ<ðs:‰¾ëËço ŸÚ^ÎS#±<:)gßFæ]Íc[6MÛ}WšÈ"ÿáµÓ "ÿ¯‘šh?ôwÜD;+ìzI=»˜ä±ÕóOñ—’¢€-e×Y©0ò’%9Q›úˆ³‹Aû¶æÆtˆOµˆã¡Š7Yù|yЉ£ é•Á}>gžÖ™øû¾Ìt/ÎÒkÄ&ˆ6¦iNQ-B§4Üj»Å"ŽfVDˆîQ6ëìŒm®ÑY'd§àI#¨Íæ9ÁG£MùV/{´’^ç,@BWês=ݪ}®°â;¦=äMÒòWU~K³ßŽ/"ΚæõpVk¦³É‹ŒÓ$m—žM‹RD4f_h¡É¾jûíÓТbŽbTxH¦Û<ÏG‹çwê/tÊ «h#ïôü^ë\ë’†GtºÝç¶iä 42¨˜ÑêÀâ"sU&éhŸº“žþ´?o•ætÍåóàâ+½F%>«ã ÚÐ[> déîÕÒ{K˜d;˜X¯¥Û<¯‹~J(c»p ‰‹ÌÚCd"¥=íSt>­Ï~¤æ†…1^¢õÛ0~-÷3Óê¹³í¤µ™Åéeå!’w­ææÆ®`ô#\™ÍØA[;{Çûº+SЄ ª<Úb®XçÛ£éq œ®eªãH×MhåÖÇäy¥½ÑÒnC¥!¬q-´_E‘²…{ìæ Ô9rÖ¾¯ýVVO艭¬ìg,tŽÁh“)/O^þ"åçñóH"ˆt)¦«Fþ.Ýè³é/ó[yÉ¿}ó±0…ò¹£•<Ý^R>KZ(í³)*ÆOõ}“žÒêù6gŽ]ÚëãæŸþe;£Bëìšþ 7·ÂæÅÅà4æ"}Xâ/Ù´ëÛ©¶QmÔ0µÎΡ ‹5Lûžæ­{z”Ë#,Út›g>m<§ï,R~ ßoõ–L|—B…õD8Ë—‹ËHÏB 1?ïÇÈv/m|&'-7‡5RBýjNÉâÐÊg¾¬^ˆj*2¼_º•Un9¶½2‘š©ï|ùë_sä£òŒïP¦ÞžÓ/¤´t;fóà¤$Vo~ù3=œP"Úk¶Ë“Ý^qÏn_+7›?-ŸØª{£WétÓCÓòT~°Q«âÖHºV`±±9vPð?Õψ·QñHt¾‹c=ƒãø.ò q|aÀùéqÌù.ƒgcþMÄ‘õ‹| E€ |g^ÌÀ}ü×l,–JÏm‹ó×¹2·¸Ï!hÏOÌ·{¹åà ©hL&^nŽÈÏ“…)"Nœ³q‡áÍcÕd- ^óõJè’Ç)iƒMl A+WE0Ñcå±rx@™ÏÆjÇ)^æøwYì6 f¶¯mwC‹²(^ËËcuÏæw¡;Q)¯wŠ©âß<ùÀt1™§ûiZ?m‹õÕΈ¥6ý‹LŸX@³76 ݨ_z |‹7åÙ{›ž‡ïRx$÷ßëÅñHnLÊâ¾¹Pý¹1id}¯‰×ÍÆ q÷CpŸ±¦ÚÂÊ9&ó åc¯Ù¸ õ-‹¿2èœv/¤WdbËÉÓm—ù1¾¹8NJða5Ħ•‰å·éÙúiãn‹°yÞØÈâ¾DÛåµË|¼Ã=Ž‹Ð·+SOýÓý<Ù™˜¯ÏN »ùØaI{œÆ#yø.–ž¿Ë‹¤=jŽoO`«È̉kXoì„Ó<ãX£òsü¶úót¼Éúö¬hT^<(V¼ÝÌŒ1åÅRÄ„3|{^üÔâÊ<:ïÝ”(µ8·*ŸÁnyµÎAèÉ\ÇYf4Ä"2«Z6N‹µ¥tÒÆ£ó"­~¤¥Zšƒãl±2´òψ­ÏÇt¶–´!óõÙ u6ÖíøRdgbv2Êe+èÐÄ#ÇìæÇ8ŒÇÎÀyXcY\`"bê]ùÀX\ƒ+Šq¨È²Ì]ÊoǼ´”c’2°žùV,Ö³ë ‡K´ÝðN‘8ìSç‰a±ÔÅâ8+C¥æDú™×F<&M·©4x.v‹©ˆÕŽ˜¢H‰r¬N¦õ3»%csé:Îí_iÉça·¼¸ü“Œ]ßr£¥¬˜[qßú™&µ»&%¶.ì>éâ+Ø‘¸+]yÄ1,í¥iÚä7Þ –_ý¿‰…Å1]¶—ËÅügNìC"-ê%2Š¡¸8Љ§°öÚtW‚ÀeJž4íçpó0EªUæ1lIɶEœf‹™ç{ ¯ÁÌoS±ÈóÛ}nž¸ ©ŸV÷2dbå“#«¨ED2®æâ¸¤†Ø¶öe$ôAÍ›=¨^™ÒòtráÜt±N¼]¨´îÒ^¶Çú¬•y²íÒ}?_÷îc[œ®jt|fÅìÓ©çQ‹«Áb½‡Òâ‘Ø¬¹È ®<Šðˆ¢KÅzQ~÷.zÕ+Û_ÙuLñøŽ×Ô»¶œX™&¿‚^ã3¥âÈøXŸj*ÿ!¢ ­ÝË~5Žô«ÜFŽ¡#ÙF¾Ú¬çròÏm‹"Øðë.•CE ‡.[æ>“:fë+ãñW.íä©-mdîå/vJq„× ³‹#xßÀN3Xròø)ÝÒ•ít/Òç\ýŒ´]9ÌÖk¦ZoÅúWºíœÜØ"Ö—ŸÀž¨¥ŠÛE¾KÆìd_ .È)GüCÖ‹0E..ˆá8ö<9óA‚UOeòçÒò–ñi±½û42ð]:®gš"GQåPÿá´kÄqg^ÊÆ€"U©5Óò‡¤Òt®™‹óÊL·éý|øž-Ê(€Ë¬L¼„#ÿ• ²2r¶8.)×€,mAúÒ:†ÎÃû¹»<ÚTZ†ãÊŒ3æïa(¢·Iý—¸‡ëA¦„îŽò8Ü7Çižœ>~?}(¨W®g9Í—ÙXÙ±]Tç‘Æ\Og¥ädùÒ'Õ·m±€óÌs ǹwsp_êö[çÇòðÝ=±^–Ofý·XÏÐ1ìf1 †Þ³kK©%LÄþ(•tÂc~QIjÓîãh‹ÈWLºbFN7åʼn÷ùVŠÏ8ÿQM:‹.†ÅX¸*áÍzh°›H=¿-LƒÙ–)Ð)/Àtf¬cœëØ~\bq\-=+Ù%%Þçá¸Ô#>æxJ;£ÈNm w;ö>¼ÎŽ]…úÿ'ŽÙ-°Í2j0¨M=@N\ ÷Yì–òQ±=¹EðÍSÄg*ž2~X=½àDñú¦}» ‚xN÷o$‹Ïçi’n ‚Gyþë.ð#ù#Þxpn}+íɥ̴×ô”Ï·éóé·Eøw|Fòá+« ZòØVËk‹¨´Žó±Y/ÄXl×4’ýJ#Sá2­þÄÆQÛ±¸Ýˆ$†#¤Ÿ3Šã»¨¹þ(–N±ˆ§ã}Ðb½­ýúI1på{_dÓ¨ÑìyvN–‰SOœ-·vý‡Ñwww777ÎXD>PAA?¥`‚q{õ ÎN¯ÊIÙíáÔ“©ßÌ’·‹ÝÊ œg·»×;èï9ŸÂVÑùÀH‹¥«Õj•Jå§l×P© „¦Óéx<{'Zh‹v{ûG‡û£)bv»(=Ïîå »ËËËz½cg#\A9‚‚~b ÀþÀØÝŽFf³ -Rd;ç†þ‹Ï³“ÈˆŽØ(-œT«Õ`é~bµU HKȆz}µZÜEs `J”4÷<;3ãç­k÷Ÿ18u¿ AA¿”|ßç1)ÐW4³ŸIûÙX¿üÞ±çÙ ‚sOžy13t/þ¥8T6HàuIàvz»³³ÓjµÞ¿ÏO<”Þëí=°.:S\l|[ !ÓÝlçœg·x}ùÚÚš±µ”)\ìâw}„ æOË Û9B<>>†¸›ÍæçÏŸ%ÛÅÅÅææ&PñêüùçŸ~VÄÁÁ-M†ÌöùÀÖ ¯ üÄF[Ý­Éd‚þõîÝ»v»½¶ÖZ__GïF¼\<°î­+h["dG/ð–-ž;ÏŽyñkÜÜRõä¯nEr¶}Í˳(=yÚʼü ‘Ù¥š‰?ÁÀñ-i'''glïØívÑ0—××hÐòæ=.–ˆþ@#,ŠÆó϶Vx=Hàg•ÀÕÕlŒ¬Ûöö6 º°Âîîn§ÓAÂŒj^ÝHðKü531ËþdÛ ³ÓElñ²ã¯™ñðv4ŽËt:ÌÄöÖVcm •-y.‡ÃNè¯Ýé\œŸã‹„ gô|·ÙíÎ·ß ¡ÈjXü Yÿ'¡aàúý©à×ï÷ONø×_mllHàÇWWCy íqþå‹+Œ%ø³*f¨WÀ£JxBFKèSèÈx¢ËFà C+|  ó›°_½^oooÏÚ»££#¤ =±œ¶àøOF¬jÍü~êÒŒì<Ž3«ö}Ì.ÛvF{-Íú{×;::h¶Ö`MšÍ¸—ü;¨C¿?Q.ä‡O爖 “…šc¿ÓòòXÌÊE蛛˵µwò ,)è%^GJ½¾j-NDãýñÇx„!>ªz„‚~ ÀHÁ®a`»v{{ TL€N¢l[[[@y»g‹ðM Ö FB Î`Яñ¢]aÆ¥µEÑæ!?jdãGÈÎÇÝdl[|lŒœõÐá2>‚·y< % ü$øøñ#z=ú2žø!ZL‡þ… ô2t1XÀùµ=<ü tM„Ôñ}xx˜l2æ (Wz,hgcu‚-žÆìÓ‘u´åì¨,B–1pÚáÄêÊÊÝd‚waç y5ý^\£Â„k™þ\Šå'Dà±XÙaöWòÃ{*=¡ ú½Z%YZN›à+•J¦%üIT6T#Hà~@GFèIF¬øŽ—]Ý\I ÷I¸\ ßüÆX~€˜iéŠÚ·Ê„›î% ÆFQcv‘¿£tDòôgB¦Ýi íN›çîð„©–ñ&D'ÐÇ›[µLP“<ó¿›™·@°§p xÆTJx„ ¡ád€ËÐ*HD KÐ<u§¥ocvX˜"€¯£L1Íá$$füÐSdÒ“ èbûýýÛÛl:;âõèì‹d ÐÇ`(ðÌËPÄ>h\Ëø#ñ1µ€%³LGÏÓ±qTL—MûYGù··w1²ÅZx™—A ¨ v°>ˆ”‰nÈ ñHX(±_yßMBëV*îïcî%KdÙ0ÙŠfØØèà£6ƒ" ¸¹é7ØÊ:[ Ù!ÿñÑ…67AcéPÐò L  wÀ®ñŠ“1b>è€W៘ÙÃvd¥pƒèÞCä#Á‚%È©'cœz¢'ËÒ©'«ÍV„ìØÒ)®²1»Åøn.+nçc:‰ ¬mÈ$$ðÌÀTðædÓ¿Z:TDf! Ú¢äIÅ"ˆh[:Ø;B|‰Ùi±Ec|´r¥ÑÐÅò•Bcò`ïžY…Ãç‚~´–?°'«¡c3¹0fÇÖTmêã V¬ðÊ•³"FLsò»ÏÏY ø£[$|?H Hàñ%K‡^ϳEíOlo¬Ø.†v³»¹R ÝJ¹V)½Ì˜øB¦€#P™c‘ë3^í¢­)þ£¹#W#Uc¥6s^ÀÓ~ÆÉ¿5?ÿ|´ïÚœyeæ•V„‡"yŠ”_¤¨—ê’GâåEæôõÜv…:üŒýŽŒ•?¦¸HKKÌŽ¾¹ŸÄì~«7Ö¾]_ÞÜâ/Ó~†z¥ÜÝÙ9ÞÇ޴螪@‹}98‹•Y¦4Pü[ÜOÜ:Äy´éWªÒ•ø™*'/O¬í¥ÒWÍÓ”Yè~›?ÎûÖ²ß_ß<9Ì•[†lm~[æ#éÉcéÛ/VŽœT,ÆÓ€pdìV›qdǨÈnk§wôòfc‹õ_-X@µ9´•I^þ¼<y·oEò<„‡"ïáÁæùÕtìÕÕÈîø/š•1ìÝÛ²Õ5,ô·øü)£ ‡5-˜ëåÈA°¡ü<í1`Ä­ðl÷ Ûý*úWiýØ>e«\þÜ<ú®Ê'â§ÈÞç þ•爇¼rT?S¼å¥/à'Å¿áMDmן§éÐ_~l¡ýþSKGÓ®Ü?<²“Qd«´În﮳Ó>þIL½réÞRy®yÈ»y /[æ²ùSß}Þ[øáŧ€Cv:†!;ãÿ½oÔU-êÇ GF¨÷ó é?BV‘¥»·üë‹ïKÍ}¨O4é¤-®Õ#¬W`_³Ã;¬csÅé…eÎçÓ+d¢.¶SÅe•í-î-O+Ÿ@ÿû ÉÖéÒaRR±¨?uo¬Ñ…®?X} ´ô½ŸY|ZVne7´ä¹ß~jÑ«Âïzû›”¹=ÕËêj^zJŸ­AüÉÛô§îË2=¤–Î7¤¿ƒ‚Ž%áHßÇyµàߤüÒrp—Ï“CNž|<ýÅÊvÙü±vÉã³ÿæÃ¿t[ÿrpÈ.Þu²#+HÇ1¹…k®ÎÁ¿å∟Ú7>V»/‹•–ÍÿX|†r~*=7‡Ø™!‰;Ï3§-ãð†˜ö»àç¶ :ðºtÀí ``‡)W5än6vt;£xÍí•qNewgÏ­³³sUóhÞÛ@¡—W·Ÿ!ðüz÷¢„¶ûéÛnEBl<ò4kæÑ2‹ÍtÎâuvºƒ‚7Pè$ÅÛJyóO5v2iáíz&¿ÂÒÉõBvè J@öÆ¢~¾Ð‰½ÂŠãþ=3„]óßTÿ±úþÃößÿ®–…¾)½ þå¿kã,ßß¿+§°£¹4Ž™û·ÿöß~çßßÿþw¼žAAA"ø‡ÀIv8,¶è÷ßg|·À.ýË¿üÿÃÿïÿþ¦Œlžå7¥£þ_Ùag…ßóÈÈnç¸ß+bGqt½LW™sîM{x=H HàW–Žà,—u6V–ÙÉð3~]42Í¡®¯¯q1k˜Š](¨!H Hà~,…KqY‘óÙ1{‚uvæcxëìül¬½ºs~4Èó9´EvÎNëÜ.(Ð"ð—"\¾‰ËÈq÷n³ÙÜÛÞæµšÏÊ.’óz‚ Ñï­3|!]¸ŸNég® ^VûþÄüHÌŽCvl‘8r—KK»D#f/ìÚ¡\¬³;ê÷Š˜á€ìŠHé%äF°/ÛÛÛ›››Ð› þ='o0K`ã¿à þˆ†¢^ €ì`™¾‡oëõå›l%d[gç°AÄ_çÚN¶¯Ù½ üòû·‡àDØ5;üPgnn®#Ed5™Œ·¶º0xb?Œ`¦““ãV«³(ŽÖêää)H Â^ÀˆÂAëÉt³Y¿ßo6ȉrä]h•`1)_ÊÀÜêú¯ÓÆ ¤#ÏñqôõùmŠ"–2›çßQ ÒQøÃ0ñž7ϰ­ïCdÞy"9hÌNwÆ.¶K>f§9±ßþÞØñ´4šÎpEÅ+÷t°Ë3°zX6mfcÅ>ú³ðMxQ2Ámí™mtppó…Ë{ñ<88<Ђñxr~~ŽëÜûóµMa›Pâþþ¾Ô¯t:„T`òŽŽ$Ârxxb8¼‚qÄÍÀH¦C™xâŸR¾” › Ûé¾.H“î€IÒ¯÷zn,[¯`»aRÏÏÅøRÐív é(çòò ƒÃOŸ>I9`¦ÖY°íüò_T;¾4½z~d½¬ Û¹Åv ͉-a¸] fmÌOþ¹uvãÉt:™ñ³4•õÇÆÆ!f÷ŠâD°èüÒêôÿÆû|øð‰x^8¼³°³³ƒWÖ××aõ<^ÛÛÛCN%"¸¯ÝjA#×ÖÖ.ŠÏÎÎ`ûP \¯'!¹ÝQý¿§ñE|Éôõ 2X´®ªTÚÛs_ŸäÆ=WÀhGG„%åøè¸ÛÝ’r`—ÁÛÆFg8TÞðEØ>ämµÚŸ&dòt8åéÌË‘É=bv·“L¬í…e/¨ëìp²Ó]i&O^ÌËZÙQ»Î.øÆîÿÉ1ªKâ#¤W«t’a´%†±•ßWë:£·(†Â?ÁD0£­vF ð {¥El[©L‹›|™‚ݤlÂÁwAÓ×'ä¼-îc¯Ni¶O{dºf)xÊ´…”#&¾Z­qáúE l5 ½eØþÂÛ.³î¿Ïrº0ì´Ø.é ¹„­†ÔÙØ; D±t| EˆÙ½Ÿ&˜èáü¼{÷h+³±2°hÎê)¶òßå1y.°&'òøô cCÁDH”2-ÿ¶O;+Ì_WUÎÆ€óåÐín"̇–çˆ'¢^àÓ—ƒb‘r6@¯íê»H"ØD8O†Ø¯¨¥^¿Ï÷ˆÙ‘þÈ•:ÙiÌŽÎ(²£§ ;’eˆÙ=žz9¾C6LÜ!²öþýû¯_¿ò´ùŠ˜Ìi ëqeh‹gi‹§ÐyŽÙÉê“ÅvÉ";³‹¯³#ÛÏQ= úœZˆg…qÃýu`ù˜Yj‹ììÞØ³{9~,`ŠÐA¼Ü/fǾ̘›‡öƘ‡ ë삯¾¿¯1¬ûư~5™ß/f‡Ù Ú•ˆ9Šô:;wÞIˆÙ…õqa½dЗ¥KÇìhÞV÷ÆÊz:Ø·h9ïÄÇìøVZ>nŒÖ¸dÐÚòëìt='ã@ËÚà ‡ ‡ ÖXÌn]"ä«?ƒìâ1;Ùè«íd¥²ÜÂOS‘æä ÝGÁ)–šAAA¬gÓóí’ÛÆÖ s‚ìâ{cy/'VÛÉÓà¸\Zb€lG-¾Ë¤Å&êIŠîVÁ> ß}iyl]ün¹W—Hª²ïýÕÉó¥µo&?^{Ý~¡ gÅkÏ£o¼ ˜wP¸±f­gtÊ®XZWœØëvÅ T+ß-ÀtŒûØØqÛ›²Œet·¬àgã*• ŽÜÁM·x‚–tü=‘ß¾ûÒhˆÜ‚y©ž q¯•¤ Ž{žñ5ñår$UжO>'??ë·ø¼åIªÅq§µøó´õÏ*Ûeê%·2²›7Öt;(Ü霼úÄÆì¨“ºèc5·ÎŽ£uóì¨";w¶D¦¯#þÄ.8Í€’PN±ø+CÃWà'ME¢ñ;W¤^«ÕÄÞ=¾“VKU}†—ªóC¯;¿}@k:!Óu"OÄ@ȶş­ÝŸC=/WŽÙ)²“Ó˜òí’Äìxm:fGÝçg¹-e‡ì½cv L‡‹©Ð'ɼñÏkŒOÁ_ùòª—ŽïDï™ÿ©pëhµÝb¸Ÿß ¦cfDŠS#UðÆfø«¿vÿ)HüùðéKþ–iñ˜œEæÐsßâß=y;.³“ÉØÌ˜]„ìèŸG‹Ùñ-År$·ý‘ݵ?¹äéáþ'ƒ?‘…ÞÓ;›bùWZlßñ]Aþ½Tù›5Œ0€P‚—j¦Lð-ôU½A€45ùª9ªð&Ð=ZÏÉD±'¾È·ÍátêMJ7›`RÊ”vœó]äÜÜì ‚Žb:ÓJèÉ^<Ù¾kGÄÈyvEcv‘£ÛcíyvrN¿{>RÌ®t{;¥™JFCÞŠçÑȉüö¯¢Ъ½½^¿ %»8×¾WC!O\”åû3Âççç¸GJ4ò^>$és¼™¸4·.º'ó»èþÐ*è"þz?þñõéô²bŒ)|æó3%ù#¿äñò̤­]ƒŽn}øÙBõ‰à݃ƒ¨,Ñcûý•-ß”xþå |÷êæøoí;üòéÓ'hððjˆçÁ§Oήé[”s!oÕŽÅˉ·x]`ïJ©ºRÅUÞ¸j–]©ÊŸ§ãé—.ÙørÈ¢z¥Æÿ¬VC¼(Y08Ê™#OH^Ôþ†[íïkÛYÞ ðr¥‚ ''§R&þŠ[ß`³ðO<Ž|~¨+÷¦è€%Úýø*aÛqÎwñîÖÖ„€×£þ(:sÎz­ÏõêÕ¢(©o†®Þ'fG7&FÈί³#?Ì7´ãÓ³c'0N[zŽ¿2óxÖ©šÖÊå‚ól6[ûGGR~>=P¤/é¸Á^^‚µPÊý¼³&R•…u¡lü]E^qJàöaük<®H½dNJFº óCh<%Eƒ<;››x{}}}<É»°€rïj»ýê«Y*A‰ál‘ò† áKd yóÇårõÇ‹kÓ[À:È å^ÈÛ£´c¶S}“uT¬“ª?ÓÛ[¼þ¶R+W`(* Ù…è_½†¤ùd„X+Î?V]]©VÃT¥yž/On…Éþþd»N²Íˆ^ Àšhn0æ= Í„Šàé±<¾Ë‹œ(‚VÃÊ'\ŸÒvÌû.l(tFJ€J$t†ô„[ŸõäJrÂäåéÃ’1;Z/L«U<²óçÙQëÊZ9žu1;»^9‹vÑ+ê ÑMNÄ=™TžšL¤;M«¡ü3:÷ؤ#ð3ìDÓínxJ‰ú$BÿàËtZ•Ÿ÷Ý…éU—Ébü†2WVªh/¥½è-ƒL¶EA]zp9ê±v+×Þ¾­bc¹äŸµÚ[ÿWÑvèIú»è½l÷‹êáÉñ±Lh@«‡CÒêÄ»Åä)rÍ–íh”0Á›×[qÊ:»vIf 8X‡œ<úa å½±ŒéÙñúÙÑò=·ž%“v7“SÙ6Gï:a"2ªy¾9åtùd93ºúcU˜9@b¬e,lÃØ±¡½=JŒ| )ßaUæçþéÄÕtêî±Ï⟿®;IP¯g‰ïŠ”Â?J†6xÜä¿+e&d‹Qß f0iŽ ÉV‹n«A9°tÎÀc\ãy†™¾º¼äñÔ ßb£ãSGÛvJã­1ϳ“1­)Ʊr°ôÚè!í›z—M—iñÙzk«Y«ÒOÐkk-‘rj‹óÒÕ„Â[ЄFŽÌm{Iˆ à ÑêÄ»KÉ3S¶¼\†ZDù—ïÊ2ÝLù¼‚ý%¯}:,]³¹ŠÁ/âóõ—¸'–jçõe±àl‘Ø%çxåÎ µû°€z»˜x'ñ$l Í ±£"ŽÍjáßu#VMÓr?±¢ƒþ,$š‡ñ¢:Tš¶„Òè{XJŒè½ÎSTá ;h9ðëÀÚ’g)yZi{Þ0×w||~ðiߎíñ]ü ˆ>Ý3e5¿5ñ×ÛÛ<(ôpŸã€nì•ïtõ¢ŠO&ÇG”3³/KD&×¥±ÍïÙ¹þ)ëSho,Êõv4“æ¾§ˆ&a‘.[ö{ø#fx¥ÿ³÷3´ä¤hHY_o£×Á—BöÅ×½{÷nÿæòñ7‰ÙÝ»-x— GET§çÔE”’j›ÍO„}îÇ?äIU>’’§È/ÕL¼Œ—eŽïýû÷2×V¯7D†ww1 ÑÝÜÄô ?£™•z»»X½ß‚™óRÍ…¾ýãÇ€‡h;S‹ÃÉAiÅ÷£•áØ¨Å1cÆ¾í§—Ã­#L„ŽÈ²€‰…vi÷àû\Fli`é`¦ÚõÒj³ÉÈîúúâ†ÚR~@vˆž>JÌÎúZv7RTÐ-Ó@òpЋ1‰Æ ’q¡ŽÐvò31ÔSûjfŽJ= ¶DªËa?¿p‹ë©<¦Å± EÖô9?ù r¾GÌN‘m³1;Óê,-9Fß™?«öuQÌÎŽáñ F[¿§Q`½ŽÕ=/6N—S€ àÒi:eñäÕž8N—‚ ™‘ÍððOüÇ=b"¯®-ž§Ž< ¡VŽ·¸ú¶çááIca¯¥Ý—ŠÙÉΊÙÉ> ÆXnþ“þcÖÙ1n7«–“4z SØ'…ýª½­¢¹y¤´ùåüø<,{Š|ýøºXN^?/¾íòõ3O¯¢¶v-þÚý'”sn¿v‹Eüjß9v æCN3Ag€Õ#Î!j7+Æfœ1 Á ZÚÝÏr„ÉW:È!è@ÐGÑ·öxŽ-²6 ˆoð}¦Éà1²cÃç.Àt¼›‚ßåû«ÀbËûóÏ/ã‡6zê6’›ÛA1o¬·K2Ì!EÖÍÝkl‰Ùåc:µ£Ç t èÀ3èÙ-ÄwbØØÐÉ|„‰ÙYh÷¤1»€•B¬'è@Ð¥uà^1;¼2ª³1;ŽÕ˜ýÑùäÙ%è̘DëÂ3H H Hàq$³›g—$f'FMP›=]g7н±8_e%ZgÇg¹éRâLz2ÖÑh…Ÿ´3•x€hí(öÆ.tÅ"»„8ä;(8ZìÕâ½±ºƒ‹Š)•‘]³^ÙÞÛó;(d½ræ™Ó;(̾1]»R]XÓÓ›å^ù‘ÿ ô"9@J+Õ껵5<ýú²GoPòãJ@ÎCÂI×vÅ»Ôã7t°¤¬™µêåæªì øv=øŠó‹äl ÚAáöÆòö°yv§ãdçVÛ=Àw‰§)ø„¥/°~˜²ø{!§ÄÊpZ×§.“½+,óóJ€‘]çËʽ¬óÆšl¯v?1²Mev‚Ýë‘ì]«±²»Áivolð“O-œu#Šáð^쾺» º*ÛŠ±§û©[*”ÿ(¨ð.ÈÄÞØ…Èn4–i ÚÐ×n”±7Ö­³“Ó/ftéfcí:;Ùò/k[´_ggçVÄ{†çÓIÒÆÙÙ|Чìͤõ“NÈjˆ#°d³•û ^T´ôµH k]q»Dgaðˆ–wPpYÜ[ø:Y·76š@ÌΞ™çh+²Ukhñ*O!‹[®Ð`~¿J 5:ãd‚^]]b¸Êו’a³²’‚®>‘~>ÎëÚ]%’e‹h^!f—ô¤bAv¢Ù¦d§{,ÍÉ =‚쒴­ ×½™Œk-÷‘å@ Çûþx~‚f“ì„CÆåyø5.8ÎäÑ¥õ}?r=E»ÿÂep̳E6]Fœ‘urn[b¹‰ž|ÖÙɺg/³h„ÎgÆ0&‰q¤½9‡þ üÃOR¦›cÒx”gßcȆ£’ùÐ@M‡´oGãÄÑ·|v¼ÇAùïjd÷5bFœYO"¨Vù¢[OB'拇W]U ð¬ú0ÀÕã[[èXªµ½»K72öŸÜMŽ­ÎãOt%õí/ÃØ»wîÝ_¥HÌAwP³KlμžóBFv>ZGÈŽO=á˜ÝFyæ{s³sÃȂߕþõÊógÅìÙ¥$²‹Åìd6V-bñ±±xË^3~ ö †L6Z@êŸ>€þ2âùéÓ'}—Š\Ì, Si§tëè3ùÞ—âÇÕ—$–³¬Ë]‹÷ˆåa5ß±pŸw_HÜP¼&nø† ññÂ3\׊D¤°UòñMwFÖ8ãàÓ'øfÀ(8ZX¨´n#±œu5äÛʗƆ0Á¸6£Ù˜¾¥ÊÉßàB[ÜuòZtõ‘ø¼OÌÎÛ%ïLÌŽŒ?¯ &?pÿ˜ú¡3hž­Ö{;¾“|)Ô˜>J¹ÏOW2Fó¯Ý=!ÿ,%)?ÂV|OÛXn¤\6Ö†e8|w—ýÖSæ6¥Û`P¼|Ä2Þ¦cv™x+õ)ØW]ŽôÓà#(s§Ó°ÀØÁP³ÃmÙè±üÇeŒŽÃÀt¯öπ׊ö‡‡(ѹ{ΦL&bv%Bv<žå˜~~Ž#“–A"f'Ð… :£˜Ýpˆ¦å[u¨Lt*¡ñÄ5M>¿\¾)ïf–)uVèù«ÓÔO-ž¿‚M÷Š»‘àUÊŒì^íÃ+ðÍzÔõî¡«ˆÙaLss£1»¼þb1£Ô@rww—§˜eö¯Ÿ±/Pà†kÍwã|—I›¹ê ó®ê:;‡é2bvlMu]‚NXPëÓ~Éû"ñ°txŠjrÿŒb)в_Êwù¸¤•á<ÚÄìÐ%§2ë:£.¡‘©bëï`´1­ñÚ1â8\nÍ?¶„ïâ!S5HÌŽÙ%ä krvvýÄ£{{»i=„Ò"ºÅþþÑ=O´)ìvw¢+a©3ÅìÖDóÉd&ô_Òíø`C×ýýýÕÕÕ_²_dÄìÙ%qÒ“=•°8f7ÇŽräÏ㈘ŸLø%ë‹>~ܘ‡ÆàùñãGöNܺÑÜÓÁöö6à:x6öWócÅëK‚(ƒÁ¢ˆ/ C¯.w£uyÓ)\Ëׯ_©„¯˜Z:Þ÷¢ðŒT‹…°BS.¥Òj½Žº°8µÎNt/`•°rà}«…ã0Þ½kK«‡»»ÛùŠ×Û­­î|œµ¾¾~rrŒ·6××a‚a¹lþt±˜qw{#qXaÿe07úƒXÌ® ]Ò˜ Çìüyvp€+¸ø7 ®ÕõÞXùŒœz’Icº ½ÅŸgçr…ÿ>­ÐÅ¢yDhï/ψ xG+i*è§\ÐnòÐØV"_ùÁ€ÂÁg?¤ðîB  A±Áo0¸•¤ íŸzÒ¿)Cþ6ŽxjµtÌÃÒ r²Î.²ty´Œ¢£Çk©Áb~R9¼òøÚSâÁÙ”}°L}ÂÒeÄ7 Ÿ½Ý]XR H1˜Å¸2èù“÷w‚eo-b—lÌΙ!³ãv7Í9´]äÞxÑ7Ì`ô:8;Óépb- Š/¹—]O$P/ÄìD¯íGÛìfïd¢ÉÍߺ€L’ á÷*$ðX1;ºƒï‹«[Þ;A+Š×lÚ?êï.:“…›±“еïqì<Ð2‡äätà:Pµ+—éŠZm¥dóÎgwwPðÀ·B#YœTìî ¸¾>»aVfc±U¼×ëïÜž3«ó³³Øíb¯ÂK&ƒ‚^—Ù™;(òׇˆ]êñW£©D*ðÃ…;ˆ±ÆÖÙÁÒ-³‹ÖëËØ8<ƒ‚‚U(ìQbvæ<;BÚfo,³«;Ñrhá 6÷Ê9u~*ÐAAA–¡(·_[·éÉÎý|»dÎ.ôŸ÷³±2†ÕSObëìׯmIбuvjËB ˆ$Zg·È.©}Ô,ÎýIÅ­“Õv²§l>¦›êwŸ4$$$ð„0ëìæ`:?—Àk9em–Xªä:;¶wfÙN6jyO·K‰ìoðBAAAO!ÁU²*s¾EÒõD~ý©´àÖÙÙ˜ÎÆêyvluTœC»ý†”“ã}>Zhö'A&0 úôáþ:À{Ry‰Åì’ÛÿOg͉èañÜl¬[wBýSöÆ2Vsv4›”èV¢S^g×Íþ„ådätàa:àì !»bvÉÍièÚ“Œ˜Fî8fÇq¾[Ÿ½¿ý8ÈÍbt ®îþ"vɳ©8ŒGÁdü¢²¢Ïwí ø¸ËëPd5Jö¥à4œÇƒÃt¶BVЇçKàz<9LGtyVø ¼z 4ª¥­V¹Y§Ó2¬ï ÀI3´ƒ‚F² ìí èõnèCÝM;(šM=Ï.¾ƒ¢‚¬ý[n&C^߯"eÇ#\| 9¹Q¯ÄúüÞÃ@‹.>š®Ç³½Ói£Z^Å¡vOlUCùAÏ áh6ž”úëPi\jžì/ÐrÞA!çÙeÛ"›¾÷éhßg'¹å<»‡";\ƒÝÈîy-Îö î{®t[Õfý­Ó eHäAârx-:p=žèóƒÍZÚ¶ÆöÆDvýÞîÅ íâ{c‡#>€bê~o,÷—yv”‘]â¤âGÃ/æáÁÎáx«UÙlÕßV^ýø%T HMgƒËéñÅx°ƒ¸’6Ä쥋DÚ%Þ«'‹ýŠï]A´Ž,]jݼ¹¬uvaþ‘ÚâasOdXöh?t– W/Dâ`hÄŒ¥ûa.woÎüõ!ŠåÝ*9sˆÊ¤ÿ$vPàútž¹ {c‹­gq{Öš²g^cß~AÏ-LEþùçŸïßã>¢?ðT³š÷ç†ÕYtÚ=£ùè{¯³#4&<Ñëìß™uv’ÓÇ>tzäÛŒ+”pI¤‰à"8 x%Ýæùá´¿Âãò6GV~¶è~êÕ\]•C鮪¹_™x ^R,šþÏn÷?þX]ý·Òd=ÿpN:Ê—'þjÿ”þ®MA¸ß ¯$Þº7«žŸ– ¯Ï‘磔ÿB ÁÅ›Ýî&˜Á ׌«×е߽{G×}4š8½ù!|’å1ªÚþH…£oЮ<[dÓu—¯C֞ Îí L[g'Æ–ž‚òÒ4ýrØc\Ž9†vâfõ/ççø®€ÃÝq²’Îçy 4á[ÇóKà§ Áu8L¼å.ZNkgž\¨Ç¸•/¢Æ½Ô·è íõö·oßpÛ4îßò>†bà´t<ñWô\˜)%§¿ëS¸À÷(× âǾó ÂÔä>CJš/Ï…r{-p =Ú«Ry ë†Û&¥¡á·ä:´©wiéó/‘~|täaaÈþ˜cL¬­ ‹âÎþ̳E6ÝÛí‘]|ol*f'#hŒ¥™Ålšm­_¹Ç‘&=ízåèèz¼¹ÙÅÍ&ø_wsóàà@æp 8þ„‹ŸðÍå¯ažÁS´ÛtÀÿ :¤ÐççR&òãRí6zQ«uqñõäøØÓò]äìvÿ„UílnbIŽ| ·KÑ[íu”6üúU¾…®¶õçŸ|aÊñÏåomýiùét©Üj®^ Úߘ@!T 7ÀJ"üSa‰¼ªœžŠ‰¦â¶fÜ…ŸwËhS¨`"‘ršDÊ@¨m5Vìñ± ¯DÿáÑX1°±±ñ×_É×6‡[Š5̳èùô)èNÛÛ»>|àåVe‡‡®ÀÙlssóZ OoÁ­/ÄÓC¢^H?‚4‰·Ù îÿÄÓ…¢ŽH)"OäÁØ@ïógíÛRdc=ŸÃÌöÊã*ÝF_/.0†Âç0¬ä;¼Yà9òK˜—²á-/¤à‰‘iÂ÷@=_n‡÷Âö-+÷>£‘Wø¼$›àÓA÷ú}ïÅΨ­q6„R”&Lçôa±]’Á)Þ.³ƒ•åK ò1ØQ)KåÇGPbˆ€ê–ÂMŸÈô ‡x‚–<(g4â>à^o÷æòW7¡ÏìèÎ žù===ÝÅ%Û»»7£Ñ»»è Ü‹îÐè(×kS¢|Ëÿä­Ý]W¾upt´ñá×/_°"Zð,’¹ÿ¼@ì™)Cìçûø±·ã¢$Ð]˜ÈÚƒšŠJ¡‹ÂÁ Oo• ÊÂ%ˆnÆ¡üPD¯vò¸Ä) ÊsÍ9!X„làä/¯¯±?É€Wn§S)Ö[‡„ÆK9ö‡œÒs¤¥ù}Šô´tiHã(áÛxŒ§gÆ~ÄÓézQ\¥¬HE¡:°G2"¥®p?üey~úô W[{.../o| ÐIˆ¦!B¸†E/“ÌöB¥2¹J·ÑÖÎNŸ›ûãǾw]yò‘®äE ;‚·Dsð„ ³¾Q,Ic:E-Ä,JsŒtIÌü¡##$,z[ )H×ü2t™§Çzç¯\/RÈ.±á$ûÈ×+iÌnÁ:;a%ï|vüÉ­³»Mè*ô>ïò`è% w è !JÃÙÁôÈç,½ÖhÈE÷¾@8±•P,¡ÓÊ{KrâX:ya&ÿݼ¦ziéXz²Ý®µW+­µ:]Šÿ¨` (Õ„x¶¯|>üÓ#;4üÉ¿hàËô´4–ä̤m±¶„ox×ÏàCþèÿ0+Ò¯Òoù”91ÁŽH™­f&8Ã…õòE¡„fÅRQrÙ¶ š…ò ƒUK@T”"BÙ` ó8Ì,?ÎU•MÈÊ× …óXjS.Ÿ“_ž|äžõDëÀ À:<<´Â'<þøëæí0~P»ÿò_þK¢LÿOÄg1¶¶·1€…4°>×èvZ:½¼=ù:>ÛÊÓÒ¾ƒ‚Øàuvò˳K²ƒâjÌèά³{¬˜„Åã@C­]Ÿ‰¥ãór¡g"ñ¹ðîˆd>•Àc4ªßÝ: $E·C9ZÒ¡R@…ð6‚/„‡ô-IÇO'°Õ´ßâ?úw_2fò÷؇&þüênƒúðŸreýúû÷~l 7ãQR g9¬a–§3±i’S»Ó“ë‘ ®¬ä…o߯ì0àõ¦a²£ë_ÝG¢/1:ãZ.cd µõòôÂzAV®SCuÕ²ã-oÊ>&=÷ã/ü ƒ åÐTÃË0³|å "ˆ:T ûz4AÐ B½ûÏ“OÚÒ#º“%„ ‡DXþòvÈí B‹ÈO@ßX'¥Á®u6:˜™Ÿxv66¬IMÇìâöä^1;þ®¢Eü'º7ö‘bvz¨qiÇai6,f•b˜°‚8µëœ°_QžLZÕˆËɤájš­&bWWC®¡É§¡7jÒp—²ùn‚OËóK£)Î`â\04‹ºKëâ5…Žnï Ðx&Ñ= Æ=m»kd‰Œúûò½&!Zdg<ÐßìÉöûn¢GÎÃdûDt*?QN1G3æ²/RÜí z]½® à~õÄÊÓ'4"_±ÒX(OÀŽ´ÉLG²šNkL¥e˜Ù^HT®ˆ©l®<‡ž=Ø,ïxÊÇ ÃpJ'š€1%©ä,.Š×Zl hEŠ¿?7ñ®ýg¿¿-ÅþÖö»w>P lÌ.«¯‘ ï³£Ôv¤pÈNæauÝýcvÞïlmÜBFwÜN5¬ÝEð n41ÃßõïfÒ£תÎ1ì†fX[{‰ôûðç‚·5gœ–€=2€Cû-K‡¾L|'‘ßµàWàPw„x<–ÁÀA:Œä„'ð³Ÿ^DÛ<¾ÈF@Ú¥qÓñçϾw¡(Vt‰‘£Q@uêW,DŒw D‚À°Ø;X:Ð>Â…ÞÛûô å€7üBÂ_åíN§)=©®ô”Dt<äANd›_/˜~oa1¦–ÉA ð"EYi,”gw“,^A™¶I<¦‰2 PÆ8t,¦Ë1®\­3ۈƉòòµÎ”Of#à]ô -ôÖô"80$BÁ`RÑ.€xÐ4„i±QÞT‚iCoaòx£×í½½$fóï|@ª>$f'¶‚Ùëì(ª7Vz‡APa»®¯®bîìì3-X_‡!.y`õðOÌᩘ™q¥îË¡trñŠB§¨"XYA÷žzå»è g` ±¶æ¿»µ#2‰C_¦Óº0¯^càZ1xA_BïÅ"6ôAA!øv<Ñ‘ˆSÜßêVŒÎFvŠ$p“ôm[,äg†ŸL£“øKÎÚ>b½ZZoÁRÀÀ!Ez L¼æÉ‰ÎË<‰ü `ï?(²fAöÂC žñÌzAEa¦e\Þ{uõ_çÅ¡j•ì[ å‰À>ìÂd(°Ùd/ÎmáÈÌu’ÃsèXL”/¯ƒ+x,åÊÙÊ̺ õ?}êIs‹È“—ÉææºdC#Ö ¦ÃtÚr”Iİ(¡R«ÔÓ5ôñ»Ò¨ô^ ·?™·AdÿWpåÖÙ¥ú`´#bÁúÈ.‰‰óxæj6s§ždìåSOxi Æ e%O=¡‹µÝ‰P–]~-‡Îát«]Ûl¾ {cÓÝ饥Øùœ—ÆÛ=øÁF9sNÏ/¸G±Ø{q9=’½±©þ „õ}ð°®‚²æÛ%¿7–½ ýß‚½±b#—]g÷2qÐkÁkÅù”˜]ø½| Á;/¿žC cïhŽ/õK„á–ªÔSÆìd J8/~žbg+¥F½ÚÇš´Ý­9˜N°!»ÑÁi ¥`ÎᾘEb²¥tC“'yl|ô˜ëyÊÜL!žNým5ÚÉNv’ÞõÒä€Þt3ҹݗÆÛËáG<}Žï¦Óƒ.Ä$ÇC0•·rž]]MCÎXS,Gì<;É:ÏNèëì–²ë!óRÀ1ŽÁ¬^)·›Àüˆ–b:&<ƒ^±¾Ž&—£ÙÑf¥^3ûc]¯€µz”uvQÌNBóæ<»³{¹ñGØ»ÏÃÙ¥.àˆªþ-‚rxMrhÔJ;-X:FVO³ËÝA±ÿ‘Öæa:Ÿîï X8ñ¼x ™ƒ‚‚D@vøÉ~›o—Ü òy3•;(’ëìèï´ãõž{c_Ë:µÀçË\?Ú%´K†<Ñ:;±£K¯³Ã´B´ÎNFÎþZüPCCÐûéÀ#­³‹öÆrœƒ÷QðáOE΢œî¦nõH´WGÝø:È!è@Їë¯öågîÙš™çÙ±M£åÅÉóìdvÂßA±ô:;Yw"¾+ÐAA‚<–ˆ¥ÓŠ‹×ÿêyv7JFÅ!fæ Ã\|Ð'ÒÇìxÂ!;Áwdïp– £¼³ ñ¯Yk4‹Çnø8õÇ™[ê»o/=¾¶z oK}k)y>¢ü—úî=äŸ)à þ+fÇG8ë±J<•é³ k£õ’ä Çbü‰Ð_w¹Ôw¾Æ“9/Úîäm©o-µvõå¿Ôwï!ÿLfðÿÈ1;Av²#Bâwcvî>¾ó.'d„è'‘îð÷‹ƒNËY®=Å9à''g‚æ´ÅpøU.cÃóüœ.å@~œ¢¾±±¹¶ÖØØ{s¨Maø®qeOǬÏo_‰á&òÀ‡ÓEÀ¸ý·’È-ékx‚–ï‚ó­­IäûnèÞt|O¼›Ð+°‡ÒŸKˉ Ï÷¬¿¿¸À‘îL·Þã-á-ÍÊçø–È ÷Ûloïð,÷C%xÈ” ×Bª¶#w‰á­´œß²²JË×M¼Á¹#=Ý^þ“ÉÌ Ó<€7\OaëºÓYG­åÆgÜIÏZq.ågê•mOã•îi \[!rËâÿ=swÅDåCÓúãáSÓ‹Ù%9ÈÏ(Äbvü')‹O‚_&f'sþnC4o ŸHGÐ!¨#,îH‰œºKrn[àšv\¯)—§|½ {x‘çÍ"×:ïíìÑ|z_%:?nM:—£·ç×.ó»°¸ãèðïâ–K:j$¥iâÚ]|sw‡u £xâ¿Ä‘ŽÒ޹´L™€ôÏ/ç¸s}·×û¤w®ãr†£©Qš‡«!]ùŠoÉI±GGÇm\“2nÊE<)I‚‡´Lp#j 7&ãøµ5º‚ßJË9ñ-[»´øîШ¾ýýî—/hb'é¶ùãbÜY„û46;4¸Ì·d1rõ w€ Lp'Ž× $Jù™z…6n4u4nÌø°µ¥‚=Üß÷úãÛ üÃàâfÜèàùGùrÃýÍ ]W–¶`=ºGv¦ÀZžE‹æïÉ11»h–å+³£Á÷ä|í4‡kh,èG—î F„†ÉM•éò‘(÷´ãv7jÓ¹m{O ^¸Ê¥á2yi/¨ÎÝÆU­ö;Üz#íˆDè.®Sè´Æóë•ù]¨]ÊÃüࣰÈÐgÊd\Üíâ-<ù-ªo¦œí·lóäàë;üúwë@Ð"4êNõ5”|:Àá&B©9>hÅåðJxËÔ+§ T¾§á&qK1îWƒè|¡?–H eÒ ŸN¯¨|i#¼“Ò[ŠV±K±½±±uv<;GvKÄìÀ c‹ŸQ_hñ™(è| e°M¸ <]~FQ0Œp"™ÃCýí[µÁà#á޽·»‹xÊÎÎnrþ]l%IK[ ?77t‹Ä4Ò:à0å¯Ö¨"Ïx:ŸI¾Kf:eƧ¢Ö‰ïÚ2-?™2±¤tr|Z¾ì¤Ö+ËO¦L,™t&@7×£‘ã§sÆ’ºLæóàù30T‹Sº[ê›)gû-ÛŽ™r°õE4ƒeèž6Ý·…Ô·V«ïâðVÂD™<´Ûë°†P\—…†:â-CЬô.èL½<'“ñ7ÎúöŰ7ÌâÊDE‰“”þHd–ø?9ñüsùtÃ7*•ÖBvd—V%fW–Ù âl™uvø¶Ÿï žO*¸M( 4Qä5h'Ë“^òy@w¡O0„ü¹E0ÉäªÝnõ÷÷QÝ)ݧè ò#¶rx¸3¬‘‚®"%ˆßNÓ™-î¿kÿ*>\R0Ì‘ÅwñÄŒ‡¤omm£aÔxusZr"˜á®ûÿ–ŸL™X2éLxrfOä‰b»„$qÑd’–d¦LðÖÕÕ ÞÂsk[k‘)gû-Û‚™rˆÕwoo08ÅÝó¸Û¦{iûúb^FÒËäéø4ZùÚ%—¦+Æù‰;È%=S¯Ðv(|³Ûm¿{çÛWïªïvù®úؾ„þ@DŸ?ÿPQÏ?ÏÎÎ(bô6ª_ÙD:g3/f‡;(hÔÓáˆ|;ŸÙÙ^­ïõö ÞAÇ»ÖhLÉXòp„ã‘<Ìô”Ãx<•yÛÐ?«N¨Á–q4ãþ}íúzûˆ]PO~^Ñ8“K• Ì#AiUiÚÝA!æ‘òë¼–æ(ø`²œíYplŒ²ü\‰ñä\šúŠ@‹Ï|9À¯bî‹È ¾«õú³}7´õó´/äü©×»8Ljovr|Œ¨ÿC¾;ß|ú„õ) õäÇê•DëØÎx` #'•µK2äMì•ÛÅ\ÌŽ‘ÝRccÙÝæÑŠìÄ¯è ‡ AChe²¬ýxHÌÎYÂfPí‹(´žEvP(²3{Öœ-gtÒÙÅ9ˆOrrXZ?±!)ºþ—³¦Î³Ó3KdêÂÅì0’-²7–ò”c8®Ý-b›Cžâ~,È*ÈêçÖ†a‚ìŠØ%‡SçÙ):“î’1;öQ:Š.2–yŠìK y‚žxˆäžg—³“ÕÅÅ}… Ââùƒ² :tàét oÙñíb4[ÞÀÛ©Ë)´–EŸš…™Ñ¸ L(ã_;{Z7¶ •-iñg²LSš[NÈ#rÿlÞ¤`)?Å›&'øŒÎv‘CûRO“àÏ>ñ{‡£s¸è4-™áYÀgÞ) !=ݦ/S&ñVævןªÕwdŽvét³û@ÇV‘&ź‹Õíx?JëžÓEa.¦—YeÊdg¬oÆÊŒõk-Íö©˜©È*GÛ×Éjîv’I¤“wžÝoõzãÒz"çÙ­­Vû8žuw+i¹r5ÉÛÉè{î\€ôYžqë`Î…Š•’”µµ˜JÇ¥œx;ú§3e:m¬¤+'V¯9ådð`KË}3ü!Hà^í=Oë­OY\¶Á ©r¢n”SNdEçñ“Ä.j+£2H7(!yÖ¯³âÞJ¢Ÿ¹^mïÓÑ~¿5Jžz!;²t|ê‰ »£»sεv4ÐáLž A^Žd#»Õf³#tífc î ±’+ :tà¥é@^ÌNfc5~$Ñ9õäás"a^)È0è@Р¼ÎÎÍÃÒ*`=ÏŽýEÊ%^N{ÐÂ:;³Ÿ7øí—æ·?A'çëÀâuvÎÞ² ëìÜ|T¡½x?Àw…õ÷O·þ>Èö•Ëvñ:»ÙQä®Ð¹QÁÇt èÀKÓùë솃áØd—º7Ö¯m¡ùáÙ4š!¶³Å¯’öwqDëi^Î|S˜û‹Ö4DƒÙ£_©ð¢uo9û¹ú—YM2·|È­¸Î[gÇëqZK®³eÃc|÷ÏOòƒbF#^å$áÍœUÓ!ý®ìåV¡‹åuØõÊto6ŽÆ‹uì§ì_Å› ÕÝÜP³ÙËØ¹êƒyë좽±„ì¸W/³#K7k4°tñýd_oJ¹\i4Ö¦SÙÂR{`_’Ð.Ó)žXº×¨iåJûIûWñöBû6ÖÖÐÖÅû࢘›Dän¶LÌLÔju£mÉzÍKÒÝüpw@ÖÛ¿½?ZÙ<Î|]1bÏ-ÇIê óÈ'ÀítJ_ q™——A» uDIžAøC¬oEtl©þõJå0_¶FJ·ÈYD­³“S@y^¢Lwȵ£“ÉÄc:€Fõ1EF·ãÛv{}x3º®Oº×‡›üìZzw«5¸æÕ~ÑïGÑ%­Z±;œlqßû`Y‘K…M~”ž<ä» tl©þåûÝl<¾ÙÚºhµ†ÍÖÏvûjk«„aÊïS÷”÷D²Š…tlÁ:;AvZ^ÑóìÈèêÉðŠÂ˜›\º<ÃЭ·šëÝíÍþpóàr½7\ÿ4ÄÓÓ'ç£ ©yåhHú‰óÈW mýIŸò<F–v±£¡{ëÌx<>:>†z½²|Î×±eû—”6:9™›Loëív}k ÿ«¶Zw··ƒFc|ròüu¼w»°eŽì€´xýÉ#Þr1;s¾{ñ˜ ›»x÷2ý8hèµµµjµvWz[Z©V*õN«±¿ÝêoµwÛ‡ÛíwkõrcâzM"‘ñw‹”ÿ¸y„ñ%î4 q½çŠoêY5Šÿsõ­ˆ>  ­ÿy0šU;›{fDò 2‹|7:g-_Ç–í_ÈKwÓÛ«w7k­w£áðêèèæàþêÚ¿îìLNO3ûfqžTßä““ ­{ï`kBæÄŸgçÝR1;² És£gË£ÕNß¸Ñ ûê—£Éþù¨wr3šÎºŠž´'oe”ƒ9ßD:§ˆ‡/”?·¬tq$r#_ö3þáràÑQÍ}[ØÖã1Yºî‡v{­Ùît·ûstéêêªÛÝB puuµÛíÞÜŒ|ùõZÝÓ˜ÔÂ_ót2ž>OÇ–í_½^õz•vg6ž k››­óóæÉÉÝÕÍäæ{Ûé|ÝÛ›MÇsxC½À?êˆßÎÎ`c®M°r+ÒŽh÷"ºW,fÇwÐÙ‰­ì¨œH#ˆ,‹VOŒ€M¹üá©j¹ŠÏágÃñÛJ©Ý¤:<•[N^ù®È® ?ypJðl!ß¾eQ!=99¬®ïôN¼~N&³ÍƒÎF»R«N&Ó ï§ÌÔ[BL7‚muüºº‡N§ÛÝ$C g5 :›ÁÚŒa)¶···ºÝù}Áéê<[¶]özåêJueåúâbs8llm¡{v·WÞ­AaîÆcD*õÚp·—ÇÛdBü£v¨#~ïÞ½ûôéSžLžîå¶@V‚E‹â1;XäøÞØûÆì|¼‘½Ady4tÏV9»cÒÈ®[W*udûn'|1ãJrRå‹G­7ÝÍMÐ ~àgg««Íõõõ1ÔÜ#Áy<«×-èOŠøœçqpq4ž ^°P÷ŽOÇg—6:W£i¯Oq+èÀ柽fk ]h @ôõæìtÐßÙÌÓუ#».Ì%U*›››[[Ûî¤9BšÃáÎÎÎááa³Ù,Ô+nÊÔ1I—ßÂ:B·XV{{s9lî씪ÕÙdrÙÙ¬¬6J·Ó»éøëÅ` |‡Êsh+®ÿŸ> š¨ꈚ¢CíïïK]ƒ‹6‚€F«Õº¸ ?WŸ6[­Á` m¢{½=¤5WW5JX&7€b½t<Ž­ÜË*’@¡q•Aô,[•³‹ÙÉA'‚N$V)_Rï§X¯D¶~Õã‹1&ˆ š·•j·]9º˜Ž§¸÷» &˜¿Ô»sËï÷ûðH777)Z(ñ]|exy gµ±Ñ³*R¾p¹„? ¸¬¸ï}˜¬üxB-EBÇRzrr†iÉ\`£^Çb€þþ1,]c­K±„et;¾¸¸89Ø^[kÄtՔÚ³auRàË_:==ÙÛÛ;99AÿOê|¾ÞÎ×1§{Nõ/Æn¥ÒtRû°QšM.77kúìz„S*¯†Wû‡w°ñ·ã ¯–Ïì_Z3ù‡-CÑ¿ðìB:™ÃŠ¡_íîî¢J™è}ÕrEŸŸ#¿|ëèè¨ÓiS÷ìv¼Ü ÉÊõÄ‚c,‘[üibv2¥«³±…×Ù±u73bÑÙëþlÕÈ#±p8ÎrG|°E£!+ž›­ÊÅÍìâjB)”´ÂRK½ËåC_¡Oõ=A{}…çAÁìy>]ÊÁƒ ¸«NgÍ0§|ÿ]ãQ ù“ÇÁ,Ü}ñpŽ|̎ǤÙzbÓ[kuëÇôõÉj³qqu ýÁ$ {2¹8¿8ü¸…~Bg,úÀ‹XjË„úaЧyJ¥««˜ ˜¼"üÔ±{ô¯ñåÕÝd (wÙY_AlnpQº] ¯:‡‡Õv»ŠúŽo¡^yú?Q¯L9îKÿÂÎ爃yÇ•Œ!Ô‡­-ôµj­Ã'ß ÎÖÛ¼Ýn ¼ÜæÈ<’¿CvÇX bvöÀù{Çì¸PœYÝ> ÐïÝ8ZG-0y’@ éyåÏ×±eû× ¬X©"7]ÅÁãÈ"úãù÷Q6fñ˜à;Ø‚çÙ9ïêb ¼žÅXúlš| âte†ve %Ê·äKðoZyBé:µåGݶëIbåc¦ìkˆf³›ËKL™ o‘+•µÊ2_ÌçÒþäaq(|.`ÂØÐÄìrõ!­‡Gý=àŠÑÍ-å,Ï®.û½]X1Ö“ú°½½{r‚hû):~Ð4,.ù¸»›xˆ¦ÿñcoocRÕ±eûW§ß¿-•«o˳Ñlå-YºV¯Åããã›õ÷+{VÊÓòÛV¿ŸÇâq˜u{7›MP_¤Hoo±ý‰†±ϲlµ×§‘"EŸ?£¯ûŽñº|«³±AÝ¡ó!&{EdîóˆVH_.ÒG|ß—·¤0¡wP${?2†óí(YPk¿5;GrhI÷ø¨¤tSís²cÊg3Ëá¨_VùˆšÂç`ÏðöÎN»ýÎåqùË%Ì£!{»»ùåGß&¤ŽEüIÈSDg%ÙE¤€îá­ãý^­R §Gû{»íVsŽ®ZF„øøèøb0ö-Â\(’GøƒµÎF×ó=\Ç–í_˜mïï€éޖ靈v¥<ÙïÚ­Ù~¯2Ö*ÝLaþzLÔæô/Ì–žœü…ªµ¨šï¯®¾~ü¸+õúøñãf§ƒØæÀn&ÙV°4 Üx<ÅëÈüîÅðغg£¿B†i¹åõkÆQ¯/¢?y1;¹]lx:¤C‚ìÚÍzñ{cH—¥Va½SÁÜ¥i€ü¯WÀ±Óz³Óú°‹L•jiûmÚ;¢á…¼†hÃÍ~—âkš 2§Ì‚éØ˜ü¼d9„7ÓAÓ†»Ö{¾»SpñSåDZ?ã1€†à¬‚:àukîЫÛ-ŒæªW u~o tl©þ%ߺ9==ßÛ[-ÏÞâà5º/p6])îf£R¥Ýï¯mn>˜çGèËñÀc@ôDXYEóõ*qž4¤³VÁz AvÖœÐ:¸%bv˜1Àì²Ø^ï]óhÅJ°3Lá?Ó ÅnÆDccžH§µ/âyŠ”Y,x’¥ÊDfTMúRò<¶åñ"³bR—Õ“îf§Eqºû¼»ì·æçŸ¯c±Z¦±4››;——wí«ZãdR:+U.Ê5üskx?=.ÿÏ#C‘’‡Ðøg‘;("ÔïÖÙé½±§C”¥`¬Õ¬c:¹È½±ø*Fò™ÃÇÒ±ØÀoÖï¤i ÚŽ?±¨Þ®w¶UIQŠ7–ÒZµYoƒÖÐx›CBË &¡O™ OäO¼»0Íφ )ˆòÐéü‰t¼§Â2Rz(_ÍtNWmË¢0n­ùº7G7¾‹ÁW¦Ö‰.§W–ÏE:¶lÿzHØ¿ÊðÞ¼IODƒ³·ZʱpŒ•woìo˜ ¾¼¾>½¸ñ£ÈV½ÖûÔßÿ¸åŸ^r;éPÓÑø/,-פIe:_ìçø¡#]^^Ò‚>XX»¬º‘Oȓ֙G‘ óK? O^©âѱŸµ·^Jî,t?͵K½OGýýþ×›©Ÿ©hÕiùíCcvªÃ€b OkVèˆqüõ*iˆð–Ï•CÓ^{¸RTîöv„ËÝ_£¾Ò±Ÿ´o/œòö-VÇÆn}¸_Ì.Ùõ?õû»[E}2ŽéDß»; L0‡ÿ7¯—Ʋ ŠL—Âò‚Ëãƒ{— |_‹«¯QߊêØÏØ¿Š·Î5§µh„9c:É#wP|e#»ËûÅì¬}eÏ_”›×0* 1²×‚gYë^¥î-¡c?]ÿ*l+°'„VÂ-!«¼˜]bOÅÞkçD^§¶Í‘xX[÷Zæ _©¥“É`z™Çÿéú—†·׫ؚߘ-â%E~6VÅ\Ö<óàdñ½±×»SÌK¦Ìƒ ‡ AžZØšùÙ ™9€U}Ð:»â~©ˆï y‚<ƒx¸ðt•{Ýš!BZz…CvŒï ï x-`Õ A^šðš>5v™{cÝÐûÆìnƒO 2 :tàá:·7VÎÀbØ'cÝeî ñ¸‹ :tàeéÛ1:CÉžzâcvrF±ZÄÂ{cnƒƒ 2 :tàñu@&\£9ÙTÌNñ]ˆÙ…ù²§ž/ å{2ìržHnÌNñݽÖÙ=¾m.¶þ(|7à‚ A’ëì`éüý$²œÑ¯³ 1»0§öÒæÔ?A'ï©<Á˜Žö|ÊdD´Î.Äì‚o ø(èÀO¢¯³;(x%JÙŸg7BKóYQ3ñ„sÜ.¯pâžAAÏ,Ùíë}fSö öj…_”Öµu8͉®æKtE’íF¹¹Ú¤SO®¯¯OÌyv8ƒçôîÓ£ ä“eŸt4½%‘BÌðò¼Ç‹µž²GO8Ì£ïÍ¿ÕÝ礌´]»SM…¾þ<ê[Ò âé‰Üœ~štEt†—3dÒñò3¤±°¨¥óûI¥*ºCþ%øÙy°u—®*îÅ„ÑÇ·°—ÕÂØ5[¿áò‹íí½Z³;å¤Áw-{/ÜD®?`Â;ÉÏ'¤Ÿbîb¶²¼²2s‡@]ºÃý‰û”Ž"eÞÎëjé2œSUïî¤^B¿†ƒ®,ŸEèùxéE<Ý0:fÏ,‰ôM7‰?L=M«äÖÙ¥ã^pYÏç*Ñ&b}‡î‹HôévԇĞÒ‘ºvL{¸‚™_õ?sNöç™Ã׌¾ö÷û¿mllàžÕSÜî{;wb-‘¶k‹S¨±Ê”µÊÎ&ì]ž·ì 1âl¢£ÑèÌ ¥gÓO`7 Yak»£Ãübó¹Ïøs®VŒ­5¼izYä%u‘6MÓ®Ãgõa‘ï\¬cj$šâµTs°Õ²Vi‰ür6­®r˜C§Æ4ÑíÚãÔê­”q)zM¬¿x{çVÉú¿ª‰ÓzëM ‚©ã·5XCÖ¬•qîÏü;Fë/«5º´š½5 ¥í™(–vy¸iø]µn,ì}ŠŽ÷ä'ÓdY„\§ÕÒqz6Gˆèâ3§%çü§Çw1> ÿŒæäðR}®p]Äî<íL}kæv~Šð/y¯ý‘õ½O Š8Û'zböí=Oß²ñ]´LÍ­¥¡WRÚŸ~añ‘Њ関m4Mp–+'Ž¢o~M¿ðì°…Çw,ÏÈÒ1¤sÈέóçyøFP½íú5žâl«lS))òU ?IëpEðùàŒOÿ¬ü›ÿïÿÛ(ÿÃÑ?ÿóÚ¿û¯ÿ›ÿæß}ÿþûß§G~Ÿ}/½AæïܸòsÑ.O 9A¯¼aLºô]ñÝ›ò÷Òì÷Ò›»ïôÄ<¦û~÷½ô{‰ž+ô,ÿÎ8ŽS"ºô]ñÒ]þŒhz6ílÜcûŠÓ’sþSlœ+3âGkô»?¬™jWâ:Ê“ªHÏï’åwJ~Zºô=ú®å!M[>‹ÒÐ,_~œææE1°qÔú‘>†”¿‹žŽ1=Oßæ6¸ð AG“’ÎÞˆ¾9ZR`Ýèéh²SËÒTB©S¼à{Rt”_¾åys}JûŽé)Ò_ ZÒðïßß¼ùÔ› 0µ†ß}€¤Hàá§Èö‚¬ž¢n’’ø‘B¡s–J«ÿôæâÿÜëýÇfãßþÓ?ýÓo¸Ë·–àPõ‹á%ĸ»½×í~Xf8+s‹ZM‡þ\ÈU:Oý´¡¡Ç¢‹òœW÷_'ýþ!8«cLs?Ñ2@k”'½a.-}!¿ïÄûK,¾äzv?ö¾ûK~Q¿.\²p-Ù”µ^ªÛ/2¶Ôé Oœ•åÅv»ýáÇÙÙY žÿûùóçÃÃÀôÑñ ¤°µ³Óý²fû¹‡§);²‡a®t¸+ ƒ#º¡5†‘*ØP|^œÄ§ÿd¼©œ2ª¦’¿òyÅ4We'ßÊ%è0Àf†'výoÀ{Ù9&¹c9Mo²³*GýT!!*áðn–ÁOÏ¥%@*ÔïkåmåVõ‡È§Øwò-›YO¤X¶¬˜H]2£q\:)·T‹êòaLfRcD­Û Šk¶l½2K¼áãU­„d*—¾[DÿVÿ'[Îb[pŸ2Ø·*\…Œ%¾½§˜ÅÎ:lþª’C™ž±ÄªE¼*F‰.1ÀS ûH\ñõë×ÿý7*ž÷÷áááÆÆÆóÒȼ}´½ÙÛ;˜`‚ÏIªûùÓñÏÿŽŽ~ìîAs¾¬­R[ÿïøøxzïðpww·ûåKÿ!°.£teìÖ­1•§™%’Þïí…þY¥UUˆ¹¼0c_ݱ•G¨ìÄ[2Ì”pÆL£Q¸?>6fft§3&îù * —gò,«W.ð=R—ç]¡ ‚d™Å!åá14—sd%sÏU>"%ˆ‰¯Ê0‹YãL6Ìm—Úë…sB+m‹!M­}–cTA‚ª×c^§#iBþÚÒ?é]‰“¹¸b‘%ÈEzl6f¨¬‘7Š%áG¬™ü«ôÌ:áªAB úT£P¯{•L³û¤tEùˆ¬JñM6ýX_]Ïë5z0«žF^"/“¦4Rl};>„f¡ô¼øå"ÛÂ2†T#iK”Ä‘¼$wÑ ) ß(ÑUJ_xϫ¦ ª«(ÑÀܸ'å.e+åž3ËUŽ#eNý.¦,••Òj8ÌGZw]…F¶>ƒ€UcS·–~îÍ]À™5sZJ]â»ËGî•YMç·Ë*—6J}3ƒÑãx¾˜éw3žgcQ¸ýñõ>êƒ"š—©}éË} º‘ë…ÜgMœL úÃ_íM$®ÒÕ{l_Äê´››‡„VËæÜ1ç_±nh4ŽŽz=d4ÆÜ]5Ë7­fãhókoëÛÃ͈3ÇDˆdõ¹KE Ùq•G]cFàL$]ÉÄ¡0I ÊÑîlloýüïµììôtkk»×ûHýóÓÇ»ßv××¾Ü>5Ž*Á¥‘2 ¹GmÎżY Iårá¨á¹H-½d¿e¿£‘ØMÌø–¾<4Û¨’mæâ4õ’pá.Ó}”˜Õi}Ðs.fF2§zÉÈ÷GtÍ‘wA =°–Ÿð8ÂmÇ”8â‡çfÒ˜éIÒ ‘Lš'¢¨ô‘üš„òMÏ óœ)Û°2dÊI˜+Ö¨ÑÀ§˜\™¥\Å ¢Í<±¿J˜ïg²a“»¤ï÷÷Ò¾éɈpYüª’×LYþR¡èÍJ8öÙÈOÇïWÒ—#¶6=š[0¥ŸýBBÊZ!‰†1Ÿ ôDäôMÆséq,,A™’T*•sä ðèù¼B–F?£ŠpK3-t.±HœŽ>çnD¹‹bª ¹l\ÂÐ 39<`®š±87<=å·Ò»$ªuè/!صï‡>›d¥ $Ud™(ÖOsP’”%®Zy¨@ou˜3šf¨gA´9\ÁßDdÁ/˜ÑR“Ã^Š3#2 ="ÍËTÎüsiùye8æD¬ìµ¤&ïJŽ–Ê$íÕ|Y“õ{^Wf6×>Ëz˜ ‹~†1*ê*µOÐÛÖž¨úœÒ «,ó$é°Öb_][k5›777ÿ[XXØØÞ^YýÂJ¤‚ò`ø]šE'zÓü¹`öˈ|Iwí¡ÓQÏ4¿ÙbÉËÙ2dÊ\œïM½äíbíóª82V (‡§£@2/AIäõy¶ —ØuвÒÕ±#ZŒIO £žŒ:Öh?×™#ŽD¹rŠN§üùçâI3MÕ—íÚ絉Šé˜œªu‡º…–!)šJ0Ô°*©ƒ_¡Ëü­3„Î 2&ƺ‡ð…6ÊZ«ô‹ð°j†þÏm&µŽm'saè¢y•çç…†¤nQ¶Ï8µfQyã¤d œr‘qJËicäõ!È9ó¼8YHuãèÉm!e +„ ŸJO¦0mÓ0榖Å¥¶ ¥#¥Å ÷)J3€jýRËÆueÝc­K¶"ôTÖ´jgZD«Ai2Ži6fiÞ_ùQj;­o˜]Â<­’‰³N(OèËi~¥6 ¨îeb6‚<µÅ u0*jÌÌΊžØ{„Ô *2Ó?I.$FÁÆFgè¬bÊÈ"h ,Ö2?1/‘X¥|èׇV`nwæ&è¯K!*oJ­0wØ$Ï sÿ ø±‰žÄu›jÚ%Ú3äÛÂZô­5ìÓƒŒ0˶r,v(CjÓ0iH™¥êœµ: N…”«Ê©9’Œ¬–rÝûRêúU'¸¤þª;’Sß”E mk–¼°Ú¬Ÿ:H±M[YÐ[³”Y«Ù’µÞEÓ-ê~M1Eoe¾á°Æé ·‚Lê$I„Ež¦Û®Ÿý¬R•Ͷ)hP{íÅŠª¥§nÓ”—E—ç>4»Ý¯GGÿb¬HV(Ö"%‘xEóÒò'Ø©[ê­ ø¾ Æ<>>½¼¸}Ÿk­ã€†WV–W××r´D}¼Ù[ߨ~„á}±Bw^`©„„2Òfù]^_Ü>Ș¹Üiõçõ çËO:",i#i5–ÅœäˆûìÜbocãøââöN‡ïüìÇáÞÞÿæ...AQËZ-鯪„Ìjv^/NÂôDP¯\ŰpÌ¢£¦)éI[ǘò¼äJE,y—ÊJ®Y0_Y¢Ù·biM^I4Ù÷ãÜV˜(Âàc NÒbUZG†Ngܳ“æÑŸ¥M夊};‹¤%bÂg¦-èÅÖaê7ÀŒ‹nŸ”£Bж‹¢ð W¶¡Ê›…µEg&ŽÁšÍ³¸6STl]²ѹAq÷á2¬™fAƒrRÈ"ÚTâ€ÃŒrª£q¸Ù2ñå%»fmšºZh£´60ªgî ¨N¹¤õCì 2ÞÇË–­b˜Eá¹Üóz[*ÛHaÓ™ø-5òë…QZðthÖIzqÔÊ XŠÚgf ¥ÐCjÆ–ZŠñ„ÑyV@šû•üJß…2£bÄaÞ}£·KÊÑsèÐUX33>—ÃA‚V¼{ö|<„¡BÆy. ß1óÊh&)‰@‹y©üi´yˆÓuˆ|£t5²Œ)ýÑeŽ65z©¼‚|bù“”du‘ý:Aø7éj¾.…®ª)‰þäq­ÄÉ`n²÷%)rëP¾1ýáöÓETmjwíøµÝCú¹ÂçWù2ëŠ"–9anƨa=Ö²e–p4¡ŠüBÌKg%‚…ºÝ§°#ˆúM°3αl¬]ÔAIí2³˜ÌYq…Lb~ ªž%` è'ái®— ÌÎÌ Œ±r óW$Ì#bά¹á…–ÉSÀ¢jƒ­ãˆµHð·äG¡ÄPXé â•”Kݲ,ušsíüÂÁ–š¶Ùejž¬½Ö’œj<‡t°=H6ÆAìlmÛe¸ÞÝ<<ÜÓ’„¼0R÷Ö»;»÷çVf áJëŽ9\™c$&Œ²´aI“,lCú3ù­-@jl>Ô™tÐè|h,..þ¯3·ðûúòêŽúLœLkŽƒ¹nY€H¢%©úÅÉZ¿zhœÀñ$±˜áV4(ËmçV?Aµs@_kÚ¶Œò:¯D=Žã—–9_5ów ¤Ÿùý°ê ëÚX Y7Û+§7Ù…ÑÄêqèù<ETY+‡ÇRmÊAÆ—ì,,K;ÜÍÞ))YD“œjà0ìz:Žè-Ä·˜)ƒÔ¥Erí¢ò Âfç×8j;WÃÍg^H’e¨)E«ª–’„:’ÆVàdò:£KɬF·È”½'W3.C ®ÜéIÚ‹“öå(2Ëãâz‰2¤t½TÕ¦\w«±eÇ×Ì»Ã# &Ž6IÒ“”MàŒEði¶àŽŒCyo@–qÏè!KFfÍ€e^Œ¼‘ Éy6gûÁ¸J®´ÎDøžöð%¦Ù «¨*Yd/-¨ùÆ&çþbGQ%Á©øÅ ê„›¸¥d<‰»:Ì1%¢b k·”zá?..s±øŸ.âe±ñDyym¨„í8ÃÏ^çÝœA÷ffñ«E*±ÖAê¬Ò˜ö¼¼FÊ'h‘I;z–ÅlŠ‘Ÿ4m\iØõFV—´5ej]ÿK*FEùTÍiVL)ì©ä›GÕ¡Û•ì,] 5’îÁ7jÉxŃ{KF?©W¸R|Ö([*sàVµÈ4.Ñ›Rj™ñ^a%`ìRÂD=0K%@y¡ßegºÂ|†ÅЊ+¸.áÓO±5M³wH䟒®$Í…âò¤Ñ·†¹2‚m.º¸ÿŠíDªÎõf4£ÒÊ#ªÌhL0àB €Â®Ó6¿+ÅÔ'RGJ]úoÀ…Ù¢ÿ ,õáAûpŠ êIOàPîàðp{k«Ô™R i`gkçd(¤êÝÙݵ%A!¹ã^·»½sy§eî”4ÞB™¹>ŒÌ" ã›þÅýÀbè,Ânv;m‰©H€ÃÖ’6;,5fzÛ›'—ב¥F°2Y\\þßÜÜÂåõ%mLÔ—t¥%>??W›ù8sh ³ŽØAs¶µ¼¼2Û ;Xc£äqv®få-Ÿ¾õyhzQ›üß•®-y•ÌëšKvž°¡˜£ªæn„ ¡Ðܤ¯sÙReŒý†m!žÔB]²ûv¹$¹…dÄ@i¦Ô· ’–™›QNüº$’ŠHšµ´k~b5Œ{µƒó˜0\@ÌTZóUŽ]BÓúX|¡†ªð ›ˆ¸ÚNÏ ”ãUó=Ýp”vœIÖ-qMX€ÁÚߨê4úsÄ€*XV2·™±/$¡x´DY˜5¡:Nn]§xHã›zq/ ÅÉÏ[áW¦D餱;âÃÈžAÕ±/( /gs±WÒØ­_cmkê·r]½„BpO(kµwä?rÀ$-ü‡Áñk»I3ÉGrLÈöˆ<æ¼²ßñEKµ‹ˆÆ²²&$aøi‰–º¦ð^ÚFZGíúZ´ˆH0,"0™çš-RAck1«Åqœ»¬Á¸Å1+ÂiÄ4;q˜¥–r†t¤$,ÜÁóhÙôyÉÀªÈ*Ü Ž†V£w±zaÁ3âz³èsè`ŸÉñ%òÊààX~Ô…×Q@¬>yK¡€j…Q>hPú×¶vB'½²è“dÎ_⌦ÚÇX![´î¢ÿ¨©öõQà€©Å÷©4/éçªڔߒþQ~À¬™qÆðʈ,kE-³e–Œì¶‚\^ükÞ6 ®ÚYoÓZ1vÈÈ7“ä“퇴,÷SA´IWåÕ óÔ˜éiä§iSû¸hµ`Ópi»ŒÓøUÂö›3•!Ú<OG¢Fk“Yl¾Zàø:nqM³?ƒ§Y‡[-J^Þ)øýÐr¸'À‚=xüØÛ##1®ždR4é°Ï•.!©#T>::Üè‘áGü¤.@mwgW- 3Õ׌ ò,5ÐI‡Œ4êìlë¸÷,uÿü\0´¼’D78VÑ‘<9¾½¿è'T-r`Æxz6ͤ"k“lè„qæ¶·wŽ/9‡§s­Æü<±Ôs¿¯¯‰¥Ž}˜øììt}e¹ÕBÌw¿œÜÝ=ü¼¸XYY-4AX7¤ ¡l*«FØWW=Ü,ß ì’RË–C]‰ §—µL&"”‘hwÎbƒH8å¤ýq¬ä®&…‹ƒû˜â~zpaNž\†Ð°œ±àf¾ Ž'í¡þ¯=;|\á.FÏÙ'˜[ÇóÕ)Ž/ÒqØÎ,@&ÊJ±8µý™“³ênP·¬)¥ÇA‘­âøÜs•¤ÁÇqµRÖLƒ§C[hš™Ö7=JdÈs¡¶,£žÕ1§¹-ÇÍX 5®4R×ÉÔ]À|Û-‘•™¥˜ŸÈ¯^̘̓®ºÎ8,†Fµ1+&–af ™Á¬Ù|ËÆÐÌþ׈›Y¥bÇSíÒÕ]Ämi*Ì!9z3§?aeš¦+7Õöí(MÊù&} ž!ì`[91‰¤Èe¨ZK¾™p¶ÜkĦH:@¸¸°ižÖ2Xl­’Iü=ëCL'ÙÑRºY67õ÷¨±¡ïH½’&gÊ\E&,â ŸÐßï©~ UŽ”d±ZZu0Êj‰L‚<3í¨p^w¡!1•Ul/Á£9tnJ®CZš 2÷%òâ¾d’‘Uc-T„Üê2L HšÚ?èëa„y6œ—?ÒLl.cJ³YL‡T|9en;Š–´‚ªa5?=ÔêI–:~àzEÝŽšæ¬1t ¾¯mXÉ—YhË<’G7y ¿ÎSVÞ2sMü#àJÑböCG!冕Š2ç–ÕYOD4›u}ÿ`.Coü½Bß }Áú­‹îdhÓyMu&âoÚRîŠã•û³eÖBEƒ0w'u4øXÃò$Úg+Ôµk}o¨-µ`îÀå×Cb©Âèò1“‡’7"ÿ0žT…Û³­ƒ½ýíí­[°Ô±Ðœá.À(Cç^¸t'þZú^iœj ‡$j†[37”j_°¯Êr—‡óŸwVŽ®VÚ‰3ëŸo.®Ÿï^\öæâlT[„šþ«•óß SRëX”ÌÞgè‰ÑÅ /ƒª%a=1HViÅ™û¤½%²ƒá£@àøBq2kÄ4ñ¸ 3Ÿ”M152[\™ÙB¸™ôÝDÊ8…´CòMˆëéâ ëPuu´ŠˆJxÑ¢„Ñ9¦M'ö¢R™ÇÜSY0•R¦á(tX›Œ%kj/.4Ú0%¸Füš´ÑøFM;i2HW±l,ŸN¬Y ‘ñеbüª@­c¾„CÛ¥Ø=`YVÆ„ìÙŽ3íŽz«VŒ¡óÇ•^êS¬Î²"U}¶_tl8ko Êbu&t J‡W839Š>Çe‘Õä¨WÔÖ HêØR«Â ŠßR"R×¹SMe¿D…¹œqæ¦ÅÏá‘O’’¨‚Ï Z²,5khèa8¬ÂmužkyP¾{±jx`<†ETÚ#t4àUH\…V,Te<©iKQ~ÙHnŠ3żЫ]È·þ‚§+;-¼zýW0 »HÖŽó ¨£R¦1›„µøQ±aÐÅ sråªÌY›Š¢¥2÷‘gØR”_2VöÝØëJ –YÚ²©¥s¢±7ê2~-úl¶8!|O®É镺úÕï'2žè*‚$=ƒ>¨vÿÚÇùµíÍ·yZçD^½S™ôÕ7 jú5‹§sjÙ«¯Î*™ž–ó\uHΖ$òåü÷}}Ù„Ã3±¥Þþ¶s{‡ Ín;?9½ÇÛÕ½¸Óé,¯¬äLÝÛ­æñÑí›Û~Î Å èëCÐÞÆN·×‹ÒÈ<ßÜÈmÎë´Û'Ÿçz{GWG‡<±[¶èq&°þÂXE½3ìÕãY³sј=â¬%÷åÆýêàÖÆA[Em§0#4((ïVL"h¯¬nïîþ¼½‡\¢h‰¥^\ü'DKc®B)a:ÑÏ¥]³wr8 2ÌÍÍIßÈãéû³ÞòæîÅÿÝöû¿»Ç?®b³¦®Dªûó—@á3û×a†¶1%,+Hs—5¥`Gé¥FôÉY÷Ç•z£¤_o/Îxäb Æ$ƒïbü×Tß²aú 2·vÝ¿îÍá×»óîÇîé]Œÿø@05zs c„–AÊ#©1K„ròäÆE|hM?ë×-bÍùïˆxO5Ô;ÅaN‘Èø¥æ¤ñž‘ ¾ü>Rí4>¾K¾Qb©b&–(¾CmØ “p¨Fñž~ÐHø19‹YŒ9å ù‡æçœ DŒ •w}Âñ9Íð„þ ïׂiÂ=•_ëBy0ª“©)ÙÉqNuOðyœžÜséNê@OiÕΆõÎ^ŽÞ¹A'&wþ ó[ô+R#™ÜSÝ%L)“úàNMÊaÕú5zi5Jmij)ç¥h@úiþ.ûÆ‚¶³Re™”&ó+㉅Õ½eY±l“„¹õÕ&’ZŠâ@@\%¾]•‚’$wH›õG•IÓ±m¤#ø? Ðèa4VÇ(*‰®ñ$,«k¾Ñ¯?!ò\/J3Dåç23Ò)ì»Å¯ 3ðýÌYáÎ+dþþHA^¯{å´v / ˆdë"¹¥:òè§õ­–JÈZ=ö*añ$Áîó3¿² …{S ȃtWᆹ€eh<ò DÏ$y9“B\HcÊ6¬9N*/Õ‘¤$()X„‹4ÄŸ¦ÁµF&ò®¦£Ú¥³©(§¯Ò aÕÀȱŠD½â—(QŽHqèNaÖ.£¦Éj"^1PÍ¡êXm^*­î“…ærr)\\4ª+±”Ga {ƒÎ°ÔúEŽeeÒ4e¶>þ ú—lYõ¸¿h^Héþl¯¾_MX¿°˜¹²–ò÷^¡eì Þßåëú– Vš´ñ—¼Ì wNÊ@iqÉ‹ñ4³NÙsX‚j°æ(žæÉþ6A)m‡4Õ¾9'~jœðV ›ULTN"¾äB©? µ¢fHëk¾ô@¾Ÿsû¤[6Ë4U’"ÏÌx¤2¬Ÿ«î···ôVMQ9©ü S…ÞÇÝGzŽƒ’>><ÄOâsL–±Fš²Ö‘ñ ÏÏ vØv s’•™(¢>k´/3‡û»œ ]ã ž—Æ×tRʬâ]ÚWG#áæ?Úf¬¯aÜ— kyh'9)WÄÖòi Ò k£Ð)(R£qwsÖ8XZàp{õx ¡ÀFpÂaJ$½ kJQÔtÖ®§úª¥’²EýS]Ù¹¤¶¼<ÿ DÍ—ì­0I Žš-E2Qò mŒ¤gÆY<~_./¿Ê<F =%9Ý[Ê£ý0y¤gŽ=„ñ <ݵ.qÌ¿êˆÞ•¯`4߇²Ð…p\»«àD|?Œ¼–ø„Œ$¢¤å-s¹¾K%$œêÎ&ÎFúœµ_!x”PÙÀòâ\.¬£ æ6"ÚTE9ª½ZÂIFO…cš:ðSAì\›wePâ*0.j!,Ô¹ªÚ::ÈkÓõÞC· ‹ŠÛ÷ï&¥+½ï¯hU Wì>©T2 Úc›ÿÈY†(&ßËĸ] PX¨¤h½#5’|ck H3Œ&wî#A;¢vÊR›4“µ~„ZÑêš;†Œ$ðlÅYˆè-†EÓ´Ý)º ÒX••è[x·ÄêƒÅ#eåZÄqOdn•)•”d¶øH‘™oì¿¡÷Éd¸ 9 #ˆZTx S]šT#a¦¿»»ƒi¿Eghƒ˜XDqA(‰…îñÚæ§î9ˆq‘8èÙ™}@\NÿæpæG7ÁÕ~{v¦=ó¿vï¼ðòv£±ýiv¦{v×lÜuÿ‡Ÿfñk÷ìËýyofýÇa¯|lÏÁ³áõ!éen 夥x»ÿ¯}ß?»ãçÛ_ßWÿý&?ayyü/ÎÎÓýð–±ìíهǔ ž‚án4úg½Ùí_ýÛŸÝåîiãlýÓÇÙÃKˆóòˆâ´ñ:\Þð×^²sÁšk•@q¨–Ч9L†ŒHh5lÖ”q5IRÐð™ÀX<ÑsÎ2¸ž+ÚødÐvhÁޤÓ**XåÙ(¢M.%D|Ëåðš8•MV­Yæ)>u·øîƒVFãË[¸ËsÞU­Еté*™ùN©E¶ jÀ®`^uLrWíåq+Æì ^˜KÚ¹%rÃÿ“×È\P½3V‹á€­)µiǘQz”¦ä«)—óëȃsÌÜud åµPdŸÁôžƒŽÑªà¿îάÌwÃX#¶œž@\Tò`eÎÅU¹Y<-‚ Qy2 -K:fFpéi¹Vf~¥¬¾ÚF2šTÇC&=ÑF¤AäLÄØ8Ü×D†ÚËôçKÍz«Éˆ®*ªŽš,£Ÿ$½Ò¥§¼VQ©LVe“žÓÏŒ0xíž§z‰Llï3Ì-’JNa“;ÃÑB,¡$6~%£ùÔ4¬Ý"=J[Q& *ÕK$¾ˆÌ‹iV=¼ÌÈ JÆ®¨ÒÔ"7ÛEéãÈ–j¡kã¨QUstn–9W49ª,?RÝã0õ#Õ[›;¿«Üy0·Žü´a£ÕG¨…tTÉ;ô  A·…¾»´k£¶>ÅTý‰§F…ÇI§zi ”„Ùr¬é[6]Þ’¼¸„ðhš9×1Lù º%ÇÙËi N-ÀC ËsåÝ”rÓ•¹.@è-y¢X1YÚ]­1bK1å;ƒì‡áÊ.;Bª-¸a©U#ù s"1LM-¼Rä8²Ö­@€Pת»öáË´wá_ÆÄå‰Ö%Åáþ…Y/–GÒQT_ã8OÍÉš#«»UÀñ`›áÐsºËZ…¼ÒÀlôÞî–$¾±µ³Ø¸ßØØ‘?ñ¿Æ˜¬˜aÝÂijʼđä8ôÕBê%)Ïá—zéún°£,´&0 éuW×××ma¾ð´ZWWm©o§?îîžãðEk~t [ê/±­ƒdκ³½³æÎù#L‘A\ŸwÛÇÝþÑêBÒ{s×{ùãõË£•Žhv³A¨ºñßýÖb³qyø¿Oëþìž­Û'ÝþáÜåÆüçÓÕ“ËãÕþáÌç]¼µó³¿Ñ8˜ýUµÍT2¡fæ“ô䘆¦/fé±aàäDÒEÊd&¶Ôª¢Îšïô$ãQ„ß ù†!ÁôF-rœ¼B”ø’b}­=×"í•2álsÏ«©Tó|y$ 15¹°ú¤ZFo;¡ÖIb"!žÿX›8…À gißøPÇå Ãš¯ö·„>e–Ò¯Ãa0W„ªókf–ÕøFH\rb-[êÄRÛîàË.4{Øã¯œwT«0ëH¾aÚÏ$ ˜C¿?$MË´]Ú,˜RüÄÃô$r=4’=T|µ1¨ªtxN‚Û+ê°¦Lmµ¥–’ÛKdž0¢(ilÓ5Æ‘ñ­«îkLÛ·2ßåòFRæºs.¶üwFµµ*<<áAZ±Hs!,å¬ÂÀOÇš†RIæç‚ÔÛ5í/±îQoM¯a©Ú¾~¶'YÅ!DÓ‘ÔLšTùvÊ.Fgûæ8áÄñ0û$3 ’6Ã*…3ãmЮW‰Zz9èË_Z$œw‘íÊÿÊÚR'F¯ž-õjÚRo°-uvκüuqs}“F•BŸêÌÏ-Â8;b–s§={òy¡wpxs|¤s´ÑJñNm D´ñ:k´.éž/Ïܯ6È$8!\ž¬6PH½¤÷Šª~]¥Gó½½²‚•Ñ–ZR¶ÔY¬mêI)ÓqådÛÚ¿½}Äy7ssý‡‡»ÅEPÆ`øhá‚퉜?OÔõÆé´Vÿ®KËÿëÜh|˜ë®_#Åë‹íÕîb»½zxÙ=ž?¼6(D\ý"ÂzžYê6âÓ›¾dòmkµî¥]ÁÓÇÁìüZƒ¹kƒ“o§_Ïný«Ó³ÝÞR›¤Œ¡èáæÇéʿ넧!¯ÎÊñêÙ ŠEíñí¿Þ"r™[ÞmœážHd{ ʉ'ý«³³µƒ•ެY>/ŸßÁ$cû‘ÏÈ–õJÐp´Í@HkAš¦ŒKú&›æ?iAî26ÉÌ-܃JÏtÝ(3+WZã«þPä]nAA-”D˜¥pË–°Ô2âé«Rs» …¢K2IanÞ° T,Ò9lñó &º,±—Ú’jÒaÍ`×c†umä MÇvÞú¢|'“BT£PÓT;Å.‚«¤´fÖ ¿&‰ɘ¼¤OI[ÄöÍ¥cÓ¬ SÆ™vyN^R‹¨': …6 k*ƒtÓWt¶’dJ¸ê¨'AC’HÒÊMdå$z›´‘*]´ŸvÙf VÔ4_De £ŠÎÚÆ óÙ:êxÂヌBËr¶:²ÉX°5E.Yøf‹§ùc_³ÈÍŒêñË[¨äá6·‹t9™%ñ…eT”ÂøF?k›Æ’‡‘P4§Š¥½,¹3?­— Št§{e}m½·ÑÝܨº/¯dð´âZYí±žX| /.­hj½˜f¯kˆ¬VŒœaDå'+4îø@aºCj³-êQ|‡e‰NíñZ«ÍÁnû~g¶¿×¾G¿®6ïå îçâÓ»”Œ’§«<ÁÌÄaØáª¶èLIJ…-µÌ…-þ+4+‡IÌ\*˜§7h¯l7nËË€×Ôv<"4“?Uƒb:  vÏÖO.aS½¼±zzxss~¸½Ö[ø@ùvV.ŸÛ³0í¸7ØQ´j÷òÿúÿwÿH÷>¬±©é×æÛºãò†/2¢­z(= QÚöÙæ™7q@)Ë‚ª¹äÞ2a+bß Zû¾È<(V^&q¥Û;5¼,CiPÁ/&&¢Ê߉+2Á.½GÌȈ Ó˜l)Ü9Nš´ŠašXtŸ Ï;:RÂqª£9‚}}¼T K:’>…Q/™6YsÓ=plT×€­¹îPcÊRKHÔÆIåiqùåÎïGEöÜÇ2ñŽ´p£=){—â§ÙŽÇ&•§è@N¶ú„õ†›PíêÔ¢ÔpÏ\Y‰d=‹UV¶]¤tý#«šø$>ø[R3wAÆ‚ÎuMXÕĘv¿ÌÅÝHŭm9¹ºÜ¢LBáÂëHš=á FõeÍmIkŒˆ¼©=Èÿ—¶lhAißLÉm/ 9€ÓäN¨%!–7–?õêבµ¢7è0‚Œ€•’Óò,µ| ˜É2‚ûÈ€Ÿ|¶<²º,WNQÁ‹t¥_uÇ-ÅWK}z—ÒÄÿè 9ɧJÂe–’{j˜GTE6TKa³Âj +~lÄãJ¬#ý õ ’ £.½À’¦ÌéG¹e% "I¿Ê»téÔ ÇdÄžX^R‘O6em)ý5S )Ùv é˜öJ5µ)?P^$ó¼dªê˜Óùî”ã¤Kxëàaƒ*«±…š} ˜Ñ–Z5ß mÊþDTV3ó=‚ã ¦¢Ã†¥ŽÜ¹µ¥Î0â’K)x](¶÷Aò¤;pÈó’nn_ZÄS ÷ÁDbÑøeâøêBzt¡_§~!½ÃÈ[OžÈ8£ Í… uŸ=g£)‹ß‰¤½E}Ëõ“ãCöÝØ A—RÙ´.1_GÚ ¢·ÂåKïݰwáûù Î'–ƒµÆ,°cn¾³˜¤8f•w)Géëev-{kx¾’Ýg[ô­Svš2HÌqºàç˜qh‡(å*w†0ÃÃŒ=ô]›f@üÈ¥5Ûn·:ó·÷ƒEØCë@b¤šl©Ih&‹ #~©··á—ºÏ˜@ÛEÖL¯šÍ£lØÜ=:>ƒ_jnº$öíáÌò6Å ö.7æä‡«ýÙÅm8×ÛX"4Ú›_?å¹pçüÿØõóÝE·½~ÚXûy¸r{4³¸ÃZƺ×:Ûøx´~ ×ÍÇËýŸ0ü€Õuÿ¬»xÒ#“é8zßMöOØdÃLã|}ñX~…}öÌçÆåïÞ\£ys4»¸/_o{'l²-“ôëÆð ,¹9ÜúÕûx¼þû`fÓ¿Žf—¿ƒ;¿ø¿/_0¶F˜Ë¶ ëk,[PÜÏ×ÛK:ÑT/ó[<'ß:¼ÀVq¡çm¶R'õ.E×FHm"I³iã™.Ï ®Ï¹¦úµÚ>ïrû¦8Ô‚òµ:~b aÓ?c[s³WÅϳ;W2§Ã ¼¼Ù‚"à0äëöØ×²eß»-S’±-Nç>NE‰òµ² E0¢Uyêª/Ó¤íBßI­lpF²Õ£¶ 6ÊÆ—RŒÒÛ¦™ò(zŽ}³Ä&OÆU£?f꓊ZÈóê<ÿ5'×îY㥙d=@#f¼Çð„̨4âÉJy&Öpy—ç G\.3¥ôŸ{S˜{Dµ0gsIÎò¥<º’‘’é•ÀBØ=¦}Fô6J•r ºš_IåvWüÖ9Aæuž§Žn@³)žšdµæu®­Me–Êžžä™fâÔÖÒ¾„¨-Tâœ~…ÿIÌj{íðKä¥R=¡ò?( äó}˜±ÚÂb ­#ïÛÂÖ.Œ±4쇾Àª"¾reW=Ô *E_Ég´bD͆ÙRNüŽõR ׯã1„r§‰[Ód¥Ë ÆˆJµEÎ"´xB–2Æ4mÿ ò,ñô3£|VFÙxNOÔ|„d|TË`æª){±ýà:êR|]‡9)ú “•¹ie3oš±Z+¦OÂA)J´êæá,èxÎDÚ:Zh[óQ&–½–!‚K2/UH—Øc˜¶Õ]Ÿý‘~ -b“AÖÏSyeA¯–›Ÿ›[]]mw`N«#˜Ýw4nøèðhgk£EÉ©Ås0j<9^]ìŸÝß^Æ’„ð˜Æ–zˆ?7¼Ã¾Pt V&q¥iF†/Ä1DÏß2RÒŠg˜Ç:äë›#It<-[W¤)B+ÀiZå«–à„ÔÖÙŒÕ7ŒÑy-ʯy4_Å‹ùWc:™ò¤v·0?SÂ’¶Èêm,ršÅe\7´áã:ÍÖ+»KE·ØK'ØLë}ÍâiÕÔ`Ùwxœƒ Õ©Ž…— %[4ÑJm?C芚5±Y”W q²­ -aÐ;&Ýud1¤±ÕôßðCEy"¡·¥¿ ÷Ä06‘³×Å‚ÂP‚ôk˜ÑU>ÙÓUnE`Ô¥}K}ªêzUŸ—ìÕÕÎÖ/‘W™£4¨JºÞÐþ¢K‚O¬r×¢ÛbXT3§ÓÚ,”ÇÖQÃÙž!zÐaYë”Ö7~“ÍüZ,-§ö©µTæe¹§FB1JÚ}È,\H#iWš[ÊR›òˆîµp¯²¥ÖÂ?âQQ¾A«Þ"H‘.‹ 3spâíÒ˜Cí”°µ¤IåÁó­î¦ '«Mˆ«ò 8‰j'rcäu;”мeS%g’GlçÕ„üãC§2öQM4(P{E:esu~äé¼¢þXùp ç¾$p“Qé nææ*ÖÂèyÒOåï¡84Ëp»bæ¬â@ø•Po0—nqK˜)ùÐЙ«ÔׇLH¡FÁzA¹LÃãboƒT*s…2?p ±<¢ž2«›OWTúTfJŽÊLÿÓvk€°Íûúà\4/îY¤±‚D |Vœ‹‹ROøX9~*[[IúñÈŠB÷=3wËûy¤Z¡-´^7§Ï´ $æ[yºˆº$}Eü¯I¤ /hSÙçΛ@s¾ŒâcÁpÊȦFÞÚ°Ô²í_¿³åû—i~; CD€|Üâvjzgá,•”ºZNáŸò§& 4tN#‰¥ž¯f©qtÝů³‡þ}f±TÖ8P͕ϫèT9$š+t“øéÍÆÚ—Çp–‘/D–äËæø T~+ h8£ïUñÅZ£X¸\]L9Í´ê[ gõ£Vƒ*ÊV¢a -þ4d4R“Ì:O1wä`Ì|ÏsŒŽV#V\U8©ÚZØ0‹S$®x©s‡±Ã"u³†aS; ç³"×#’ËÈ¡¤œVeªÕ§V™KÖ`eúÓÊéd1¡xšgÜŒeóƒmö<âa–Z·J–Z #ç`U¸3¼*ø€RÑï!Ì¥q¾Š•Y÷ä&ÿ”rN$iË>ò>`L‘ªiô¢™Ä´_Tp¢W†ãˆDÇ–ŠÜPäXrÓ]´ ´6sFzÊ=iIœŽ¦–ìIx®°!©À£‚› â‰áHûL8¯j¤.5Ò5Oø.Á°VG¹8&ä×?1MiA±ò¢ÄTc¹—…oè„ä¤Ý¹®ºÂIöQ 2µ\²™œª¸êh`Ñ<‡ÑD= =îÄàzÙ˜}KN [z7Lk™ÒJ"Å2—ðÓyÄ/Ú˜N'ȘN„ÌäÛr\¿è—úø·ônY$û1ÁÓª¥a–!™kû²E„îMÒ±ûŠkÊ‘­AófÙ¥’™)¨ÈUky+ˆ:ÁÇû¨*΂u20)alO«ÃXÇÖÔq†Y†ÄR²“¤¢r\µ–CR–2yñŠ'Ѻ5A=©`(=­5J¨Êâ2$>a½Óæ1Ì•¹ô“pÔ™ò ½q4ëÚ^Qñkb©“_jÛÙÍ¢TK)¥É>©ÂÓ™Ûw‡¢êªzêiöáljŸk*è*f:Ù=[è<üèYJÎýÁÎF©ˆ©÷Èȉ}ƒ¶‹¦ 5Ÿ”,†ãXfµjÔ—‡ Ô9c¤^ k"Û\Q>œp£‹ôÆÆë¥hxx:9õ‰)”ªUq¶ø›Ÿˆ¾U~áM½ óÅ6 6«:çQß±HËpŠUÓÇãÐ÷i`ô¤¹|Aü‡’Õ8YM“y‹:¢ò¸: ð»:ÂG6sé9´f¯’ò¾U¡„ªJeüš¾ïK]ó«‚2¬Y…AóÏó,lÖf†KN2N˜>±ÔÜ4¡<ÙQ@µ ±ˆåyúžaU:uæ°—Í«T>Œt öî‚>þ—Ë?´TX ”±ør¾©²ÔÊæ†I‚¡8«` )Kìñ•¯¬ÃY‹d!O+úÁÇâ\Z”ì7ˆÿÖž’ÕRæ¸îR@-½;žMh|< §®¿&Ý&þ[ôGÞPA«÷&¢¯#ÎU–»ÙoÈá+±Ê6¬[b‹$‹j멚×êKN['âi–¯m‚þÇö*ñͯö¢z®‡Õ\®há ,µ‰©£#u³‰J8ì Cõ ˜;ó<‹µXªÒ‚¥æq2Ïr[ê¬ÉIÞ'­˜»ìXFèªX:~OÇfË›%Sù«õ‘p/àé!%¨‡o®Š]¥”Y¿Ô,éÛšJ0.¢ØÕáø–,´Ò]?æhšlÂ’ßõ9‡­Ïûܾ›O?0ÇònéEí4b +bÆòH:’¯–Ê<‰R’ZðP”jG¼<Ú"þ*µ–osNƒ2úÇ :ˆK†XY¢;ä˜t3‹S¡†q¢Ð4¶F%­ÜB5¤äú<†Ó·…ø›Ô1ö­|XÖߺB ¸™õ€h‡áéL™‡Ú¢–¤–±ÖMGU,"O-¥µæF3ö‹5×9uZpxœ§iÅó­˜­mj±<ÊfEí= WlßÔŒ$ã8#òÏ´”"c}.í"I§öÒþÅzíTy[ã½bùÚÁ,¤‰•QÍ×$™é¡Ò'$9•o4ާ0½¬†–¼/¿ÑkºG“ãË[4( ¢Ž ¬{ܸßq©R]žV)É’BZ„2—!‰Ë1"? _çµ/ȯ™Y@/2¨…>"Ió»ŸO³c¯±ÁÇHI:qG†âÌò¤¼¸-²e.Ë+£?Z#‘ŽŽÒÔªoòXåVÙ¬K’€Žë‚ÂYú¼ë#Ø“>°’i<~MäÌ S¾KŽjå¯-’sÌ|yë°UK‹ÃÙG[j•’Ö+uÅШl¬!\h.+µô»d ’Î[eŸ÷ª]\ÑÕº2<-’áS^ÁÞ#kQ)” (ÂT‘ZŠWqRLàunëÞ¤cº^¥Ñ@ä/µãq ôJ‘RôÍŸ±6¦œ{ô£Ð“)”ÁÓRh‹§<Ò]Ë”ûl-[8“OrÑ‘$HLØwÃk#iS©vq¥Ìr’n WZú… ñÕ­»3aÉÅÞ9f||‰dã°lYVÂú@RÈh•àŒOÉüªyÉøôJ5?‡¾¤[Hݵñ5,ñUÅj„óéØ4Yƒ-õCÊO‡— ÆR*M×›LaSÑ ɲ$ÓM¨*L]eŽzžá˜M:™O UÏ£ MÙ¢€,«cFÖAvÅd²=KGX€8÷\:¹ø%\5ÇÆu)¬r(ã'rˆ'r‡Þ:›±Ô½ìn9³žX$0Ë#Ïä„.G}Î…óòò·¤+W ·­óh]n»4#šm6!Ò„*QŠí5©ç5Ûý‰º0óM*B¿È´KVJšÂêÐ|q à+¢jeÔH“ôë¼® µ³Ñó~e„”¬,Dg¢þˆÑSÆŽíÝ%·7Q FØm†EÁYLû~àÎ)9ò©Ö([âÎmoÊsÕ•=´ªçjÞïÁö#|ù‰;I>\¶¼m7Ç´#ž–„g/¶RU¹eU<~  ÇœD>êQ43«Éê"ÔbÂy™”I¤õÃŽgѨx7­S!óì˜#eæv$)˜-=8¥Ìq¯ª?êD¼Åǵ½èðÈÕ»Žó¦Ý+D° 5’f¡~¡WÖ# õYÇ‚5+ï/%¹Òæ¸j•ÕϘWd…"PÈLüeyŽëUm¤˜¹½ƒ¢ˆds%Ö5ÒYA»DßôtF„UwÞD¬;4M¥›YÂöLÇèD4$Ô.m®£çú!äKeˆ¨:ŒEqÆ3,ux—µÈøªŠãžœ.V~74Žˆ6ìe”0Ù‡¨"•;n/Ë+¿D8ÃR cÈ—Ö¬{ßÖÄ~iÔ–z~1ø¥ÎŽž$¯48§°õâx!£†Ž^d¬ã²Ú5VXS«f—)561…c¶ w,'Å•2?hÉÓS3qhüþµxGjöÿ1=Œy…u!•Ã鼊æžÇ=d‰½®ñ'ÒO|C,‰ÖQÆt½s ž,ðLÂ%˜»¹ÒÀ2çicË,¥Ë¯ήéå-y'¥ ©d繘¾Yuþ 1uÖIs˜ò–i6µàªH}’Bøy®½2kË^¿@¸ê;o¹öåâXî\å“Õäá-kµ(×ÊÒ 4Ú¤;•¨‹eÔX"74Y§ÈÄðY]²\¸¶·ØS²lEçÈ8‹§§voÙjÁi°Wx—^VLÆsv:-‚:y`¶2׃L/4ùÔ»âƒhN³sÉãʬÓ"@~¸®9üš-£TúK×$ÆÎ8ç{NGÛÚ…Ñuh8Ö=I’X[žXx‚M)[“’.–™sÔògéBë3¡¨·Ð:ÃZ$¤¯úƲ­ÊY‚§Uo¥aøÆ¿r½ŠË¥Æ¹o˜ö{füUÆyAó*HƒÚ_¤_JÒ™·¢X¥<ôV5žæ4ùT]}”³š„äNL4ž³E¥McÝ)(òWC.³’üæÝ„‚áâÝ pÖù(@eÑõ0×ì%d5ìXVÅï¦V~÷9ôa éÞ’t4,)‡s ôŒ-]cˆ.éyÀ”¾ÚÉpƒØs‚¥õÃiÁZÚ4©Ö§çÉ‹ˆ®ñœ4r*%—XêØÊdxë’ç”!fs×'Äy‡_ƒ_`æ o­qøI½°@7_Ãß§_#;ž¾ðhV­£òcA*‰5 p‘«æH•ñ%\œä—ú!ËI„\%ï'ܵÇr§–„Ä‹U|Fx¬r¦âIH^–à(fHJ«ƒ­]EE)ÑÌú[ètºäªð¤¿úYÊ.pùj†¿cE)tà*Åù ¢’Ù k¼ Ï$Z+f•s™Ì¥ "ááa-m} =$fNV–%ªãË¡Õ÷žrNb¡3íU%ÕBÔ·HÆ–Z†ÌÒ·¿„·¨+¤=aŠu ÚÅ£9⟑{ý¾˜g†° ¿ØÍ0pè Úǽàí‹R¶^É›² }°ŽþWÅÑiš,¸@uʘ†Î76ãìˆðBñ-ÓœŽ°Ú±8â€4DPÇÕ0%ö:“¾Ùáy©øBÁuž–¾jNjwE¢ÚlIO¤aâ”P2N¦1ÓV,Ê_gÜ8ØYi‹Ö¥UAò=/ZÚÅŽó#¡óÄU¯£T‡—R.öSÕdÒ3£Z….I-²ƒù2DZ˜câGdœ¶OC‡%Gà ìÛ=yaÕ$¬ó¸ÊͬÁ&ÆÐŽ9¬T£²A†,ó,ìRü@Ϲuù…8ô*\¶/—‹^Ðý‚²†Ñ}#œBXššÔKq?³ÔØ#÷Ü8¦³NêlXü÷É“‚/¿O n"ìö5u£‘î°ESÙøAuTÏEArø3èßpXú´_ó~©Së|ÉpKQ ›Ž–Óþ4øtå¸9ö˜[D´%è¶L$iRè˜Cfw2 ¸PÅsÇž¶ dqs¨–ÄGॠM¦uí¤¥kƒ,bÎ,e2¹„É%¥z¾bž‡wÃ×çÞR=4Þ÷̬ÇÈ'‚™4³³iœ¬eSÈv€q²ù[ãfqC’B)Î.®yÌLÞ£ew »² i0 j™]"ÓÍlÑN+•¦™)gF&Y“°eܶþâ43i]Qßôt©Îf»°çFÀäø?p*"^þþ˜¹4¼kô*ò¯l–Î7V³“¶™ítŸƒêd0÷P_COóP¤®ÒeX:idG€úå)Žõß7¦É+6o~{nšegX]­J¿BŸy uîÁ(fF›:ã7õpÉ e‚Í»©üeÈ,ø`¶sMöCda.³åÏ•³)hBØ+ÃÓˆø7µGk±•…å'âT“ù.dô¤ wfôpXü„ËÓ×öÀ~å'ºq¥Q?~^nöS1œéS™ÍÌU9ÒÏôñú嬳¼.Ù2›8•å·q†¦i‚E=©Óï*ؘ [lr)³©0ì@•îU>7ãäðuTq,-Ä7vÅÞIYÞ*=/ÔcøXyÙ|‹áhØ’ïjÅ´|vY?-¬®MÝm^òݲZ&ÃÒ©$,£]Yf¼È³Xþlªû¸C=Ãxê¡¿¤¼$¾¾Çý8)™_mÌá“–æЧIŒó2Ï+ë['ö÷$s‘¼‘[&\hù5!ƒ!žÎ­×óŒtÓÖ’¾É%¶­iãd[9Ó^F*Û±ÐF¶MµLó$‰ç$_pF[lÛ‰Ø4 O*ߪUZT¢¥Ãui¸:f‹žþªÐí¢öZ-­j¯a=·0>Tö¹†+ô°D‡‡êv±h_°}§ZKE´¯Uõ_ynïFO2mjt¸¨Ÿ¶šV•Úðv,ô)›þóÇÛ”‚ JÚÎ ‘dÉÜQ±ò÷¹{x_pùŒ%Ÿp’®ôèØgá—znáòúòö.ÊRÿµëçæër/ñ#Î#ùË ~ÂrØ• a\î¤>Ú˜tÌá»W:œÛ.jKÉ—‡"çWГ:<âãXNÑèO±^™¯+¯æ¿ŠÔú&P¦Qy ,c{qÅ·‹‰ñÐU_NŠXñ ÇÖ(ÿý§¢UIµØ—3‹Dó‡íÑ™1ʶu!\õm­êyQ7Ê¿éÙ±´ê aÕx;¼}ëŒÿÎI?mžu|ò<¹ K±ì pXê›;>X”wótZÍm·¥v;ò7³ö­â'ÂŒ•·Ç-r™©©†§§:¼‹MóåÃu8ªŒ½¬áºŠv´ké .-çyLöÓXÀZ¼‹Õ¡zRëÛÅË·ãûËá9}¡øɶQ¡ŸÖçžÇý6ò4 ýVU߱χ;2|s¥mý›â¤«4¸ŽžÔøþ`ûiÕ·©âw‰±¿ƒ9ÿêÜöót`c÷à°Ü–šý¹Š9/'­¡}ó¼uL´ôËF'ŽnÛ”a}êóùêjN%q0£â ço2,”å -sY‡O2q¬­¤† ¬¶²M‚2í½Èmâªó|[)‡m¿TpxE61æ'h“=Ôβ’Ï®âÕÆ^Ç>x\~wRñÇåæó¶­ãK£ÜÎuè7¨úܳëÛCS¿(Õ7y^ƒ‡–Ò=®è†|ÿ q"ÕLÍŒ¹õOÆÛ볯êçæXÚZãRa¼*rØåßô²cfŸ´¢jïDñÛc¡_çS;ªø¼éxc :4œgÛ¢-5±Ôd’a©‡Û¨Õâž·p›f·‘Êé@†k¬ÏyX=ÎVq-u8˜é2U¼µ}.%’'cpØu¸·×à­«XñǢˆ„ÊlµGpcqmµ«¸ØçèƒÕ·:áçä•Ãp¹z¿ùTÔ·„c¶rs.ÍOG+øfÛ§ìŽíqR¶Š~g{åðð¤Zu2éTéápý©ê/Å1¹"}Ç!cocö5Oÿ :6–šÀT‹ê¸–­³ÖY˜›ët:s t—°` ^¯'ëQ<©“NÍ8¹¼$ßâ»x.2Z__—ò lOæŒïïïwwwWVWQ¿ÕÕÕý=ð;„K¶yÙp”É”yèbÝ£|JeòYÕlÇÑma˜ ›f‰_ˆ1íí"gSÎÛYî$,?¥´å¼KœN {dy|¶å„âÜ“K3à’4sÉ[f&« k’…FpØ&~”RDê9>¯TžC8¿*.°„ÏÎ)>çšIYÊ_ÂVqcC¹p›N•dj=¯Áá½ï^Ìwhÿª’ÛÓøf]?×iwÇbë1ßÝqU¿ü˜Þ—ákm,ı=QG’áý×²Ñæ+ÓÛúÇ\5þØtŠ/—‰édD`£øï*N÷‰úüì|ŸÀÓ 3ß Ç\G+ª¾Éß5ëØb?*ö—ªþXÕ7mü× ûnxüWZ¥Û%ß…œgu[ç·­bK-?äŠ~©ùœ:sú|µüR3y83yÐÇÇGдÀÄÝnáÑ<å˜ë*pÉÀ¾‹ËË'''q]+6ߣ?!ß~¿O˜ƒË3Ûn///OK:@Øsss½  —W.ß(9Y\\\YYÁA•²Þˆå?::Šå5ÃbVűÏ#R/c”­%¥”$}‹~RŸªì µùï:¼ø¸º=g\UÎ õñ1ä_QÛ¦¤·9[g«ú#o¾]­S;Y¡û‹FúMÉŽ 6~1œé­ßÄêðÍE~:I©ê[\•tvl´é”Ží•pÊÒ‰c~4ãIuØ´QŠoÇ¢\XZÇökÎp¢öypöœ‹ä^4<Í~‘‘Ï4ë[Gþ5ÚTç£8OEÜ/ïÚçi.ÓY,Ó‹MÌüó¢žëœX©ó™žSÑ/ò}6÷NìÑ5ú¯íã6cTq ©Š“Ʋq²ÎøFq*ÆX;–Ž1Wèä4ûˆçõ^æ÷7RÎÓS§™q0ø'¬þéài jüƒsÌã(¶üøxYã'Øs×/s¦Ž…6ŠòYþ­­-Ônmí šl\¹M5¾µp­à§µ„eT6ºÈ_š'ÚÇdŽ5å/>±çcŽ?ÏïGº6—¿3¾´šíe™pv)|—¨£ Å8ÔR¡wX–šéMµt~xß)@gÛßm?­ Wõ}Ëj×l?JãOvœ´ã’囫žÛ8ù°Ñp•¹É«d6c¯]Ç>q¾›zÿòr>Œzw2ÌØRG‹Êfb©Ÿ„>€ÍŠ59HŒ>… Q…ŽvÕ>‰ÇÙû{Â…Àß0œF¬™~qíhóB8¦ æÎˆ»åYu”ƒw§Òæò…ixb¶€˜'gg`¬%Îýã#ì7°H@„ÈaÛºW…‹+øX~ÖN•ÿññ1ä½¾¾z}}ù´õYYiú#Ë?;ƒÅXsff¶:oz?´}Kø’`©&\NlñL 7zžzkŽoÎÆIég¿™äu¸Bç x¸ìí_5^Ð>^è§#¾;xèªï]ÃǪ±¨îs3ZV}ë«ó °ä{àˆoà%sqêËþn±]\&Ó’‰Î‡aR¤1‡z·a©y5ŸÆ†š Àcù%k¤ßjµ¸y(ýñ¹ê|¾1ýÒò[ŽŸ ħ§h¾ñó}+ƒ~©°Ìôp[À*~ºho=®=¢o™¶ðܲP– /†Kø*îÝ–ÁUq`Oƒæ­°tVžby’)¿<1\£dÙJÑä0h•…sÜv'Zšæn»çj󪶺šÑ[ËWÙžVðñUéˆ$“ ËÂ:VùvɶEjÇ"—bƲi»}¨Ò›f.ŽUêŒîUém^Pñ-hD¿ J,ª\Þg%es/é×ïæ¹çzcNêScã¸ütÉ|Q¡ÃO×ÞÊüâå¿ØàÉmWÖš«Ô“-5³\†u. gø`¿ð&0ÝÞÜ„Ñ3·Aô qr¶ÔâU©!}€]ӸĦ/›»ì-{Ü|‘2Œ.`E óS[‡=… ì/„ BT ØwaaIäcóÊä[%«PþÓÓS”¿Ýn^¹A!/ä» ²:ÙR)·B¾1ýÒò+ ÅuYËòò ˆ9®ÜÞNü°Ir£-¥óðç%vE:UÛú5Ò—rê]üí(ßœ´+S—B›‚”¹ªîuj¤=·¢Å†ÉÙ¶K•¬ÊÚ.—KÒ-Ò®Ñzu ¨k¬ŸIÏ5\Ô[ÍRS*ï#ÙSJS™+4vøw'Ûë÷÷ÌXaÆ´”טcN–o6Xñ½®:þ0NnØæ[ë]©¡\6l_®|®Ÿ¦Ó»V^š¦ؼÄ˶ØÔOx$ |jX™çÔ¤ÇÆ¯®´ýe”ÑØ†u|Η­*/›~U]JÞ-éIJy…v¡Úv/Óu£c¶u•þŒ«cµ”x2‘žÑguL(Œ$%c…#ËŠÂxbç²:ãžSÛ1Ðï>wO«Œœsÿ`9ˆ_ê{^‘ËhýR ŸaáÚGñÓ¾VÎß)²ñ€áG£Ãùùyš‡&šþ¶F|‚|²¼3yÂöY9 3gÚNZ½&æ¨OÌsåiÔ>»Š·6éj×¼2Ïm^U<“á¥9È[† À$ÏÛ8‚È3ü·åÆ2q'K5WäPTVUiÉšDŒJ’áü$’äž°Œ¦SÂ…›82šÅ÷cXG¹”f±øÕOø-[‹0fšñ3'¢ºˆwsÈ=çtTÚ6%~UIæã‡²Ù|m™óiÚøfü/¶Ô)Z¶JsŠmgÛHÛ[ÛÚH²ª­­ZØ8úÅ#éOP·¤É¶„A½Œ ‡Õg+¦|xDßI ½í›%}¶Ž®IÒø#c‘öw3.•'åcHfn²ã[â•KÆ=;jX4–RðùÅçë¿JBÏŠSœŒtzb†¥Fé|hllK-hcÈ:ç¶D"ýŠîÃÏS|òú)ž=9E›ã ­‡ªêˆ2Ô9Çq¤ ã:'¹LCtÀÓ°ñ€ ‘úï>¡ê”¿æY•)ç_ïŽ'Ìd‘W®à³U&î'?Ó§L¸ªÿVõÓgį5ÎÔHÿŒÛŽUJ¬Â¼í^§mlïìõ”hA§rÆÖ³xz"÷{ÞñKüRóу‡…ƒw9üÑrÈìI¯jëh+By²Ã®NiÚw3s*QãrXbï gdãG|Þ’t‚߆”¦p¯ÖßH W=%±eS—jÇCß“y Ûç&,1«â[Ø6NÉIŽ&wÅœòD.®IG¿›M1ýUýæè8UiÚòW¥?î»UifeUGn%*iSiŠá5È·cЫ»E}ËèR…¾±&X]Õp•nynûTišq½jÆɽ¤Ÿ†UA¾¿W¼‹âX‘ÅåvìoŒÊâlŸïþèùÎñŒÎÚc빎`a€ ,BÁ/µ¬¯q¥~åáøU4b&—‰ËÄX.R¯ÉÎmvž«ÖùÕZÓ÷׫­6ÍÄ™46Úž«ÖU¾D*öïë[»Lca9ü¹)¿”dä=Ùzˆ+oež›tªž×ÏKM(ª „¥ÆNWËcŸÛÖxn˦µ´ö¾a›o1,£´J£"ló²µ¶ï†W¹¥*êbãT¥™y>T†%õªÐ“J3Ô=©£™v‰eŒAÏG‡³ºZš¯ôîLß´}¶Ð¯3=·ê];vµ–âìÑc”Ïk>¯¹ÔÖÒtÁÁÜé‰jûÁ¿Ä^êaAÒ.—C¨ÅUWðFîjø—YÛOk…+Øî ÿ˜6áížr¯ðk¹ðJRâ Ç ×)gà siÍ >µ„­âÔ‡>ųjå›&¾ÓÈÄÊGâÕá€'TÇ1äP(›}·$\¡iùï*¥2É}{±;®–~«©æ§K¿%“.<ñ ªj<±cјcNå¨Rg|ó8>º¤±"l%‘ïzƒAÞ–Ïa²µ½³[jkWêáÌN#þfç2y·2‘IŸNYk†m«äª¶eÌÄŸÁ_å_x‡cñiþjm¸Çͷλ"œø ]lZâÝ>Z8û=½Ü‡f&Ž- ׉S¬Ëˆ2Wä•I§Fœª:Ö©{¥¾µ¹/´ÑðzÕѓ˛þáùà ˜;l}²¡òýs¾ÎuÀuà:Й¹ßXmÏ·gýz;{G{·b“I€ºC¶Ô‹ÁãÇØRÓÌÔi5Å/uqÍã¸Þp|øðá5àô„ó¼»»³¨:ƒoj`©q±×ðøŠeC¤Ä¡Ú=jVµö®™ªâOX¨M®ªîu2W>uÒ|ËqêÔ—ã\Þö·O™Áb§‰Õ)­3X`އ¬¬KËïuÒ™lI­2ÍÁÌL¬£ ­¯¦Éñ=üèrs95ëøÃ#N1ýh9åYÃÃnüªYÌ/¼¾xmÌ’tŠmZÇ'·}«ÚW7­ŒJÂÙtFǯ㼎–ÖI'Ç–-…«åP'Nyoª!ÛŽÖ'º>¯öÅ.¥b~ŽŠîa—ƒë€ëÀt€-§›a÷¡Ì q$‚_jÙô/'ÍÏÈêƒÿ}äýÔÀ"¼+ÙÃ,‡ˆÉ\VEÝÈÊD”œ”îUôÇŒ2¼oòñQ4¼¦-Ás¾_Oá]ÑüåzÚáµ(Zá _¤V’uÒ±2ɼ+…‹ž³ëøñµql …ç‘-¯{¡ %ü½Sí{8ùn³ž’‹áq|''ùÊ ržWøÕ–-WN+Ÿ˜¦õC—äV-«\9£ 'óÜÔBç.V#“\™s[Y+\&.“‰êáäà›êÅ›'〡)îM>=•tàóa Âxèžï92 Î=\VyÝ€š(ÎYAéôûÆôõÇäNŠmXê*¦ó=—)#²Ô©·VµX‰SÂgÛ熴|aqRÂsMǦ ë-å€-7ǯ!\¸Sõ®Ù¯©ùëX(C±îòn ŸZ(g†­â›Çç˜\õn n»Ž†WqÆ%¼²•íPùà¡ë´c¨] †œŸvžÞuÀu`j: ­saèG 3qǰ,µðÓŒµgq3[ø<"d/}ˬ@®âHHô¯Ë*~‘{Y±ô˜:)ñÊÍXFN-Lygñ}Ž¥foF·69NÖ£b©\ÂqžR¼òåâ M<Ç»¸ÏÏÏoooãËOŒƒí­óÎòò¢üóÂö†\^ÇÇG¥e®œXêjÞ4aÙ ¾Yúþpf½‘C^ÉU›ø™8ÏHGÊcùïR.6Ç[gx_Ã|Wq®uâÛwG„‡óІÛ#M+á闤Yhšuøé’>UGdVŠ÷F;7ö°Mbnn÷ã#luæhš<åÜÂ÷wº/¯,£ƒ÷ûü±w¢<Ùôëååw™»°C«L™Š™if–º–Z&eÆÚ@BÊðH$|þcb$²#)pHÌ €Àââ§ÏŸ`®š‹cãO-ŒòزIX\® ¥Ï?Öqd™"ó:DVwûûûÝnwyyy½Û=<<qŽL߯AiÇŠ?<ý*™Ô¯{L:0$/=À@­ð «åÚ¶קååÝÝ»ÛÛ>Cv¶·!7èQÇðÖÕÕU.ßÇÇUõBK•|O0ú,:oïR†Ðä+OÒÿÌóB:ö]ÄD-ào÷Ë‹‹ÕÕÕÍÍM‘ð´hÅÅÕÕÏŸÿ­,-íììè»ÍÆÑÑQ® ÇÇ'¥eÙ:±ÔÆ–Z1¢aþ´ŽOŠWDI2ϰÛÎpÕé”Ä)ðʶŽAz¬i6Í*o.ð¬qÅ(ø/¦o9×’p»-ἫâÊV•×ixú1Ò¬*ÏÐ4«d¥=¨ÆwƒJ=áÆÖ¹‹ÃèYèPççç7WW'''@´{{‡¹86þ …¡èÚ(î?O~b!‚½P^ÂSL¿Žž¯ËüoÖåžd™,»iÆ–:ÚRÃ×±ÔâcF<üɶ „ÇÓˆ ¡Aãâââÿ]\`ðP4 R.ÄŸÚs*Õ9• eŽa-ŽŒ>¡lÂSëeã  ¾Q–ºZV[[[ÝîÚññ1 < ¶òèèp\YÅ)­Ký2K¾¥uZuCÒäA8Y|’£Ò\ùGÚ ÿý<;¾Üýþ]âôû·Xª-./ŸAzøioo/–Z—“áÏóóªz….™ï Q¢ë¢çFIŽØ½‹â!#ö#Iwâ‰Q¶û{Oø³×ë!,ùj .¤Iqà¦`eô³Ä‡2 üÙž]]_Íh£Ñé´Âc:{)‘¾™ÒW nK]‡“®GëlÈÖ©ÁmG$J寲á¶üú3ì¼3é×H³ªlöyòWÆç'¿ŠÖ]÷iVäûœ:VµcyftiÌú†*Ïû‚¥Ö Ù„!•¸¿Àç:ÚâÂB¯×E˜z wÂ2¤ËòÊ wsŠyqy¹ºJ<÷êÊÊ÷}<Ç磞ö÷ Ú_Äi"z%žKISêóí6ò½½½–8È¡ Å4ïAOH:c¤¹¿×râ9Âxqum cÎêê~CëuvvÞëvSt^Ùuà%t€çhÁ9r+ñÖÂRã¹²Ôr<+±Ô~xŒ(÷@ä`H‘ÃM:ÖŽÉ$t~Y—ã0”ííM@¦±ûœÅkû™¯ÓÓ3~ NúsÇĤ¨@ZÝîúÊÊ*qg@ùb”Áˆ&irÝ(¶|žŒbHg}}ý h;Ä?;=EŽ››ø0§e‹õ*-O>/:Û½`Kd­åyx¸ Î࣠YA2ëë]T;¥ùðÐ]_Çó‹‹KªàÊ êŽ‘¥]aî|:¹å²B ÀÏŸ×!Uˆ(†Q÷Rùå†Ï h5Èfüä±÷z\˜.šCä á`ž@^¿•s6͸ÞþaÔî³¼½D9QÁËkL?çèè“âÚÚ*2…Ü0ííîîĺÀ âææ*¶;ÊID~V2å <.C¬¶ÔMM§;ÑdÒ$C%~þüùíÛ–¬Ýâæ q À(*þ„FÉg‡P’ÐÓÊÒÄo^”øççgkkk¹2P:<@=€°cyРЇԓCú±ä[j®Q¬×«„%wS•¯EͨÂö¹âNYýSK©¦e¡Նűï–ÇWŒ+=פ°¯®S™¥œòÕ¢N|#´mz·*\«î5Òɤ_.ó’:êÅGÆvÚÚÖåql^Y¹ÕnG“¾tÔto ÐUÑÑSx<¿rŸÞÝÝÇ †ÏA˜5–ÞƒDw>ÿï? È»»»’F¿]|Ã3YNã9ú;†ϸ#Ì}”Ê x­üˆáË¥GKœû~ÿàà`iiEÒÎßöûÙŠiÎÎδ;m¬·id y­É÷ž´;Ù™™mTeÀd 'ô’~U½ £ãc‰£Ròpö›Fl#—ëɘ:öú2ñÓ­¯<ž»ÿÑUµ²Ô‰ºÎùF@ Ì06NÂtû Œ5‘«.Ä8‚øG?~`)¡aanáè臼  jøþý÷_qI¿?;Š:>>•¼¾ÿœ ²½½qqy!écDÛÞØÀ`‡q ÈÛ…²É»‰ÏcTð÷ÆFoïp?–󶋇ïˆÌÖ«´<%yl©UJFV[OOذOeØj·——Î/Îcš($Y<çZos­·„ÑÇgF˜ Jîx‚u;ʊø!¬ 0A ¯ÌƤŽqDòphk—‰Nr®Oî#„Æ?ý8xà1‚1Ë”XjŠYj^góO†Ï£'ÂÓÈ#¶ÔÄ›þüy¼‹Oó<áSêÏÆ4ßB€VØ2åÓ»ݧ%Óį)pZ FÁ+X‹cØÅ—8ÐÒX K|©0={Í6j7¹tbšT·>8|ÅcæbUQ—ÔéeËÆ¨–¼Xž\^2”pŸYYá-Œ¼ ŒÁª¢ÌîOñIŽ¥„ fC–¤Z5¬ôPk)½—ÊÊÔ·Åí-oµŠgÚζ£ ß÷ï¥]€ÅÑF’XRLQ(¶p<ÿñÖÃ-‰i圕•¡`K­úckl©Ïah›Q7Â,=±uY[]ûñãr_Ž)°ª^ú¼ð=AV)¬çrQ/Ðê?? ažž4>`1€2!eÞwÙ)I§4ÍÜsÄYYY‚áSñ9õLÎ ë ìh¤Ýaçe>})bÔCÆ7TfÑ÷ÉO¨üauT’&鳿Räw5þÐ8Ê­šÒÒ—o¶u'Ì£Dî|<¬.aÑÊ2&AÌ•zXÕ¾õŸ›¾É=Nû)…‘/¾ícEÊÏaÓ|uÅ+[þ¿öšØ[¹œÒ”c= Ò£aw8Óu~L¾’—Ž’/(ðÞ—›`KFãò4ÉÀcvПvggá)¦ÞÇxÒnãcòG„y£%èL‰5ë•F¤B9uäñçÎ廌©„c0ŠÊæ¬FSèhµ‰,µà‰pz"ÿl©ilPü!¯Æõ½2ČΠ[Â@Exƒ«BOŠƒ‚OÑ˼‹˜,`]JH-É&‘^€i­Û ¶nYfaJú¶Ö¹tb9þ f";€Z`ñ“²ý•ç\( ã‚[®^¥åÉå%³KŽû,ÊJÊ ï.,,À ö0R ŸžRøôÇ)Óù( T&r­ùÝ€ÿ(\Z6[ߪpiŲaìf{w–ÙŸm “ÚÜÞن谶 ¶ìT òlN§‹Û¼¬|X̲ް¶Ô·e–|£R8<ñ°H†úi ™DI‘[8ż¢n†K-Ú2õ°–Ää+¼ÀùÝ)O#ŸZ2¯Ó.UqR/ýwüôÙ‰x¢€cˆô;05ÒÑ0bp‡•þUÒ÷Aß`‡;b*_ÀUB7dã@lð8â¯v•ý=7 0°cÄWÖbÿ-MËlÞ§Añ±ma5áàôý–UZÊ_§^¹±"Žuþ<ÊÐeRœ›\&Ce‚8™lEÍç´ (c©U`©¹£*W-h#ø¥¦hiÃ,5Ç¥8Ùç1=— qב¨Ù¡Ñ Àíþž!#ÅW›ÔA¼ìþ¡8vh}âc9CÞYž©X»»{ôE¬I€Í+WË +ØÞÑ~/FWRNq°Ü»©K¬WiyrõN(ç—º(+ ‹`ó ­6És*Œ=ès!—A Qhá㣎þMÔz÷ê"ÔZ°8óÜbwQ%+[ߪp,©Ü–—–°ä€´q_^^’¼°OV¨c°É¦ò ¨°±¦˜+óº!mÇó¶ÔŠbθD—°£ë ù 쎚d\ÚN'ÕY³y^¦“ß#±ðí…›Š +s”"ï ÃIO$Îáá>¾`ƒðö™¹CȦ璎©KIš¹¼Ÿ—yÿÂÔé`[)¬¸;лAɘIð2÷<ÇÉE=üøiÑîîªEÓ K Ï«Nœ’òݨN_†·ç×צS’æ‹•_åöÄôŸT÷ÔsCÿÝØÚ n w PÑæý”öi,,˜F;ãÒqråógtCéÚºí¡Ù„ ·àp x„‡ôw‘@´¥þüù^‘E¶_#\šæê*mÞbXCXöl >Æ”eÀ X¿^v|ó°Œó.—ÃótxšÇjÕ%J \uXîþ×™[ø}}yuÇrf ×n6àé`«w{w‡Á…Á§ ¸–ì_±gŽçõÌsÄ û?è9päîî÷ËËëÅÅy6iQ|xüëI{– cWW3a˜C fZÌÅ K°90Ì4öê-,Ð^q©{8|&«'N0lÙ$L%Äæ?ÃÖ‰Ó7¸.yQÀ¶Hä‚eЄßÐÝÝoboëUZžB} #ÕU²‚Ì)ªÖ¿ûKù¢Ø‚‰Ÿ>;;ØÛƒ] yPby"ÁPë6&ÿá9Ù®ìì  KËfë[ŽåG´å¸IÙhÐ3²À†QÈn …i’´LÛ!€-¥-ÙƒæpèÈöÅ9[ùà4H>|"G²(ÛH¨Ž‰.©¶½Êµ#^‡èð.RÀŒc²/2:†2£`@ö‚i­îE]•1_Qñ1UW’ó†:£þBýnï¨z–ê!×–¹Î–YÔåð‡Éaõ ¿¾ØÜXaB37y¸t^ÎŒ‡ã­Çq]r©G÷Ç—ƒóÍvwkçèðàúöFÓ´ ±Ù\è´àx÷s KØîpû¾>:íæÆöÎþÎf …‰Úÿ¼Âçíö“ÀŸO“•`eÚF¾çý1ÒMë >Öd k³~?XGLµ–ÄµÛØAo[Š­Â¾0àß^ÞGXØqÑÃw½6x¿«/ùô%°²wÛ]nõ–a¢æ—KÀ%à˜’ŽÎû'—ƒÓÍöÆÎîáÞÞuŸ€±ÔÁÒ܇óóêñƒMû˜CæÓ^pÏ“-b4c«½ÿàaC²a}š¬€ê`{ñ„§ÿÙ ¶#«˜èñƒŸ¼Šþ°Ýj°¥.õ¶Ê&1¥œï(lm©¥äÔ—C-<ì2ù#u@g"3íØâa™Ã].׉ë¬>!° 19:êQ÷¼!ˆÓņOùn±H  x(žž(L˜<÷p’C°¥~‚¬`±Ã‰/_¾ˆåÜŸ&[¶£Èxü§6¾‚þŸPëè—úát­-µôPêÑÁ¾ÜÃ.“?R”‰H{3HéeŽ’‰ÊÃ.××IëÙRÓ†>yƒ´ 9£m©A¦o \6¿dÉÑh|àf­-µËJ´k :4!kKÍFù¯sÁEaΖ$®²¹‚A…Ù}‡a”Þm©E«¤=ü7ÈAl©¿­ÁÕ_.—€K`JØ?½ ¶Ô»G‡{—·ð8¬¨ºÂ–º1è´2¶Ô€‰Žªs-&¨{ûÄB{ï°«[ß\P©±1»ɽ ÛRcÿåÝ+­Í°­»-áDÎY཰؞(¶ÔÓ·x޲‘TNXŸ…òÍÄÃ.‡w¯|ÀJé×$±¥^nÞ‚Û€q™ºëô°6¹ËD†B—ƒËaÂ:pÑoG[jø·¸Å6D:H‘ꥹÖÇùÅ2–ºÕØÚÞÙÝê‰oqŒÀþ:ür ¼{ ˆ‘±@ê?ƒÏ“¾¼ø½ûò ¸Dè¤p$G§`WOŠôY°ÔÝÅæÖ˰Ô!p ˜Î*¦Úpííâp üåØ?½=¾¼;ÛìôØã±Ôd²0ÄãG–¥†UèFÝÞé—ñ÷ø5ßËür–øæòÇXQ[Î;ñyïÓ‚ÅuþåtþÊVÖŠ8bi†O!°Ú.,õÖ*}!¬º`•¼P†˜ànoé¬+Îe–V¨Í–Ý·ƒF nE.­®:¤-Fáø³%Ãe©·v÷öwoû¼æf+êòø‘Xjue.Ž~©q6|ýb\]ýgðyÒØ^—ƒë€ë€ëÀÛ×:»8€Îö2¶ò·¥þõëæCgîî¾qßl`Ï&J|ÑÅŸý~UÛ­«Õ•9ÿôg£%¯K`¤’-õÆÎÁáÞÍí=l©GÜ—;à—:žžöG?ê¾E˜§a,£sY…ÕsîKÁuÀuÀuÀu`Š: sî|0ùÊ7™áóà=Îõp߸m\Í4p$Èi15žÜÑ”ñd?N²×%àø“$í5¤RØi(;…«&[êëëË›;þ‘i´q\*N; ¶ÔâáíóÎ9y¹¸¸üI:@öÍfô$ÓÖKØRŸŸÿêt–NƒþlØ3!î˜÷ú·8Zvðmáa¹s/›­K¯öìlÿÐû¹WL¬<ìÈŸœÜ¤Êóäø‹.?RÉ–šYê+òøAP:ÃR‡šÓ¡ŠØ'ËèœëÁ—ßQ/c6œ½ËDôÉåàrppx  ÷tnTvœ™ì‰³Î‡íjölj4hÜøz0²4ÉþdDPÎéÙ "¦3’ƒžÕ¤Êóì y.?Lê—ZÆòQMç$Ò‰òÙLOO¤U9Ã÷°ÉYNO”'Ä p7õ°ËÁuÀuÀuÀu` : 3ûÈ{´sPš§&4_·Ûí‡Áý,‘…ý4(já§aõ'·w#ÜÈF# ïõõu¤†»Eá;;Û?â¸âù?~H‘ácôÓ§Oˆ‰ KÑF%¦ƒr æ<¿‚˜ò ÆÄã§c¼‹$ÓÓSD#»ó&ÝÁUOH<žŒKÀ%  F•{#ÝPËŠ2#À–šÆ²ÁOs ZãQ†¥~ îÁÓtNËuÀuÀuÀu Rˆ¢sXi>²ßåù'y¤v‹ˆ ìP| ~šxjÕäPoÐlÁȰÜ⯻»»p3‚ý”¸ïíîÊ;ß¿¾½¾¾¾8?¿¼¼Œzkk 1···wvvhÖ\P¸ó¿þý?Å_ˆ‰Ç‡›››ûûûHw¤÷a1B%¿¿Ÿˆ-Ê…ìI¹þ 0NNç3?]°¥¾"‡ÕÍGðF§Ý$¿Ôß6îîn–Ý–Z”@äèa—ƒë€ë€ëÀttài¶Ôí6èæàÁª Ö™Œ9ħž½çf÷_¿®š­¹Ã›æ9Ù{’6fg «óöã- qÉm—çç?^^^‹Ë¿ÅÅEÀhä‚òr¶C´œ.µ¥Ž¿‚±Æáb²_œtL¼Ë—¸»•Ëm©ÿèæUxƒ`â—šl©oŠ¶Ô¡Ôƒ>zIÙl¶¥6HÜíeÓºÄy5çÕ\\\¦£cÛRC GKw1ކר••Ü=7aÓ9¸ƒ‡ïÒǽ·ØØ]zèµÍöÏçg†»¸ŽlËý=¶ÿ‹X@{kÖýþmOÃ5íׯ_——a³÷ÄÚ>þŠÊH:¸£fR~€ud´¶¶3ì㔇C)õ7T¼H.w#²Ÿ¦ó§Èš£ötÌ‹îôÀs²¥Æ?drO†cP¸?òŽ·¥ž‚½ ñ=,rÏËåà:à:à:u@FÅ)ØR#£Z0µXé46;ƒ½åA·óðx{cj ^K—ÎüÏÎÆIóì²Dn·;ÅÝkk«KK §§g°ÙH>µ‚1K|"tfaðÍ»$q‡%‰<¼†µ Øëoß¾«–‡5Šy7XÇ ê˜‚ÈêƒvR7cÔ¿Qæÿ¨³Z×ÊþG:¯œm׌Ççc¦ÃǸœ]ή®®ªŒ§aKèL»1Xí æç@Eµff?4[Ë×ý?~Üà2[G[êÕÕµÃÃCÄ<<a [ÜãBðÇ ¢a ëØ@K »;»0ÕX^\ÄóY>X-LÊôk)KÍlô¹$ް¤ƒ4‘Œ§±ÙQÐ<®ããc`n@ùɉÇSr ¸¸oª]í<¤ñ(h„ÅÑGã†Úç&² –ºxÇm©Ý×r0 tÎÌyS×××Ò±m©eF÷ôÄÛÛ~§Ój5Ë °iÍζn.Ï—ÝõÎÆÆÚpÛè[¬óÉÉ 4îÖ~zooï÷ïߨPøåË)ÞÚ—/xrÉOàÙC"Çtì9/1Œùää§$ iXQaãEÜ—––$q؅õ¢9r ¸&+µ¥Æ×³äwH”Ïÿ>t:pÕsv¡Û`s=×iooíînÝÞÝ-,,ˆÇAÛryØåà:à:à:à:ðâ: §'^t>tšìGO®ÕƒþúbóÛZåq†O˜DÁßÜô—–æÎÝÝ^_v:Íá[Ÿ…¿âp ¼k °ÇÁùf»·³w¸·{݈¸¹ÜiÁ·Ï?à«ÉµÕ3MØ\“-5ð³ÛRëÆí×âf<_ç]\þj ç©¶Ôã²Ôà‰Á¶[÷½Þªãéw }¼ð.’€ÚR«ý4ÍPŒ™u[bd©oÈ̶¥ÞÛݺ»½]XÎø¥~¡"z².—€KÀ%àÈI è—:²ÔÝÅæÖDYj¾KÀ%à.è—zƒXê=f©…Žn,´[óÄR ¾ž•{‹Ýí1îKmOˆÑíŒÁS5§¢¶¶ÖÓÞ]&¬E®®®®“Òb©É£knl #Må 8.KmzλJ\.?XÑ–ÖÁ¼ƒœÙ«-5¼í\ÿ¾>¿ÂéPü…±1ƒÓq@êÞ·-쀎¶Ô°€¼j.—€KÀ%ð% ,õÅöüª–ë%l©ß`õ½H.—À›’€ØRŸm¶™¥†-µîïÀØ´ ¶Ôb2‹3`ˆžÆ½1C[±Äø¥žßàé8wå:à:à:à:0Žð1 r>Yþ;Øéö9LósÞ}SÀ ãp LVö;¼|”®Z|Äý¯37Ï>W7ä]›á§ÝÜÚÙÙÝÚp[êɶ„§æp ¸\õ%ðöm©á'òÎÏÏØ_® áº~%&œ^[¯yã¾îñ].éH kK½{Ù;dK­ë~²¥†A5~¥‹~¸-µ¬<Ü>Øåà:à:à:ðJ:ð¶Ôõ§çnw}{{ûââî¢ÏÎÎ`*Ox©Ÿˆ=+±þ[Ó%à˜¾‚-52.a\Ñ–úœþBVÔ0õ[j>·)Œbr†“ÞùM‹íŒËÁåà:à:à:ðâ:0 SÊôôÄÌØ«VŒUsês¼àµÛ³5§jÓ1&œð!SœkêV•¹ðÏýú…q"î§§§ˆƒ¼ E8þPhD¸Ûí"Àzœ Æé‰ä—ÚZq)WÍ1²¶Ôü†¼§pÜÃ.××××Ö>=1ë—Zç°á3èÓ¼v¡âI{D«bCbï6ë¹¹9PËØå+j`k0Öøð—”weÁÐÖøX Øýùó§««_Z“0-ãOä «\ÀÙF[Ò<®Í¦Omx.¿AÁ~ZXêÀYl  ¿¯/¯˜¥ÏÔífCYê»Û…?=ñoP¯£KÀ%àxsx-¿Ô´Oc—žÿþ-H6''' ¥¶±sÑÊfÐØ× އ9–ZÚË–§*üæÏ äøã$`üRG–¨û=šê—Zm©wM¶‘¥ÆžÅŒ¿ sÕÎÓ»¸¸¸LKˆ ~©í¤“VÕ¬ý4–ZR«§‹ŽÅw`襥%I¬v¿+´tÄÓ›››Ø¡ˆ_ÁCÇ’cç" Eäϵµ5ṯ®®¸<´¶ÝUá?½x…\oQÑ–šð°ìòEÙƒQ°¥¶,uôÊÌxZœ„çÅp™Ë\ârp9¸üÍ:0][j™hë3Ü ©¾}û† … ¶//¯°íö$@íNgN6,ʵºº†=‹pèW֑φÕ5þüù“G¼&6" M0ÖòV4ª~2Dð].úH¾¨KMôs<=ÑØRÓñ‰nK]_¶Ó%àp ¸^J¯eK=‘úÀL3(êéðå)³'âp ‘@²¥ÞÚÝ;Ü»!Cl©aõá¶ÔÎÇ;7ì:à:à:ðVtàl©ë³ÔU³ï§OŸ@N;žv|æøÃ$ –Wt–K8‘}T»_ê?¬¡½:.—€Kà’ÀôýRÿQâóʸ\•@ôKÝÛÚ=K}Ó§ƒÇ™}Xî|(óKM^>¨î—ÚmÇÝŽÙuÀuÀuà•uàmÛROt¾öÄ\.·.±¥& MñÏ#ýmŠbýñ"#¯²KàIÐ/–‚Á4ïPœ‘‰Xj2‘¤s¶Ôál*ú¿s3.××××Ñž·àЍƒŠ°4;A>‡¥®ÏéžGî97Aç8f Þˆò‹é—®JYjœXŽtp¼"îK¦H|ck ·wvpà¹<į[ Èaš2býñ$lá/¹þB È(ÔâñGz(qÕø—¶Ô‰Îy1,5=1]²Üƒ´„§érpppp¨ÔÁ€üT1K‰3 ö>¥? “£Ø@K»:·÷ˆ$`×ÑjµJfOkK Ô{xx(1‹œqé¡”¥F:âaÀ°$xpp Çžƒ&¿¹¹‰Å!àÄ™Q ÒøC—€K /µ¥†IH°ýÞ “jú'oKmOO¤X‚ÁMš.މ.—‰ë€ë€ëÀKèÙR–Ú¦/C®§±Ô°{Æ…ÉQl %}¦öóòŽ›s…ÉÙRõVoz±ŒF.e©Q.yŽÔPHÉ&Ý_¿~]\\„íG|+ƤoÑNSWÿÕ%POjKÍãR³Ù¢—x(hl©ÅP-ËR³+kêˆÎ!‰¨].×××iêÀ«ØR×çt———ONNJçâRŽYbvææ"—Œ?Æ“b"¥)€={t rzii ¤5l?â[ˆ)aıþ¨&<–KÀ% ã?zÖ <~Ði/ÌOË­m©Ù­žÛR;Ï$Æåàrppx:ð¶Ô‘Ÿ ¾}û¶¿¿ý2Ë"¶X5§˜Ã+.˜8 žF8ZEÛKYjx!îbìA ùî$:xk¤߯ççdƒ'©G6¥Gp Ô‘€ÚyD[j`ëÐÓ° ¶ÔÁ7³Ú®éç"kÇæa»ËÁåà:à:à:ðÂ:PbK-^`G Ä§ÙR×™SmœN§sÌ,.çam²··'†”¤26|Ã\®¬ŽàØ&NÖØlŽ-6ÙòÓîî.<‡ »‹‹s„åáÎîî§OŸ–áïc~[9…Ƽ><<‚ǹ¹¹ú¼û¸ðø.¿JjK:“Gjb©ù¸1cøßÜÂÒååÅÍʶÔsíæÆöÎþÎæ5{Ù¤ÎiÆ ô8’yX¤ærp9¸¸¸L\ÈV±ÕNŸŸÓ6ä¼rÐï.6·ÖJŒ%b4¼õdTýœwÿ*xá•u üUÀä'—ƒÓÍ6Î#ßÝݹս Á`¥Óú8¿m©Ùfz„-µó1¢<.—ƒë€ë€ëÀ4t kKd>Ò’áÉxµzλÂðʺþ6 äm©õ{íN¶Ô°¢&¶u„_j1¸^ÖÃ.××××ÖŒ-µÉk”)ÃÓ<~È÷œwÿ6„áõu üUÈÙRÓi‰2‘H°¥‰0K-'•? nK휴vçäD.—ƒëÀuà5l©¥þ«@’WÖ%P_Á–š-;O«e4#ç¢_jõ®§§U©_êæ!œóæötî_ÔÚåàrpp(óK?I±t:|ÓüœwëÏÍÓ%àxwP¿Ô ø¥<Íç·¶fHM([©·‘~©›™"7£Šæ2w™»¸ü½:à¶Ôïsx]°r¶Ô § V‹Ûb©)PË/µsÕΡº¸¸¸LQÜ–ú†'^5—À{“@´¥æÓÉêƒm©ªéVðKí¶ÔÔÀ/'äuž\œvx:ð¶m©­ßhx€†«éªóÉ‹ÈïÆ‡ëëŸ%ýO#üù³>|ê°éK ðoý´¤ü-—€K@$ ¶Ô<.[jõK·¥˜‹·¥ñ¹M§ËÁuÀuÀuàµtàUl©ë£|í½¿¿ïã¿ûûß¿ùò¥×íÖ|ÝVsròS%l<™üü©k&˜‹V< g¤çÁ§eäo¹þ ¨-5NxQ[ê°C±Ä–ø~ô‚ÇÁàŒÊårÎÆåà:à:à:à:0=x[ê"¿[…ä+p¼à*äâòRþ<;;ûüéŽ9Ä™†¿~ý’‡à‰ñ¼Û]wédD檅?F˜NL g%FÆ××בîñà5üúãÇùqÒ,–0W6Dˆpû/———‘îršºG0ò1©À ÝA–l·Ùòt å©‘?w üIˆ¶ÔèO²‘N¶Ôˆ?z¥K‘( çiD.—ƒë€ë€ëÀ”tà5l©GvžBŽ÷µÇ.¡nlmõûýí9.ï >@êññ Xm„‰áîÓ l’˜s…óf¬ó/ŽXGR¸#¬’o6ooo¯ÿÆ“øÐb—"'ë…SÐ÷÷÷‘îѽ Œr"œs¾µµ!Iሸn·ûOlyÖ»ëx¶±±Eÿ“`“×Å%P%í“b?Íÿ%[jñøû³û¥v>Þ¿E¸¸¸¼!˜®-µØF[þó£€N{OLS“™fæ•Áƒñ=::’_VWWX[[»¹¹ 8¸±°°Ã6ÅÜÎGB`·×ë!Ðm$¤ñë·oßðÖ&ÀÖEP< '2dwwwKKK(g®À(öím_žžž!}€¶myVV¨j ¹K rd.?UbK͸¨M÷‹¬-µô4œžè¶Ô$÷“-úàrp9¸¸¼ªLÙ–ÄmŽ?FûËÉ/öႵ¥¾¾¾£0’ø÷ïßcyðäøøXÆ×VWeS#n$UGPÇ%ðgH@m:˜”æsÃ¶ß K­(ÃRS?î?’,Ìg †‡E..—ƒë€ë€ëÀ è@–¥Nã­Z4VÏÒb§ñÒ×>{gwfËð÷1?%ÐÌ6þ¿ÇǬpâaŸÿûï¿`‹Å#ud…™Q¾€ÉÅÅyÜXÊgÛú‚&Ç.CŸàEß­­o««kA¶9Âp,õÞžÚRïíí¡¸ø æxEà>¢=ãO<Œ9¢j@äp ïéÈù¥ÛÑÓw Ô”€ú¥–½‰–ús K——·wq•<è´šÛ;û;›° CoÁ@PúQ©föÍ%àp ¸\OH °¼@oØùg__=èw›[kjÆPš²u¾1nÖÏywܼÞK|À·œ~/åå|! ìŸÞœ\N7Û;XîÞÜXÓiä×+ æçƒ-uÊ?ù¥6«d6µVÆÃbxîrp9¸¸¸¼¬dXj3 ±¸Ùì9ìésÞ}¡¹üµ’É-&"‡‡‡¹…ÍkÉóu ¼¢Ô–ú‘YêÈUÓfE*T°¥N%’-5~¶SlÇf­Ù<,VÔ.—ƒë€ë€ëÀ‹é@Æ–ÚÈùEm©Ÿc‡ýŠ“ýKd ë6icŸ%ŒR^" OÓ%ðŽ$üã‘W46¥zå¶Ôð´',óY[jçc^–ñïÎý»¸¸X(züù¼¨-µ³ÔâÀÚfÜpëñßÿÁðãA/ªKà%$ Ì´°Ôì:/pÕ–¥æœ… .;¬õTç`^Œƒqž[wñ»Ž¹Ž¹¸t Äã‡ÌM£fËç0ÍÏywT¹üw—€KàK@¾Ñ).dD0³ì<ÎØRO{9–Ÿv~ÚuÀuÀuÀu`Ú:à¶Ôï}xÑ]œ’-5j媩–E[jFßnKM½õÂ×{Øåà:à:à:ð*:à¶Ô&ñ ¹Þ±²¶ÔÌR A³¥!nKí¾M Óæ¢Ü~Ôeî:à:PÔ7nK ×θpÃ!Þð'U"XßÒUW<±4‚¤I÷¾ÄþÖóqÊ3ÓGaŸ_OÁ%ð–%l©©ŒÖ=Šâ¢:yüÐ}‹nKMÄ´óÓÎÍ»¸¸¼¾¼Š-uýó(Ë}c€¡qnK<ûðçÏŸCÒ±g£Iš¸bú'>¬~ùÆŒ9¼T#“oâ~¹þ` [jªbà§…«fH €06lkb<~¸-µóÖÐç­ÝŽÖuÀu`Ú:ð*¶Ôà†kb‚¸K“ë—/_¶66ö÷÷åÝÈøþúõkyy~èp—s¼i¶R‹â?~ü(Í1· ‘sþãG¸ˆ†W;yÜ0ÂÝ.Â(©!/Üc–{ŽaâöùÓ'ë8ä<ÖwHúÈôóçOr¤­] ËÙæÂ¯×”žGs ¼; [j<øÎ‹Tub©uÿt³ñè¶ÔÔës3^ÿVà:à:à:ð*¶Ôîs¼ìz·½…DƧvãœø¡ÚÎý C‘õõÏ8cp¼4³\úH,øõïßðKAy£¤{||‚?ñ‘—9.,÷Ãøµ·±ñû÷﬙W¥?z—××kk_†¤i9õÑ‚ó.÷)cK ´\fKMK[ñ±‡û yÚÃå~©—uÛV××××Õ)ÛR‹í2æI±c–I_p°½G0 _{ã…ãÓÅBƒæÓTåLõ»»»¥¥¥ƒƒƒø+ëÍ^Pxn®òdõ\úHóÛ·oH2M·¥v4úÇK ú¥~$ÿx‘¥æC_Ä–šP6í‚à΀›ž¸-õër3ιü]\\^Å–:Ç A 9^öäøx•ñwà®(ðáÃPË———[Ýn/þº··wxt8(3*ÚR4¯ÖƵ.©1¸oçJbKè9¶Xß*îYž#MXiI³¾ÜþxàåüS%l©ÄRU‡»²Ô kÂÝxǰԌÛûÞ××××iëÀ«ØRG~z$,ˆ¼¬lì;<:Ü.X0onnb‡"’ŠxáÈûÂÆÖUm©‹|³.Û w±ëÀ…ƒÄQ6”%‰<4HwÙ+ okK]š¾ì¹<>>^Y–f§3û–‘Bó.÷+YÁÒ鉄žÉ¨Cxke©ùòÇâ§ù.v!·¥v~È9B××××Õ)ÛR;ÓGóóóŸ?ON~‚“Îñ¸««+[[[ÀÓ=Á»až¥Ì a£¼³½]šuÑ–:F‹Œ²€~qq޼`U‚°DF×âââÚÚZ| OŽàñ€;òÜå,u“ KÆÙÃÓæŽGuÆ£Çw ¼ ¨-µœ›È~©å‰Üþ×™›»¾¼¾¸½ŠžiÎ<ç;èùÛ‡û;p´‰…ìÃÃ]ýíÏïE(^N—€KÀ%àxã¥*{ûà3Îuõ ß]ln­UîêCd¼%ÖÏO¸žóî²{ÝW£ªá$¤ª0‰{._·¨ž»Kàu%°zsr98Ýloìîí\c·…~$,ϵæçÿ!Wتø@ÞF§ùî¶ÔÓö½úº<óp.××·©¯bKýd,þºóýX¹ÃDÜwÀÁì!ïæì¹ÇÊÅ#»þ0 D[j†Î‰«V[j2é‡é43dKm=~@ Ó¶Ÿ{Ý=æ^_—¿ë€ë€ëÀ[ÐW±¥Ž®âþ04`«ƒeÃ÷ï;096J©iΞû–‰WÍ%0RÖ–Zä…3éUf©Ãl²¶¶¶ÔÇÃ.××××éè@‘¥Ö¹)|p­šŸÃ4?çÝ‘Sò‰»êÿþ»€½Çÿý7Ä16JëVo¤É¼oAÖ–šYj媣-õÂõååÕí¬>¢-uocóhWm©ïî ꪖ=ï„®=ìrppppx!(³¥¦yÇm©ß¶ð2¸þ6 |?½ù™l©woïù€D:v¼±2×ü[jBÙø£z wøI,5˜íÄOÏx˜%ãrï.—ƒë€ëÀ ê€e©í˜3Òá9LósÞýÛ@†××%ð÷H€ ¥é’DZóñ4¡hqã ××—7·ôžæ:z››‡»[××·ìñ?!®sÒÎÍ»¸¸¸LO0%µZØãG‡å®ßH¥þ{@Œ×Ô%ðv$<~´6vŽ÷2,õò\“<~D „§Ùÿ<4ÛR3OÞ?^‡p¾Ó¹×××ׂÐ|DlÍGveIÎÚd\Ÿ¥n·gÇÍǾ,.Îþüy}ý³¼Ï07)ïp LYlK=C,5óÓÂR‹Ÿb©çpPê-|O?â<°ÙN»¹±¹½¿»í¶ÔÎÍe/å2w™»¸ˆ¼–-uýIú >›qf æÖxŠ!ò‚Û!n¡ëÆcº\/*¢-µd—l©Ù„ùiài¾³S=õìA ÛR«ŸçéÝfÔuÀuÀu`ª:ð*¶Ôõ¹çè~’85฻¾Ž°L´|þô ‡&Jšm>6¥ÓÁ¿³øO¢Å3 ÀëHIҰ{Y^^Æë¸Ëâ~¹\¯"¢-5ãib©Å–ú±÷h‘w½&ßÉö÷6ZÓ•4ýñ8#<ìrpppp˜†¨ÏW¶ƒˆ ~=Ç·týƒã.IœÔ sÍË++ÑÍóÎÎNocã÷ïßÚ’DåíßßcVíãŸûûønd¬½»ŽD666âÑß8“e¯àþœJ˜ÿîp Œ–<ðáApXв"g†ÔÊR‡½‰t^9ut·¥v.jª\”ÛѺ­ë€ëÀ«ÛRƒ Æ…y’Xä`'-@ÖÞãÄOIÃ)½^Ïq\2^Y]]Åí­¸ˆôs|7>A"++„4Â’ ÷ÅÅÅÝÝÝÒÒÒÁÁÁè9ßc¸\/&hKÍǼðºXÎP ¶ÔK×—·lõÁ V£[ê·¥¦µÈ ù[ Ííé‹$\.××¼¼–-u} éÓÚCǰ}XŒY|‚ø<7+7/Ö°í}~~633‹À‹¡OØ%à&cK½w¸·×¿—³^€œKù¥–õq²¨V¬í~©£užÞuÀuÀuà•uàUl©#<_Dîyvf&L¦ƒèÁø°ÈIykÄ¿½½…MÀtܰØjµ`rqq¹µµÕívGÉ#¸\/$cKÍ^§q9ãixÉ;.²¥Æ¥w~$a·¥vÛq·›wppxUx[j.ל’£=ôêÚÚáá!ÞÂ]Œ=pÁºZŒ@`í³£åtÑ–zmuUâ_]]Á„ZA@lNÈ"e¤ yÍr{4—€KàI°¶ÔbQ­wÔv†ôpyâ~©Ý®Ñm[]\\^U^Ç/uý©6b\ì,„Å3,7pGXR»|||N§Ž-õÎî.ÐóÇvÙÑ76H†Ý`©÷öÜ–º~ãxL—Àä% ¶ÔꆚŽ#WÃUÁÏðK­¶ÔôSØm²¥ÞÙßÙt¿ÔnãôÑí\E.—ƒëÀôtàµl©këŸöRgÒ†ùPµ;Ÿ®#+ãx³°¶ÔG{}väCÀù±±Ði&[j­@Ú¼è~©Ý†ò•m(_•óº»þ»¼ x[êIáéh³kÅÅÅ7 ¼`.—@ D[j‰L'$ÊÏu–zá§'>¤ÔKÝ»¾¾ÅÈ‚íÆ<¨973=nƹa×7××טU´ZÀÏÏwX|6B£±zÐï.6·Öæ†Ì‚ÏašŸó®- £÷0“ÎÏÏÃÿÝÜܰ×™Ñ=ŽKÀ%ðŠØ?½9¹œn¶¶vŽv‰¥ff|Œðo ®®®À$EìÏGöé˲Ôõ§XNÇÇǹøpôaÏP«ŸšÇt ¸Þ²Ø 5F¤Gá¤í¹‰d"-,5“ÒÆ.„w2&Mf©?íaáf\.×××ÖõCÅó‘~#e™ë×Ôb©ës̘Y[­ÖííM,ÉÙÙÙÊÒR´KÁ—Þn·‹S»Ýu± Ç…ôŽŽàYo~þ#ŒLŽ9øïJø®]__Ç[¸#¬oÍÎ"ÂçOŸççs‡Óë˜ø[F$^6—À»–@°™ž‘}†ÊU‹¯<®Xj±™4y×#ÏÕÌ ðÏbKM,uàN<,œ½ËÁåà:à:à:ð²:PfKÍ9²{ª¡×sì¡ës̘=qžËþ÷ýXàãímÅÎ|&ùçÏŸáG¯ÛííïïJ4¤wwwyy‰Æ¿v»7·7ïîííìlK¼ã¼…ûÞnx‹¡öÏÿþ[][ûñãGÌáÕÕU¡óýr ¸^TjKÍØX»w= ƒ¥æä{t–Z{~²¥v>æ…ùËÁxØ¿¸¸üõ:µ¥NsЋÙRƒ?Æ…9÷ÈU :·÷8gc2ýðáÃýã# 2ÞÜÜà5ðÖßÂÞ©ñPïÉñ©¼ˆô¿}ûŒ‡øP `0¢ÅËÏÎN{½bâ~¨k¤¹°° qpL,ÃÑÁDöË%àxi ¨-5ï3Ô•³9ÑEm©•Ÿe-–"l´FaÑ^–‡pþÛ9o××××ÈØR›o/eKÝïßãÂL‰;Â2=ËnE{Ó¶ðÙ0팋‰DžéÀ~0:Ò–#‡‡îï…uÆ)è[ƒ†œÓét l‹ÐïÌãœ.é—KÀ%ð‚ ͶÔì&¶ÔŠ“¹«RXm©Ãåº7\u´¥F—v~Úùi×××שëÀëØR×·¡¯½ ›aå ‹êÛÛ[!’£4£s‚ê‘„¶é—†ggÙX“ ùRŒ` ;Ü÷ööððpcCÍE^JxÒ.—÷A^öœCl©ã“dKÍKaúÒ¥VÔºÃZú°ÛR»¹ÛŽ»¸¸¼Š¼’-uä§G¢ˆÈ7Ãôâóçu¡¨ ‡7aΈøëêêj{[±¯µÕ. ¯®®ð&Ä|x¼žKŠ¥5¹Á±m âýr ¸¦ ¶”–HÁã;ÎS[jý@Et¶àoñÚt[ê©ó1½Ý¤ûQq¿:®®V¦nK=î”9fØ~ˆm´¤ŸïììÀBvÀÜËËËú«ÉÆrÏ1Œ·...ðîçÒ”?‘ |ñã–Üã»\O€µ¥&_yä®wµ«þßÜÜÂõõåÝlC3„¤íV³ûuópwëúúKaÙu¡åÌÙÌèa—ƒë€ë€ë€ëÀ‹é@*¶úÁ\x~^l…uÞY=èw›[kdX\uá­ú'¶äyλO˜¤Ÿö ŒLàb®Bžöº¿åp Œ+ï§7?/§›­­ý££½û桉Ì}hµ?Îÿ#ß§"žª&:1‘žƒ±v[j÷Éý.üpcãÎÂâ"îËËŸÀß\ßÞNð;ÃýýÜZ­®¯ãëêê:¶"¡ËL0ýš:F›“²<.oWòïîäOÕ×±¥~2w†~r|xÓƒó>1§öË%à˜Ž¢-5#gb¦ÕºƒÃ0ùG¾OÉŠ|†æ'\@·¥v¿³ïÅß tõêæN¬~þü‰Ï¯_¿~¥,ò#±¹¹˜~vvƒÈc>£ápÿpúºÁý1ãžL¨ŽžŽû›s:ðJ¶ÔÏñi=…©ýóçOâ¶¶¶Þ>ôŸ‚4< —À4% ¶ÔâѲ:çaúÝ‘À6ý/\Fµ«öóýüÈ7¯¬«ÄÕaƒ<æ˜ì §»°°œÝÛÜD²z½îÂò2 2ÂâÃ\/&Ñåå…îׯ¼+?ÏùõûÜ!8ýv{iii{gGÞÅF~ì¸{ôÙÝ縸ˆcŠWV>Ãzòü×/᳑ÅZ<÷ÜÚÚúÉɉ”­ªøm–3Ç×b¾¹ºÂ)ÁÄévâ;éÑÑÂûûûËË+˜v–ºàÝ‹‹«ÿþ£YŠ6Úx_B= äœá‰I]]]`ÒÑ‘¾‹ÞYÀÍx‡Ÿ‰.Átd{»Ž.·7·ÁvK:UåáÃQÀN:òÖ𥅅ê…b H±^1Òr:/ûæxYÿæÑùiû¥–‰ù³ÔSCž‘KÀ%`%Àš¾7y?¢±ï?Ôùe²÷?nKý§Ú&þ±õŠ|­ðÄØÕP+aôö3Ea€ÎµµU N Âÿá]Ù8W4›üNàHáÝݽ>ö±v¿‚óŽqà–Ž@{l€º>;Ów±PÅn|áËáV8?{‰`޲¼²‚³Ó†—gya¡ÈM¢œb/Þ]lÇ#o}ÖjyõBMQ…X¯˜Ni9ÝÛyú·­nKíÆ%àx+H¶ÔÌRë.Dã3ËR í¶ÔîƒöU|Ð>ŸËÙƒ……†pÐjX?Kç-à¤a„á  ¶Âã]BŒpØŒ›á†%RØÞÙZø<=>>’ç¸ÀRãZ\\dÃåÅ#—Ï=:>F©ð:€;l¾‡—'¢¿“D{ñ« ²‡íxäæ‘õÊê*p6ˆv[¯˜Ni9£užþMë€ÛR¿,áåp ¸‚0ÛR‹—<ã–º[j"¥Å1}4¨FØm©ÿ^ûÅ÷é8gg ìKÌ4×…·ðOÌ–Öm²–ž™Á,²<Ç»lWŸU6ŒËz† Ž`wggfÀ=ƒðÆÅ=òâQh-Ë\8ˆs¸˜…5¶êoon/Oi†ØR£Ø¨²”V%±^1Òr:Gû¶9Ú?ö›R][í7oKƒÆsV"8y|ô@ü‰“I¥3‘Âx".¿GÁ–šL;Œ¯<€²ÔòÓÓrw[êžòmÚ»Íw´¼ï=Xd"óYey΀ 3:îÇwù„3œFF›~ |90ô¯ós pÄÇFÕ ssò.¸a¼…w¬¿ÿyñØ.dqÅ|6~•Ù—¨å໣ª<¥ÜaÑ¿GäæQlÔõªÞÙÙÊɤªœÎѾiŽöyßmþˆñêul©ëãPRX'+3ů1=UyÙ³ëçRŒ9©tžS×%ð·IÀØRSWKMh™¹j…²Ôò—Þó¶Ôʱ1¯àaá\oNÑÎvÏç©À 9cì3¼ºúE ýÂÞÁhs “‰OËËâg#߾؜K ü$ cjØoìîíÉ»0ä˜F‚˜Y™§w/ž ãW8!}ú.ˆF2¬*O©Ž±½x¦l‘·F eÏ%K ˱^1~i9GëúüæôÙÇÖá¢-µèª>2£?Ç!F}3*}t 'ó‚Y …¾‰±fÝõuÁÜø”„qGú‹0Eã‹LÔfgq—?ña\8Æïâÿes·»N¹„šKÌ?~ümàÆë똾¬-µ°Ô|Ö8¡j)Ìÿæ–®//¨7‡Åu«ÙènîìoÑa•îîè9Ëï.?Qð=w› ×p—€Kà-IÓTëÃ:=±Ã§'†²½ôé‰À¸¼/bô%1{Ý.>X n–w±ÆÆŒ/_¾ ü¸°Ž¶¿®¯ÞÙÙÅ2ûôÇ­-8äÁ ˜l!> £w:,ƒñ»¢±€—wñ'Œéà“bâÞ~í—KÀ%ð²Ø?½9Á鉽֯÷ÃヽÇj1ý¡Õü8¿˜±¥–²´›ÖÖðQyY±²žÌÃ.‡?E’·oÓ?¥M}ŒúÆêdK¾ÏèNö˜¼žÆRƒ?¶\²Ì‰b¯eïqÞNHZèäÈà (YJ"¶aôkøyee”3žœŸ‘Kâx"Å>;;¥Ïeî§MÞÐa0Ó ÈÉÍñtl¸^XlKÓÉud©c¿¶ÔÆŒZm©yd€¥¦ú+ Mpƒïv9üQ:l”]·½»¼ Ó|i&¢ïª¶] r­š@Ÿæ[ºŸ—8~ ~åᨯDµÀ\{™ŠM3í<ÞÝGœÑ–)Àr—å¼£ 4¬5è¼§FöZ ™¥´x+àx3ÑŒ|9Þ…}šdðõå%ò壩ür ¸¦!µ¥~lÇ¢-õ ëñCý~ˆ-µö[:ŸÜùž?ïqþµš%O.ç§]Þš4ˆÓÁ\Dó‘-Û ÛRG.yä,9ix Ÿsß$ È+wI*Æÿðá"üøq” ôŒ;ÂxCDCRzdÄ€,­sïJ:°Á&lÕÑØzdi=‚KÀ%ð ¨-5¼„¥ÖðOÒ½ÁRg;º~™Ò•6-”éÀc²÷ˆü´‡…/q9¸\\\^N+…¬¥ÙËrçâ6gèõ4–Z’ŒüôÈÙ7rÒˆ¹·w€+òаúàP°ŽûççæpØ“$»¶ºº»»Éo„ñD~Z]]£c\ɤä`]Ú¼$ –9Љˆ_.—ÀKK@ì¦ÉAòKMg(  l÷Kú­bñhK-‡ZOàa—ƒë€ë€ë€ëÀKë€X ž´yåðeé<ú4[êq§d ìQZ˜pD¤ëê_¿~Áðc£×ƒ+Iùßãc@dX}n^[{-ˆ™ŽYE˜m¯qá]|:û¸#,³yiIAoÃÛº· ß%à¨)FÆ4©-5óÓ‰¥†=>¯\¯DXS¼[jçc^ŽqÙºl]\\2:P°¥Vù¼˜-µÌ…õîŸ ;€€›ÿþû/€òÅå¥ìSÄ?ð¹)VÔÃ&÷\‚~F¼‹;;¥ËæeÃH|]‚øåp ¼¨¢-5@°à°ŽŸ‚_jf‚ñ©Ô–ú¥9 Oßy/××××Õ‚-uxþ²~©§Ãp¿èÜ\—@´¥;,ñŒ–:RÓÿÈÇ$ù¾îÉfËm©ÝfÚmè]\\^E^Ë–º>K=ñ9Ût ¸Þ¬ ¶ÔTRáªåú‡ð5óÓ´QïòSà­Ù/µóFι¸¸¸LS^Ë–ÚYê7‹i¼`.W”@Þ–š‹B§(GHÍÛ‰ªæC]ÀU mí¶ÔîÓÃ}¼¸¸¸¼ª¼y[êWœÝ=k—€K`ʈ¶Ôê—:zÍ7,53Òl[mYj·¥vnÞ}¼¸¸¸¼¦¸-õ”!ƒgçp TK ÚR‹Ç†Îú /Á«Ç?ÂW‹)HàªÃæDñýé~©Ý'·ûávpp˜º¸-µÃ—€KàíH gKÍÞñïüÆàŸÀF'«‚Ý´Ý–Úýp» ½ë€ë€ëÀ«éÀÛ·¥æãÆgù6ûñãǯ_¿¾©³ oooáµúc»=ÿñãÏYD}«PVAâHûóçÏCä6qœ„|ƒó‰§í º˜w¶~©Õ²C}{KÝK<~€Á–ó¨è†IÄýRûùˆ´ÉmI_Õ–ÔåïÜð߬oÞ– 7ŽÿîïÿþýåË—^·ûFÐŽi\°ýüùw¿y}=—“:&8á-©(²Óþü9DñŒÉ Ê ùNª‚,•'õ'I cKÎpµ'¹þ#fÔrJ"¯ðh‡"µÛR» åkÚPú9~^©ë€ëÀ›·¥NÞ³8¶â`Áà­ÏÎÎä DP×à4DÜ-½³³ n{~~þÇòfaðʈÙí®GΧ0âüE<ÄýììTbÂÓ?œÅýôTZø²··Ûíõ€ò™)kâD=#JËc¹çF-P¶ùy*$ª#õï&Ü|-¡HÈH¯‰<´¦ÀÕL)p:'V‰UF ±¿þãè1“p‚cÌ(gä‹ÜÝâŸaßZ]Œ-5÷ÖÐgc9ËýRÇŸÝ–ÚýѾŠ?Z?Cο¸¸¼}[ê_ <ðaö”ã1Ÿâ r:X±ßÇ}owWfØïß¿U^___œŸãHEyˆ˜€’ˆÙíöö÷5f¯×=<<ÄÃýýý³³s‰ Cü‰cq/Å‘ˆ)„qñ*-­K £°¹¾þ ³Š]¶è¹ù\ÊGGG²„` ¬?j ¿)± ¡ø.g@â¡Tycc rØÁÒüzÿá!²€ˆ¹Þ]ÏÅ´rFî(Ci•ý¡Kàù0¶Ô”˜lP_yr–ºÑ¤Ž¢=A±¸°×Î×:_ë:à:à:à:0exE[j©U÷87G¾\)XX°ÅÒaöŒðÔ² [ÜO™ëÅ´?QÇÖ‡ñDq¤ §—¯®®ž+÷Üj¶P·KKKò:^¼¸¸€u‡}hA0k<Ì<&JËc¬PŒß¾}Cl÷Íí­f]M®®®ææærql @ç¡ð) Pר,þf^…szz&üúÖÖV$¤ñëÊJ>¦•3rGžœ<—@©¢-µü*˜YîrýCcV£q{?¸í?ÜÑ}ð@ÏÝ/µÛ» ±ë€ë€ëÀ«ê€îðáùÈÚ”3?4üzŽ@$›ï1kkK ¾x7RªÑ‚&ÝûǰU© ¤+¯÷û·EÈ‹_aà &Ú(|0¸G1ÀæFȈB"òÚÚê§OŸJq$R¨Ú+YZkßl9æXÙXµ*[jðαF–ç.IÁ$·…QZXn@&™–¯áŠù¢¾&f_~·rFîQ¼£Äw Œ-hK7w÷ëþ@î’Ö?Xƒ ·wƒþÃàöî÷{D g—Ów7Qn¶êƒ#»\\\\¦¡a‡Ï#9s5sìÿz=çÄúïAö¶„³³3òç`ð-ƒÛíNòâW¶ˆ ¸K?|øðíÛ.ŒC`·?>ÂLâââ<.l$ŠÂé+f'Å«´<¶.1lkÖ϶‰ÃŽ%Ö(Æ)OÁ¼†·À^K}Åä_´fÓ&¦ ǦܣxdžKþ‚K`”‚-5Åàä|7ÀýáîaðÀÒ?´Qþ$ZiyÚÎééÄD¾Ã9æNge.渰°pss#χóÜós)TY ysuå)ã!,Σ$×VW%&„cZ9#w”¡fÛy4—À¸¶Ôxí§bG_ybKMï O?â<öº:ûÁ7ç§§wppp˜®ðéc:Ùo¤±UMŠõ™æb õß­âkÅÆ”±¯vÏĸË=\€¼`eaìÀ(ËCü ôŒ˜0³ŽT4nôzÑV"øioO ¬m]`GTŠ /..ÎG‹íÒò€óÞÞÞA¦€³Ã9f¤‰8q'bÌe޼øpžû_“òEÙÈÉI¯%=ã!j%¹Ã1áŘVÎbž>.Nòø.š` ;4êß⽃ƒ27ÿëÌ-àsÒÙÕ-‘Ôôem°2ßÙÞÚ>Üݺ¾½…Öêb”)‚°ßÃb_èrp9¸¸¸¼”`ÃÖ½óó³ùoz sÐêA¿»ØÜZÓp¥Ó¡u¾Qs¾ŒÑžóî¸yýañ77¿nm}?z¹€˜ÙÆZÍ<†¤ ›°òqûæDr÷D\VßOo~^N{­ï‡X ^Á„:pÕkK­ùùEe©e'ð4Ð7]î¶Ôn;îvó®®®¯«oÞ–ÚGNàË'âÆ6*bÖƒùuäŒ|# _'¾Çq Œ+hK-ôô#‰’»|”"¿Ô°™4íª´nÎÈ·¥vÛq·¡wppx5xû¶ÔãNÉ||| ¾¥ŸSYX³YKŽóâ 5ÃD¾#-ìŸS$×%m©%3fä ;Ô­‰¥¦§„¬™¥æçnKíväîãÅuÀuÀuàutàíÛR;Èx! ÀVvç°÷øï¿ÿ&hFòB¥õdÿ D[êA°ì ø,»#K-`[7!k‡=Œ´«‘<~¿–½ç.—ƒë€ë€ëÀËéHæùı¬ï©!ûevŸŽÇ¿Ix5].ÂÍd¿!ÖŒ™iÓ4ãçG= Ûç®/¯O/È¿º0Ø Y|pÙÛݸ½ícóïàáỼe3ž¼îa—ƒë€ë€ë€ëÀ 뻇£3;öÌ ûÑc™¯=¬Ï7¾}qwiŽs\.éI`ÿôæørp¶Ùêmíïíï_÷› ¸œ nt—;‹‹Øž(~©gg0VÍÎÌ2K±¥fKkµ¥s>æåø—­ËÖuÀuÀuÀê@Ñ–ZäßQ£—¶ò ÕYêé ÏÉ%ðwH ÙR3N&<ÝÀÁ¨3âGû©É/5Pô€59Aì`K+ú¥f~ZÏPô°øIu9¸\\\^BжÔ"gú+[ú¾¥‹iÔ—ŸåÛìÇá§¢ê ðWð+‡ƒáÈyþãGœò„²u!ä Õë-+,¢Äo÷Èb^öõ!o!Ù‘'n¾Jy¦ïHÆ–Zì§e,bÃj’þ‘µ>ß›­æ,@w´]“çnKí¼‘s‡®®®Ó×·oKù‘NÒÆ÷÷¿ÿþòåK<0üÕΔXÿL×ï~ÿòúë„xcý²üÄá±Þ²)÷˱ˆ#S(]R y GÀ<¡úõå1ÿ [jñ‰§ü41ÑŒ¥ÿÑ3=‹Ó‰ŒÅùü'„œ‹unÞuÀuÀuÀu`Ê:@>^ñ¿ö;€ÌMçðúLósXêœõ 2½¸¼ŒL-ÀɃ ‡€K8Ü-U¼³³ n~—üø!o¡^à•é(Áîz¬#ÎSľ&<ÄýìŒNäÆË~8‹»Ó»öövq!P¾ T¸¥‹ÞèJËÞ®QIÌf8Ê <þĽX£®©QñÌÅXT«ö#––ÇšÒD ŠB05"Ùák9ûøìÌçææP¼ˆúJñ¬dlaVd¥‚Q8ö}.//B,1BÌ 2Œž/ÇìÿXÐk= dm©á%O¼%D°¥¶_®ÈÒƒ‚q[j÷mâ6ô®®®¯©¯eK]öÍÙRÛíÜ º]þÄWßè•¢Ýî!¯ð¬`—å. ‚‡þöm/Ì'âöGD˜R\\\Â(\u±ÌànÅì¤x•–ÇÖ%†m-25š‰5Äm©aÙó ©K¤À«ìà±lƒ|¤´6– KK §§gHÂ`}jçÞ*Í1ñ¼~ãzL—@NY[jµÝ Ä Õep­,5Cmz}U·ÔŒªÝ–úU¸÷-0}ß.s—¹ëÀ[Óײ¥®$†ì‘´üîêꌡ‘ìááŽÚ–ôÅ´u +n.pÁ2Xì}AbÇ­¨ñ×Ò|onnüx úÚí1ó0¶Ô-üÆ6€Óò¹†"+KÍŽ>é ,µl4 „hiŸ»‡].—€KÀ%0 ¯#0wÓÇ™H0âð ÿ9?êC‰!ÎØ ¤€÷¸—´+¬QT0Ê’)~zFLXNLJˆ*7z½¸Á"øioï XfØZnâ‹‹‹óÀßqaiyl]bNB@Ãà$'óòB!bÝÁ£#_r`ÒëÅ©j»/½^¤Õm¾»;»0nA9d…OíBíC7PA˜_cóbÜø8<_$^µ_³~‹{Ì¿YÌRÓX$Œ³P.ØZ¤ÿÍ-,]^^\ÝñyãäE0ßnnmoïná@ò[ô, /øNïá|rËy¼.—ƒë€ë€ëÀ¤u³|ªŸuæh'"ÏP4Þ®ô»‹Í­µaۭ󿼯ºƒG>®ÚpùœºÏ€³‡±ûsñwÿr |?½ùy98îµ¶¿íîîÜôùlDص¥ÖüübÆã‡«3K åäñãáá^NLĈæw—€KÀ%àp LElÒÐÌxüà|ÙãGãÛÿˆÿ§!ÐÌ€Ô‘už`õ€j¬ÝÈSö¤þ °ÇÁY¯µñýß|~Ý⃠Ÿ5Þl¬Îµ’Ç`KM`[m©ù(Ø`K-VØâ“ÕÃ.×××××dK­ôTæô݈ÉÏð~¨Ç{:hÖ—ÀÓ’©3ïQb^æiJ@m©y"Û°çPäüR;KíL¼KÀ%àp ¼ Àp¶ÙÊú¥~y–”üNÀ=½ØG>>b{ÜÒ4gnÏË%àxƒˆ~©•¥¾yP÷xÍæê\3ÏR WÍ,µñKíü´só®®®®S×e£é»j8ç…Ë0q–;Þàkâêêö×/¸…¸»…óºfgК»mÍÝwæFí„|ƒS¿É%à˜¼˜¥¦±ˆPr<䉷QËg3·¥vq—€KÀ%àx‹˜š-5ô‡ÎÜÝ}ã¾Ù€Ç¸>»~hàO8†nÍ4¶[W«+sn60y„â)ºÞ•¢-µœžx!¶Ôìøc}Ám©Ý.Üíã]\\ÞªLÓ–úá¾q;Û¸šiÀ1ò­nã'<=À †ÍvÕ1„ï xa].gI ÚR7æÃŽÈvÕ¥~©ñŸžÈ~©ÉÓžø¥f_â÷ÃÃ.××××—× ¿Ôá«ëÐÉq,¿Ô°–n50Ÿ~VGØüq8ûáap? s‡ä†ÓÇüúãÇYâŠ.¥±urýógøTþø±½¹ù5âuL¸pôWÓ15>˜|–o³~"ð—].gH ú¥0PÆÿPøQÇÌé‰nKíþLܯ‹ë€ë€ëÀÑLZ|žÂSl©Çš7;ƒ‡XIö9ó<Ù¸‡Eõ`,=h~ÎR9ugâ°øŒÆ¡$€×HÿÇÑQocãúúú÷ïþòò Ž—ÒâìD³¦ÛÓiÞøÎô¦ûXõòÈ.—À%`l©›ƒpB"ÌTZ¶¥ÆP¢üt(—x§–ËÃ.×××חТ-µÈyâ~©A_]õo›íœ {KÛî?46–«.†ÐÞàwQ08¢^ ãØ?ø¯•s°¯~ýÚÙÝýùógnŽÇá‚8F1> ð,Úð‘'åp 7#„ j\0ùGoÂB:\jKÍþA’-5ÇQ[j‹M¹ËÁåà:à:à:ðr:m©Ã<•æ u]6Uæì%Šœübï¹WÙLy0 G}ØO“¯Ì›ðóño¯µÜ¼~° oL¢Ë–J ª-ÎÆ4jóEœíímP×ö¡Í Æ!¸ÀaÃzäääÄϯ™ 6ò¤\ãJ@l©cO§>> œŸTÚRË(NOt^VÏ ƒLœ³wžÞuÀuÀu` :@ŠáßÜØ;„¥›ãh,³¦˜mØ{n6…scðÐbƒHÜ{‹Ý¥‡^û×lÿlq~føfÇÈ+Yj u‹³EhrÁ¨²×ƒMõÆÒRæ™R7Ø(Þáááþ÷ïã‚ïp LJbK-Ø8ÜûÌO2,µ2¯4<ðŽ°æv~Ú9i××××)ëq:ü½ó‘åÂãÜ4©™é|øÐº½½^é46;ƒ½åA·óðx{cêååÅ> Ï(òÊE–z®Ó¹ºº’׈F °÷èv» ¨WWWs‰Gλ$ÓQǰOP ž”KÀ%P”1Æ¢ÄLkyRd©ÁÝb‚Ä™ãr~i»LOßuÌuà}éÀÔl©eâl6gÚÁjg0?*ª53û¡ÙZ¾îøñ8omçÚÈ+ƒK†O6çØ”Ù,ôîî.i\ôº]<„ëøÔ“m‹¹ËÚRýúUŒ=ðúö6%å(Ç%àxE [êÀR NÆÿ¸¿'„›ÙÊ«3Û@oßÙèÂ~ ["àZè>˜½ÄžnOS”Ãåàrppp°:0;3Ólµàûó”ßá½A÷øLïîíÖåÕÝ v+öÍÙ<Îbacp»¾¾<„«Ž~9€•A<#µƒƒƒõõuñþ çÀÆ–ÀÚ;ïzöšõŽý‡ &ðôþþ÷ËËk€uLËEJû±…gíøÛ$À§'>œô>|ÝÞ?8Ü;»ÄY«²;±Ù]n/,.&–šÎpa+/f©•Ÿv[jç§ý…ë€ë€ëÀ«èÀÓl©Ÿ6ÍßÞö;|Y^€ Hkv¶usy>x¸ì®w66Ö†Û~DŸw€¼²¡¶×%ùòå’/ÁÓ¸ÔÚÛØ|Û2‹?>¹ÎÏŸÿÞºÂñôÓZÖßr LJÆ–šNxi6g—]µû¥vÛ¬kÎI—s9¸\ÞŽÌ΂/&–ºÓn#e¼K ËŠ››þÒÒÜù¯»ÛëËN§9Öù‹“š³=—€KàÍJ ú¥îíìïíï_öÍ™Áà6cÝåe~©± D6GØ}ÖŒ;•Ãö°pù.—ƒë€ë€ëÀKê[GðwUÆÓqâ jØ.¾'LÉ-ºGG§íÖ}¯·êxú 2ôW\¼Ô–š24î­YuãSðKM¹•ñKÍB"ËÀézXø—ƒËÁuÀuÀuà…t€Ð4mýSϯVÎCüR?yR‡;Ž^o-:åxr:þ¢KÀ%ðGJ€×÷ì 2Û ªš™Öf¶Ôʹ’×k·¥v;r÷mâ:à:à:ð*:ðZ¶Ôõ'a³þ™®ßýþ%»£–£^˜µj²ãé{ø™þýû÷ÚÚ—è—Z> ÛKæcÀè³³SpäðxrrrzzZ¿$Ó%àxi [jÊjf–ì¨Å¿gŽ¥~ 8ýób"â¶Ô¯ÈÍÐâÇý«È"Ðåàrpø[uàµl©#gõ8ü*ü„çë¶Å®®®³¼–-uä§G‡oß¾íïï‹k̘|ýúUÞr¾#¼‚àSi@g„±a8ãÛÛÝí¤vwwz# à\.iJ@l©µw³!H°õ Rü¯Ó™»¾¾¾¸¹c®šìB:Úk¼³Ñë÷ûX‚£{[¯iÝór ¸\.¿V0„€±e´Ûm‹P×Öçß¾,¼É€WÞÝÝE!gggVVV˜mÀz¤ª„°Ìþþýûõõåüü"pyܹ¿Ôbr œ-ŽDür ¸ÞˆöOoŽ/Nz¶¿îííýº}ffûŽÆÊ\îäÕ–ZÌ@šMäôÄhKMOÂþk³|èr™¸\\\^Zh?ù^ÇÞ!ðógß±NO\XX€í¨ëëßÖ`zžF ¡ÿûï?Ðá¸ÛóÏá?ä’/ÇÓÏoGOÁ%0Y D[jâ élDÅÓâá×ÿ`ÂE,õÕoY$XÝi7œ¥žl3xj.—€KÀ%0®Àû‚èyã,õ¸•òø.—À;•À÷Ó›“ËÁI¯µùýð,õ 3S7›«ÊR3'-'&Šý4±Ôl)RÊ ¼4'áé;ïå:à:à:à:ˆ¶Ôº«Ç|/};,õ;^l—€Kà `2šNq¡ýÇOc¨ ,µØR_Ýžv[ê'Ø_q ¸\.À»°¥~z{’.—À[”@´¥–úüæA½w4« -cK“]y®N¶Ô9 6® ²Gv ¸\.—À K ä{©°DõÝ?5eK=~òþ†KÀ%ð.% öÓ4þ°‡ØwG&õB5‚_jrL=x¼‡7àÁý#˜êGyœ¶üªRÙ½‡]Ü\Þ\\^TdÞRïÔÙ1gˆ×ççÏÕv¿àóSó\.?DdÍA×€‡~œâÂxZ03Cj¶ Q~º9ƒ_Å/u7 >ªñ«Ø[ÆÚÃ$D—‰ëƒë€ë€ëÀu€7øDT™1ÖYê?£x5\ïJjKöuK­˜™!5,=_3ªæƒ#K]Ê ¼('áÜ'K€W?Î…»\\þz°ßKïõ«iøŽúb3ñX,5NlÁ-Ûíù777ãùäðKÝÆ¸·g?ÒO_ãOxž+;âÈÔgÇøQä/Ö¶ž°Kàéëhê§ÔšŠÓ˜«ÎžžH»i# yÿ¨æœƒ™ ãÜ6K€.×+—ƒë€ë@Qâ÷R`Mø~ƆÅè“êéãäÞ¼»»[ÿL×ï~ÿòúXG´Hò(y¾©ïïá|ú÷ïßkk_àsZqs¡2ãšït`Éý¢üäjï)¹þ. $[j¬Õ?^Ζšú¾ø©&ô=CÖ!×m©ÿz~ÈùrÿVà:à:ðŠ:`m©27q—ò¼¨-uäŒGâ…½½Ýn¯'Dz lkkkÿþû¯âæ“ùo m8Øh;Ÿ°|MÆhþóçÏ­ÝÈJy—€K \É–Z÷l©©+š³-Ú8ƒoO3nKí\‘ó…®®®¯®9[j€i¾Ò./ÊãfÁð0tqvv¹ç\¼\ ÏÎ΢=I1ý@Rk/Z;GK.—À“%l©u!{üý‡”$<~P˜F®™²-5S¾ÏÚ9›WälÐ>.—¿ë€ëÀߣé{é`p{s»°¿>‡BŸˆ('öKÍœ4[­Ñ#·¥vÞëÏæ½¼}½}]Þ…X[jãñƒ¹Ÿú°wü95òÓ#_ýöí|Þžžòœ:@àëׯòÖÂ+.xßC4ài„wvvl^/Z»‘•ò.—@•‚-5£eözœóKÍ,5ÛO?̆sjAÜnKM"sîê rWÞ.,×OÝsíúðëC´¥&óóìñCg¨·0ëw:c¾Úíöââ<ì­÷öö¤`C˜f8ÙÚÚø†¹6\Y#¼ººj«ã,õ[h\/ƒK 'dKÍ«f…³~©ÙŠK,§Ù;5ÛO»-µ¬Bt_§‡eMærp9¸¸LMì9¾ÇGöø!ûë‹{ü&d'bÍkaa¶ð™}}ýÛL£¨CR€eöÿý:÷âÉ2Ãß­Y0æp LVÉ–šWÍÂOã,`kãñƒYj"¨£µû¥v~ÚyP×××WÕœ-µýfø¢<îX§'NvÎöÔ\.·+hKÍËzØtžKM(šJm© q3?ýH«ÿG¶®&Ûçh—upppx°¶Ô9^üí°Ôowú÷’¹\“–€ÚR³Á´øÇËÙR'»´[N«ÿ·¥v[U·#wppxe¨:Ç×YêICOÏ%à!hKM'$ž&ç«if©Ù. jòø)af©Õ;u°'–¬ÄŽÍÃ.××××—ÖŒ-µ™ƒœ¥vøãp LYÉ–š-8š-ú‡=P“?‚Ô¬y?"Aeºá Ee©«… ]âŒÞÃ.××××)è@ΖÚÊÜYê)ƒ ÏÎ%à T,hXHi½eXjñKÝâÃÓNê*nà¥9 Oßy/××××™°ª¾—:KíøÆ%à˜¾Ô–ZHi¹)r6,µX„0cÍG)’]pwDâÂO ÷°ËÁuÀuÀuÀu`: g&„ï¥Iæo‡¥Æ‰-Ýn÷c»=ÿñ#œLÇóÉqVyÿáÞžýH?}?áy žÀyßúçψüñcÛÆŸ>nð].R ‡ÅÆlßÁcCj…Ú © ˜…Mmp7…e?£r»\\\\¦£ü½4z¡NsЋ²ÔõÁÄÝÝ@0®ßýþåõ5¼ïá(Dy%”Ôá|ú÷ïßkk_z½žü$ߎí%óñ££ÞÆÆ5\ÿî//¯lllÔ/‰Çt ¸^\Ä5g2þPÍ0ù5ám‹¥üt7à\µóô®®®®SÐùRZú½ôEYjáŒë\{{»Ý^ïË—/‚¡q,â¿ÿþ«¸Y&×pm_\\È_Åcj„µú÷øÇ(ÊjiŽuâLÒz—€KàYÈÙRsZLRË^Db©)<ËÛÕ 53ÖUÜ€s3Óáf\Î.g×׿\h®2_M­>¼(KÃæೳóÈ=çâåJxvvO)¦_ä­onnççŸ5ýûË.—Àd%À¸Yr¼³ÛñóA,5õeÝ»Mg»ÐI0â룔pnf Ü /|¤]x¤õ°ËÁuÀuà¯Ô)ÛRƒŸÆ…¹Ol e:¶ØÞã4 »ŽV«U:k£äÖ–úððPbʬl¯owa@²³»;Y<੹\Ï’€øÇcXÆ'&’wj=ѲÔ3äZí¤O½X$î\Ñ_ÎEÝp9x_pp˜¦LÙ–vϸ0gŠ ´Ì¾Â.Û{œ•¼ã¦ÃJÎØRTož ÓÛHÌ7 ©—––ž5ýûË.—Àd%l©É#SØã¡ß£2,5ÓÔ|éåt¯âœ7uÎØuÀuÀuÀu` :ðZ¶ÔE[çªyyyyùää¤ô×!ÖÞ¹9ØuÄ·ÆùaøE £êÉ‚OÍ%àx®‚-5¡åÂÇ&`kµ¥V–šs‹,ué>ëiòž—óa®®®­¼–-uä§GNÀß¾}Ûßß?==%¢y0@àëׯòÖko f\ð¾'šl„z<6–:ùÅEÃR3â&Üí¶ÔÎEM‹òïþ=ÄuÀu §¯eKíxú"/¶Kàe%m©K-\5ú¼ÁØØRÓCõû! vù™Un×ø×Ú5?äçhú¢®®ÓÒײ¥ö3V^—xê.w*j[jv•—³¥N,uå™UΣ8—æ:à:à:à:0x[jg©ß)àñb»^V[je©Å ¦õø-¬Ý/µs±þ-ÂuÀuÀuà-èÀ”ýRË|ì,õËâOÝ%ðN%ýR<~°K=ËR»-5óö´æp[j—ƒë€ë€ëÀkë€ÛR¿SàáÅv ü™¨²¥ÖÚܖÚíƒÙrÞí¤¹ÿ»Äy½ËÁåð&tÀm©ÿL\âµr ¼S m©£=æd³~©Ý–Úy©×æ¥øã‰+àÝÃ.ï½¼}[jœØ‚ó?¶Ûó?ÂÉt<Ÿ<ú–†óéôÓ×øS{v6‡(G N>þ ׈¯ÕUG¿S4âÅv ¼{ m©Ù/5M×LF¥ÓÝ–Úù9çª]\\Þ”¼Š-uý‰ç6¬þü»ß¿¼¾Æ¾F…(¯£äý{ºpãïß¿×Ö¾ôz=ù‰WÌ™‹—N?~lml£___㙿~y<¦KÀ%ð‚H¶ÔÔã9/ÀÏìñ£á~© tÛq×××·¨¯eK-œqkoo·Ûë}ùòE04ŽEü÷ß7 LÐöÅÅE@Ûù´ÅÒïâx5¤ƒ GÇøuJâq\.—@²¥&NZÏrI,µÛR»-5éà›°›D9ÜÇ‚·…ë€ë@Ôײ¥Î‚áaÓôÙÙy— XlßÄÑ7_1ý"o}xxòûÅ!‚gàp Ô—@²¥.ùØ+5·¥~‹Ü µï_oCé~W\\\¦lK ~°0YB®Z|êÙ{œ‚a×ÑjµJgd”œ£t` H ”¬,uá ¿aK·ŽŽwwwëÏõÓ%àxq $[j×4:X“½OÄMº-µs´Î »¸¸¼Q˜²-5ìžqa†h™¡…]¶÷8sûVm"ÌÙRTo™ãÕçkcfÕ/<—€K ¾‚-5¾%>CÓ)ã¹Â¤|M{Ý/5/7HîgÀåà:à:à:ðÚ:ðZ¶ÔY“aóíòòòÉÉIi å®Ê~ëÌÍÝÜÜÄ_Æ“4ìIÜ–º>Ôñ˜.iH ØR[~úá¡”¥†%+`5Ù³Ò½Šp;?·õtppp˜‚¼–-uä§GNÒØD¸¿¿zzÊ„Ì8¿“·r¶Ô6)xÁÏx<ðÎÎÂxWÌKÙR3;î—KÀ%ðV$l©cï&z:ce©ƒw=°ÙÒ¥…!?psîÖåà:à:à:à:0M˜²-õ¸37\ÝóèÅÅyâ½½=IdK Ç [[[аš†+k„WWWñ <‡ “‹ëùùÛÛ›ƒ££qËãñ].”@°¥Ž,5ðtS¶ÿ›[Xº¼¼¸ºcÛFÕóíÆÖööîÖÆíÍ úÀï&ž²“ºÈ>ÄÃ.××××ÖÌI>|Àƾ9c9hõè¡»ØÜZËKLp2ŽÞ9&˜¬'åp ¼k |ÿqõóºqÜkíìa÷ðMŸ—ü„ª« ÍùùE·¥vÞ¿?¸¸¸¼Ex-[jÇÓï÷xá]/%‚-µ°Ô‚ªqnKí~‘I%ÜGu0Œr}p}pxUx-[j1höË%àp d$µ¥¶{Å¢ÚýR¿En†ZÆí×Ýß‚ë€ëÀ_¯¯bKí,µ)—€K Dy[ê°7Q!›û¥~U™»ßçÅ]\\ªt`Ê~©eu–Úá”KÀ%P"à—Z<~´Zt—p¥v¿ÔÎ »_×××7£nKí°Æ%àxC¶Ô°#À>éxÎ ¡jÞ§è¶ÔÎÓúÊùrî´.·#wxC:à¶ÔoLxQ\.`K=“á§å02×u[j·¥v»m×××7ªoß–'¶t»]r&ýñ#œLÇóÉqVyÿáÞžýH?}?áyœ Ž}‚˜pMíÆ%àx[¶Ôt~‹ñʳÔì—šÙJúð¬Cœ³yCœsÉÎ%»¸üU:ð*¶ÔõgqœÛ°þ™®ßýþåõ5ö5â(Dy%ïßÓ…³ÿþ½¶ögŒËOìn+;gmllôûýúÅð˜.—À4$l© 'ffäL¶Ô–Úm©i¨ós"ý¼L×××7¡¯eK㌇LÕ{{»Ý^§ †Æ±ˆÿþû¯âfqT. í‹‹‹€¶óIZ G‘ƒ¢ÖSާ<—€K ž‚-µ°Ôñ„DðÑÔÛ›M·¥v[jRüç>\®®oJ^Ë–: †‡Íµggç‘{ÎÅ“/½ñÂÑ7_1ýˆ¾onnóÛ·oõËP x,—€KàÙˆ¶ÔM¢¦É²#Øw°yÇÀm©Ý†Ò¹y×××7ªS¶¥? ³£Ø@Ë ,>õì=Ḭ̂ëhµZ¥5Jnm©”A?+K]xAà7^5öÑÑ‘òÏž€KÀ%0I $[êG$ –º1Ãw±æÊ°ÔnKMbq¾ÖmÇ]\\ÞŠLÙ–vϸ0;Š ´LÆÂ.Û{œ¤¼ã¦ÃÜ̳¥>88¨ßaBnÀ{kkëÇ:CO xZ.—À³%l©KΗE±a©Ý–šYZyø™m.××××Öײ¥®oǼ¼¼|rrR:Q¿^quææ`àDOðçÉÉ1œ‡·2àþlà ¸\““@²¥,u@ÎÜÛÝ–Úmˆ›wppx“:ðZ¶Ô‘Ÿ9Ãèyÿôô” ™_¿~•·r¶Ô6)°Ñ¸à}§ÞÙÙAX8rñ@ŽûÈx—€K`z(ÚR[–Úm©“&]|m.ÊËàzè:à:PªS¶¥wnît:Ç|µÛíÅÅyØ[ïííI"CXj8À7̵a<ðêêj.ëÌÞÆq‹åñ].—@Ñ–Ú²ÔÍæÿæ–.//®îØÙ<4æÛ­ííÝ­Û››•ÕUøÝÄol~M—Äñ°ËÁuÀuÀuÀuàEu;êaXŒ}slóZ=zè.6·ÖÒÃÉΞ@ÆÑ;ÇdSöÔ\.÷+ï?®~^7Ž{­ï‡?vw¿Ý>„ª<6–:ùÅÅ`KMû1ÂVÒ½YÅ ¸±sº®®®®SÐײ¥v<ý~A—Ü%ð‚¨²¥¿ÔdK;΀ÁV i¾³_R:=‘¼•}ßî‹ãMù‹¥ï®îÆuÀuà%uàµl©Å_ž_.—€K #‚-u<í… 4ÙRPã ˜æ¯ùYjaè¹ÛÚúÞ×××שëÀ«ØR;Kí@Ê%à(‘@µ-5Aéf,5EaÜü@„ XjzB,5±Ø|w>Ò9r××××éëÀ”ýRË$ê,µÃ)—€K DÑ/5aæìïlê¡¶Ô8ë_ýl©é7b¯…¥ž‚Íœsá.g×××׫nKí°Æ%àxCH¶Ô„!ka©Ù˜šn2~±]5ñÓnKÍ é<½ÛÓ»¸¸¼‚¸-õ^—€K ØR"Ã÷ãdÙy(td©aõ?áXþQü~P<·¥fA8oäÜ¡ë€ë€ëÀ«èÀÛ·¥Æ‰-8òðc»=ÿñ#œLÇóÉqö¡œƒçÓ駯ñ'<ÏÄÁ“?¾ëÆ%àxC¶ÔÂL?°eG઩˜lKML,sÒÊÈ27ë¶ÔÎO¿ä^~èØôí2ý›ƒËÜuà}éÀ«ØRןÂqnÃúgº~÷û—××Ø×ˆ£åu”\ÎAÄ™ˆ¿ÿ^[ûÒëõä§œf nè¹Äg(Ö/‰Çt ¸^\Á–: dÍPæ¤&ÀÓÜÇqÇÃGîînKíü´Ûл¸¸¼¢¼–-µpÆu®½½Ýn¯÷åÿÛ»z€Ä™-Ê·tØI‰tÒIÉvØi'%vn‡”Øa'–Úi§Ý³d;í¤Ó’í´ƒò;7†ðØ…ää½ÍÃd299ÉÜœœÜ99ÑÃ"^__{q³æËò'DÛNǶƒu{®ý'"@ÖßKíº¨÷C; ñRË]Kæø_ ±5´Cù^êÍÒr¨½ñ|‘äÀ7ã@\^êÑ`xVßþôôlµç@9To5Æ€´¹ùÆë×è&‘|>±Í‹ÅâÃÝÝzlØ^|/µõqè7‡ŽJ-—²DÒOËPã)«RÓK-¼¡—š¹xÉr€ˆ‰{©¡OcBß§h 4§ž;·!étzb„–»^j„Ôív[KŽ‹ÑúÖøÍL½^ïöööþñ‘¹ü¶7t㑯'£^j_ŸªÔˆ§qïH%ñÍDÒs¸ÒKýÍôyZ¢7šþxr€Ø(Dì¥V³õ4k·®ê²;·Ý="oûÑa x©¯®®¦ßF»Ù:›Í"þ¾¸¸Xϸ‚­"[Š@ÐKm¾EU©‡B¶É¦—”ŒÌK-÷8æúðlõ#F?¥éox.x.È­ã@\^jï¡cލûûûi*õ´ ²¹\·Ûµ¿bkÆ ¬#s4‡EˆX%/µÙ•º>Là¬^êA"¥¯®’2g^jêÓÔ³Ér€ˆqy©¡UÏÙ-×ëõËˡ‡Ó³°pzzªÛΈ‘²ï¡âi,7 ,c[5{@ù>?;k6›s6ƒÅˆˆ1/5všN«V-¯šLÆ$²|Ø0ù«e™y©©ËR&Èr ^Dì¥^´W†C¾gLø¦°PÈ# nµZZ‰7¦Ú¤‘¤V«!€†]©¬±\.—Q™C #Å5þ<ªTö÷÷mË"°B|/5®ï”¹Èíh/&”Nü—Ëí¿¼¼tº_úXùlºv~Þ¬Ÿ}t»¥ry71ü‹|¸h&ÔÂeâ@ä9°j$R©ÝÝ]|Ø—s|èƒÊ7_•B²v4Á,±”Þ‘±Íα” Y  ß‹»×Ç·Äm5ݸ¼¹l6ßœ·YÅl"Ÿ/üp²ëA°–Àš^jêRñêRÄŸø“ä8——šñô7ˆ~xD`ùø^j£A«‡b­y©ÍOúé Ôlc a^êØ}„¢mÔ·ùôžò|‘äÀ9——šÙë–‹°F"ð ð½Ô:~KJÌÞ\âçÁÀW©ûЯûƒOц¬JÍ¼Ôæ!ƒy¶.ÏÏ»¼­Š)1÷Ë{ŽËX¼ÔT©¿AðÃC ËGÀ÷Rëø-°D#ªÖ¹hÓIÉø!a£DÏýARÖö%ú6*µ)!sê.KÔ]ˆ§a9y¥/ˆq fq â¼ÔÚS¥^~,‰À7@ÀÏKP©Uë]LUêäàK2 ª–˜Ú%ýìÔԫ䡃Z5µjr€ "å½Ôß á!略*µÕªÍ^j‰­%ˆQÌNš´ùņ5$r€ È8 zÏ”÷¥+…*õ÷‰x$D`‰ ½Ôbêׇ稜û£x©Å1™4w.Ñ©‘$ϪԪHc¨ÑÒWJä99ÖßK[*• ’Iç÷ödÚŽOޱÊ3ø‡yfgO~:µ?a} —Gë9ùùó'²\ …‡»»%¬ŠE`襖 ¢â¢6s “ /uZvU'“)\Õ)«R«V-‘8óNÐóJä99bñRÏßïb܆ãC™Þ{½—·7|׈¡us´¼÷)Æb|?::©V«ú“QªF&¯^__1Œ"‹éõzí››çNgþ–°$ +G`è¥öTj‰VmÞâÙŒø;•R­š^jæà{ r€ bç@\^j«‡öЭV³R­bÔC¡1,âõõµ7k˜ìOˆ¶;~ˆì=˜8¿ª«¦Ýncr4󫫫а Ñ!0ôR{*5v­Zµï¥öœj;â¤6¥U“¦—šÚ<óQä9#âòRóúë§§g«=ÊÜÞÒææ¯_£oÄÜÝn7ŸÏÿüYüýûwt÷DˆÀ<Œ{©m=©ÕK-ÕW-y©‹š^jA%rï µ1bNä€r b/5ôiLèûÔ­¬~­èÎmç _G:žØ£å®—!5Dh-©š´;©nÚàý@`ýøø|Ϩzž ‡eˆ@tŒ{©M^j¹S™ ÛŒž¨c%:ÎéÚ5›5œ2âOüÉr`{8±—¾gLèÕ­]µªËîÜvሼíG‡cQòˆ—.ŽiÁ·ß!Ô™‚oÅpÔp€ÀW]¬À="Š€›—é<üq^¼¼ÔÉK-Ý“föøREv¦6@í„ú9@ä@ˆËK=îužÖÕ‹EÈÉõòeMú-›ËÁàaÁ2ÖàÏ\.•Ú®_i¢ÀÐàˆ"àæ¥Æ—‡FŸÍø¡‚µŸ—Zß²i¦jYŒüûnêOÛ£?ñ\ó\“äÀ Ä奶úthHQ¯×///¤× °pzzª[͈‘²ï¡âi,« [6EËX JŠ? H°^jœG'Ü|/µU¦ý\ÔôRG ÁÈé W›¹~Ér€˜Âˆ½Ô‹vÏÙlöÖLÈ$](äá·F <­d†JÄ µZ Á7ìÚHeår¹ŒM°Q5æøBŠõÁÁÁ¢íay"@VˆÀÐKí}é;·?~‹ÈÑÿårû///Ï]˜>tòþA6s^;o5kÝn©\–'æ¾ Û2I^k.r€ Ès^ÅÝÝ]|؇øÒv“èƒÊ7_•B²v4\¹ÜN‘±ÍαܚY ›‹ÀÅÝëã[â¶šn\Þàk‡nOžõpJ¹d>_€Jí{©Møswæ™9IDATt:=± FË]/5Bêv»íkWÁ-<3‘¿ºÑ¸h4ŒmˆX/|/uߌ5Ž×÷ˆœ?«*«ØQ©ýµFÍ6ãÁÐKÍ\Ô†'øG‘8ä@ôˆØK ß3&(IêÖî\Õewn»yDÞö£Ã@ßðR_]]M ¾eCG¦þýü¼»K‰z½B)¶†˜ëT¾D4‘ÑIcލZ½[ŽJ­Z5æ¾JM/µy¡NO/59@1p ./u@3žL‹Åûûû‰¼w¼“~ËærÝn×þ‚e¬±6//í7ŽŒcˆX#|/µªÔ¢Oë#ŽK+G½Ô^Äíû¬‡™õ¨Sòr€ Èh9——ÚêÓ¡}y½^¿¼¼|xx0Ö §§§ºUÀKíV…ˆ²ïa%âi,[›Çëëk:™ÜßßÝ5 "5£^jѧ}­ÚØ;/µj×¾J-wz©ŽÈ½ƒÔʼn99@ʈ½Ô‹öÐÙlöÖL™L¦PÈÃoÝjµ´’*5ƒ ›‚oص‘ÊËårY·BløzÑf°< Q 0ꥶ*µ\ïæ1ú¿ÜþÁËKçõ§]£D>“¨Ÿ7kgÝn©\FÞMüf‚m™Dåæ2q Èr€X1ð>®b|Øçæ¾@T¾ùª’µ£¡Yb¹½)"c›c¹5³6"@6‹»×Ç·Äm5}Ѿk6ë_Þ¡ *.í&ò…½ÔÔ¡é'Èr`9——šñôæ=l9X!c^jìKµjz©E¦ÝþHâ@äÀúq ./µæËãDˆA€^jƒ~hæ-!Èr`9‹—š*5)"@& æ¥楶٪™—šº5õ{r€ Öç¥ÖN”*5Ã)"@& 0-/µÞ+îè‰ÌKM=›y¸Ér€XÐKͰ†5B€^js2è™Ö‘}ˆq Èá½ÔkL°)D€Œ{©íhäF‹fü`^jÂ\°ô“ä9°>X/5Fl©T*{™L~oI¦íøä«<ƒ˜gvöä§SûÖ‚”ÁªöPYY«§ uÎÀ†x÷Rû#ºh^jz©©Ý2ç 9@kÊX¼Ôó÷Ö·áøP¦÷^ïåí ß5Ú±ÄÑòÞ§L‹ñýýýèè¤Z­jÍ&ßÖÈd>M ž. oooÓ1ˆŒ-?{X’"@/5µgs —6µ7â@ĸ¼ÔªÏ3µZÍJµzrrb$ª$†E¼¾¾öâf “ý Ñv§ÓÑ¿¼„ίêFzyyÁHЍ†:·åçi Ë"°rè¥Ö;þñû}â@äÀq ./õh0<«›~zzž¦%#,v·Ä67ßxý}£ÀÝݼ.˜... ~¯³ D쥆> ±°z µ«ÕœzîÜvÁðu¤Óé‰=2Zîz©R·Ûm-9kë³ Z­V6 +uæþþËó÷õ,IˆÀÊ —S—Ú ]ŠçËö¹ä-yKD쥆ï4cõ@k­ê²;·=7"ïi¼ÔWWWÓ‚o©ÍÈÔgfúøF@ü¶¶ì• Ü ó 0ÍKÝg^jêjr§ÇÚteÄï1Èõã@\^êq¯ó´Þ¶X,BNžø«èëS¦l.×ívíXÆü !‘´:F[?>>ÎÓ˳  !0ÍKÒ»§“ñ!žÌͨiÚu#êFä9@p ./µÕ§Cûi|Dxyyùðð`ÌX@ò;Ý*à¥v«‚üŒ i=°ñ4––‹ÅÂåÅ…Æâ777ˆ×CÀD€D‡½Ô‚5õ§õÓŸx^€À*¼§¬“×ûwâ@Ä^êEûf¤º»5ÜÏ…B2³5@ÏP©‘™=|îTÖX.—Ë&Œ¾í~¼åóyÔöû÷oDÕ‹¶‡å‰X!a^êÿrû//×?‰¾ó|&Q;?oÖÎ>ºÝR¹Œ¼›XÛ÷Û!›Ë¢åC â Wq äÀÒ9€>iwwöåŒ/ÂÖ_¾ùª’µ£áÊåv¢ˆŒmvŽåÖÌÚˆØ\.î^ß·ÕôEû®Ù¬|y‡‚¹”Mä …á艢™™`¹o™¦ |'ýƒÇB= ȵå@\^jÆÓ›ô°åD`…ÐKmÀe^jÍÚDˆ9@l âòRk¾R¹ŠW¿ŸŸ1˜b§óòôð€AŸUÏ&X’D€½ÔÔ×ùž ÈMä@\^j«‡öÑ­V³R­žœœ‰*‰a¯¯¯½¸YÃdB´Ýétüh;X±fvlµ/129êIïîbÆËv;´,@ˆ@tÐKm°f®æâ%Èr`Ã8——z4žÕ_?==[í9PNóeÙ F››o¼~¾¿¾>íV0Šut±÷Dˆ@(ôR Dô‚ÓOäÀr b/5ôiLˆ…Õ­=¬æÔsç¶ç…¯#NOìˆÑr×Kºí«Î#±¶ª>fU³y ß6„Ÿ¶ì~ÿ3´‹g"@¢C€^j`M/&uzr€ 6‘{©á{ÆÍX=ÐÚU«ºìÎmŽÈÛ~tè×Ñr×K}uu5-ø6ºÌð¥#vT.—÷÷÷ÕG+pOD€„"@/5 â÷þÔéÉr€Ø8Ä奵lÌêf‘šãþþ~šJ=mË쨩¬Ñˆªa¹~yy) ˆªC»x D :òR›gåAJÞ@Éè‰xMêûºèAÜ0"N!5¹MÔäØfò–°ˆËKmõéЮº^¯#5ÇÃÃé<X8==Õ­^j·*¸;0!ûV"žÆ2¾JÄ2¶ýýû·®¬Õj ?yHh3X€(˜ßK­ñ4>ä3y©O3/5sr399@qr b/õ¢½r6›½5S&“A>iø­‘©C+ñFu˜T#ƒ bF »6<ÓX†Óu=ªÂ'Íf3G•zÑóÁòD`¥„y©ÿÃE‹wL¯ˆ¤E”Æm ŸMÖÎÏ›µ³n–.|'1`¼±a }ŠÂ›¸¬@‡uÀo`å90™L¥RxazV­æs¹eqµ÷õ…^óùùé«÷™ÎdÐÿU*Ç;Éô²êŸ³#^ »|Ó5ë€?ÛÀûÀÒ9€q~wwwñaÒ_ØŽ8—o¾*…díh¸r¹Ý("c›c¹5³6"@6‹»×Ç·Äm5}Ѿk6ë_ë˜ä¥l"_(üÐ'éþ×—¤™Kl­OØ`Ç©OÐû¸qÞGg„E‘çWñpØéb«öå¥êÙò R£’ˆzŸ[cF{ÙkK\-|üz´ï‘p,Ðæq\e4ÏþqÙz&¶sEç"zýžZõwÅ</5UjRD€L@ ÌKýÈ\ƒ$îÇý¾jn’ñÃÏõa50SõˆnG}‹˜¬\MÃèì} ¹kšJÏ‚Nd~Å[˜ ¤b_?ƶˆ}wÒi˜¤[—†ÚøBm(ÙмmX™ñëN2)¶f="è³ZM(îõzzí@Þ†3r2sÙlÎnÏþ~n\ƒD…пÁ˼(s«[ãXÊ¥2Ž ÍpËÖ3±äðZq˜÷؉œŸ˜{jF>¨Rÿ;†¬|CæËK˜ãK´5«©o2~ÐK½®BI-0¾D®;)|6àiÆÙ¬4¯côÑQ¢ë$–UÛöˆw±Œ¹‰€'xÁ³™ ÌÈA{vv¹úáîN·Eùr©”+J¥â‡¿­<©zó¢È,·Û­¯¯bwÄôWQÐg´'›Å·•ÁwDÖ/.®ñçgÌ­ncA¨ ¡!5F©°Çeë™ØÎÎ ùÿ]õ㎋^êo”ðˆÀæ"0Ÿ—ï©ZRè•ÚäÔd^j¼ðïÛ{¿Á1|ÆOÏÏ1õ¸LdêGÄÙ6 ÝÙAÌ-ë…íÆŒ@¡ó¬óžLÂ7rÕj‰¹Âl‹ŒWpq˜/#_º//º-®an]yw7‹ üÙ¸A Q©QfZ{&¶a†—ÇZÛ`r€xÇeë™ØÎopÞym~ã{½Ô›{°åDà"î¥6¡ôÀ×êĪÔôRSÃhQKÁÙÊŽoóîääDÛo5c,#ξ¹½…tŠ9¼ÖsŒ?ñ®"4dÞñö\^\<>=!àÆ^`§F< C…n UI¾°¾ûúZ3ã5¨—Úâf—a¤†½ûs0À ÆVSŸÖž‰˜ÌðRc cHç(Ðy}½¸¸°Çeë™ØÎ :¿›ÂC¶s¹ï%ÖßK ?Þ;íe2ù½=|£lÇ'ÇežÁ?Ì3;{òÓ©ý ëÑÊè/>{ÀVn»»;¸¼0é'לˆˆ9¼Ô¾šzG•›5W݈f ÛF&C ‹9–õübÛ\6‹ìykggæ*P{ˆ§m£¤vœ°MCcF·×l4µ „gìîð°Ôh6¶ê¶®.n—áË€!5`<ˆä´öŒ·aâõhuk˜¼ßÞÞ´rèèö¸l=Ûé#—'b¾)üÿ®íœö¾t¥^êù{k<`ÊôÞ뽘 ëæha¬ÏOŒÅøþþ~tt‚[‡þdî #“gK$pׂ“Û=:<*›¤ø2áéáõüÍcI"@–‰@˜—ú?X-Ñ?wºÉTr I«S¹lZÔª½Þd<>®èÀY- 3¸xèÞ¿ î«éÆåÍe«ùÖCÈ,ã¼`¾¿›@Ödü0*] ¯™RðubÙê[ôR«Ö`1áòwÕìÇ=Ê<×ßõ\ó¸6ˆÛqy©­f^<==[í9P8 £C`¶¹ùÆëèÖî¶x?fD`×k¡­b"@V‚@¸—Ú^Üæm5®dñ2/õæxˆéÝüwï&ó¯ÿ;†ä!1\"öRC?ÆE=ÐÚ+kN=wn{kèÐH4±óFË]/5Bj$Å×’ê*s'Ïhæ¯òîHæOì°ýÜ + X) ³˜ÓKH«£Tn vôD³†툧ÖÞ©umÖ%¬õFΣq}0¯‹×דÛ߉ۛ~,{©á÷ÀuI=ÐÚ¹ªºìÎm§‹ÈÛ~t8%x©ñuÇ´à[6•©Ýq¹a§47('"@¢G ÌKýCr}øÆ`uªY•Zµj´yÚ뤦Eä90ƒšuJç&îöGn”¹ôŽ5 Ϩ_$#MÐÄ3Z˜Íå\ÿ–±Æ­ÄÝ6›ËêøS˜°`M K?jVHˆ@óå¥6B†÷ÙáÀªÔôRp©ÓS§'Èr Ä奞óÛDtõz9ï”Ãü,`tUí•gä$AVL0I£âi,ã£C·/w·­VÏÆZ8&,È௜ˆˆ9½Ôƒ/yúW=€^jêFÔÉr€XDì¥^´›Îf³Èp‡ Ù< …<üÖ~~L_YŸTãÑÑ’„ ø†]©¬±\.—§©Ô(Œ ¶L(¦éö9"a^êÿr¹}øíõÏ ÑO&Sˆ¬ ÙÝÚùy³~öÑíâ FÞMäAz=`ãâ2q Èr€X5›jwwö¹nôA囯J!Y;1K,±Edl³s,±ZVEˆÀF#pq÷úø–¸­¦/ÚwÍfýãKâa‰Šû‰R6‘/~¨gk€oŠ¡H|Š*Ñ×5bû —š>òÿ"u»uÐíØòpK8——šñôFÇ=l<Xsx©%~–$ú˜c±ß·£'ÒKmÎ =”1x(Eÿ›™£ƒç…øßžqy©5_'"@ˆÀá^j“¼G¼Ôø‚hŒã}Z=ù;kêC[¢ ˜ïES[â@ÄÄX¼ÔT©H"00/µ?z¢i\¾2ÆÈŠF”ôFUd^jj¥ÔéÉr€ˆ‰ç¥ÖN”*5Ã)"@& ž—Zd8Œ›(©eî¨ÔôRjÒÔhÉr€ˆ…ôR3¬!D`÷R‹&=Hî¤E˜NaüÕ”úó¦iô/~{ÿ"Î>‘<'ÈØ9@/õl  á^jO¥N$S’AÊ´dü0¹>T! —”Z59@ä@,X/5FlÁð+{™L~oI¦íøä«c‰Ë<³³'?ÚŸ°>œ Œ®Áñbìle Œ¯a`Cˆ@<Ìí¥ÞAmüÓIQ&ÄâiÕÔ,Í™cÞæý Èr jÄ⥞¿·Æ¸ LJ2½÷z/ooø®C!êæhyïS&ŒÅøþþ~ttR­V½¸yl*^aÊg³pr»£'æó2‚ÌŒ±ço-K"ðO„{©%|ÖÁÈá¥Q©é¥ôPÆâ¡T¹†ør`›9——ÚjÆ¡½o«Õ¬T«:¢!¢^ sx}}íÅÍ6L6#Úît:~´¬XŸT0!4||ôÞëš÷÷ÀšÐV± +A ÜKmjQL<-(ú*5óR›SB]*j]Š˜ÇîaïÙò0vÄå¥ †guÍOOÏV{” èÊÒææ¯ß©½:Æ5iªÔ+‰X)Xp/µQ©¿¼¥n;z"½ÔµÒ˜òÑòý¹G{©¡OcBß§hím5§ž;·½0|étzb§Œ–»^j„Ôív[KZMÚnè%iôÿvUj]7¾f¡H€…‰Xá^jU©ÍëÅÐ:’"½ÔÔ§™‹— ÈX9±—¾gLЌխ}°ªËîÜö͈¼íG‡;ॾººš|›ydkªÔKˆ~XX:á^j£Â~}âÞÑÿÈDŸ†§iÔ¨‘ä9@DÀ¸¼ÔÍxF¿\,ïïï'˜¡+gs¹n·k·Â2Ö¸•P¥^z,Ä ‰ÀõRÛ^}ó¨Üïã;E,0/uì>Bœ¶~Vr€ØZÄ奶úth\¯×‘óîááAu(,œžžêV3ÜÏÈ ‚ Ù÷P ñ4–†»/ªÔ¡È³ˆP/µqN'ûŸ_úò¾ {*5½Ôæ!ƒy'Ì+Iâ@O99@DΈ½Ô‹vÒÙlöÖL™L¦Plw­VK+™¡R#1H­VCð »6RYc¹\.S¥^|–'Q#ê¥N‰$mrè XîK Žÿ0/µ¨ Ô‰Um!Ä bá@Ä^jí¡õKÄ9§ýý}x?z½ÞÛÛ»k˜†{F pfÿïÿƒ޹ÍbËo;»¶9›ÊbD€ü¡^jÄЈ™Œ2ôÆM4«è¥¦.WRȹîÅã"æäÀFp ./õxŒûOÝ07&Dà{ ê¥V•zŸc$=ç.†O”([úc(R§ÜZ/c,ºùF¾‘ä€Ñz¦¾/]ižæ…Têï*ð(ˆG`/µÄÏø41åÏE±N99õ¨#r Er€ È88‹—š*uxlÁD` ˜ÃK-šôNja´Î݌ԺL½„š9@ä@ôX/õÆG̉ù–s€^êõŒ+Ø*"°¥„z©!l%ÀŒIîçý˜¡ P¥fOä9 ÖßK[*•Ê^&“ßÛC’i;>9Æ*Ïàæ™=ùéÔþ„õˆet ŽcÇ`+[ßJ#ï5*A*ëÁàkK£6ˆP/µ7V¢—›ÚÄÕF·vÕÔŠ¶\+Ér€ ¢ç@,^êù;î?þÊôÞë½¼½á»F …¨›£å=d“þüDòé÷÷÷££“jµêÅÍc;0V™òÙ,bh7ŸÉÝÝBv“÷ú #ÂT*^%ó7’%‰X¡^j+ñK.h<û©™—ÚȘQ‹"ä9@ĸ¼ÔV3í†[­f¥Z=99ÑÃ"^__{q³ “Í߈¶;Žm+Vw&„æîÈ‹¨ÕÚúm%¡mc"@–Œ@¸—ZsJ‰Lz27VêóRG¯ÇP#æä9@XÄå¥ †guÊOOÏV{” dÎ~zz²¹ùÆë÷Ej¯ŽiY·»Ýn>Ÿ_r”ÀꈘP/µŒ˜˜H|ö?¡ÇêÜjժȎ¨×2‡9@ä@äˆØK }ú>õ@k?«#¿¸sÛÿÂבN§'vÇh¹ë¥FHÝn·µ¤Õ¤í†FÉN®Jí®‡«¤ÙlÎÙû³ KF ÔK­£'âbk3Ú ¾VL"°¦—šZõBr€ âå@Ä^jøž1¡GT´öǪ.»sÛO#ò¶:úêêjZð-ŽÊÔã*5‚l|¤xvvvpp°ä(Õ"0'¡^ê¾\É& µDÕ_ri›UÌKMmžþQr€ bä@\^ê€f<£·-‹÷÷÷ LSšQ8›ËÁÂa·Â2Ö¸•¶ER|Ùh4ðyâœ]?‹"°|B½ÔªR›¼’ëCff½Ôñj3ÔÆˆ?9@l9âòR[}:´K®×ëÈy÷ðð :çN·šæ‡ÆOðo`B ŒeÄÓXF¸ìîËÝ†äæ»½¿ßßßm "°BæðR˵oãi1ÿKÉÝ^j91‘{cÔ„x¼@€ø“óäÀúp b/õ¢q6›½5òF y„¿­VK+™¡R#ƒG­VCð »6Âe,ägw[ø=^:ìH}Þã9­m3Ë"ð—„y©ÿËæö_^^ž:x\F$-.êƒ|æ¼vÞjÖ>ºÝR¹Œ¼›‰~_²˜ £–s™8ä9@¬š‰Tjwwöå_ú òÍW¥¬˜%þ²œ´"c›c‰Õ²*"@6‹»×Ç·Äm5}Ѿk6ë_KTÜO”²‰|¡ðÒÞ§Æê÷H¥¬J-Š55Z~ãOä9âòR3žÞ踇'«B ÔKí•(iƒ`\D´­0ÌðÍ¢uƒI´M_ã–ûÉs•òZÐgpâ@Vθ¼Ôš/ D`P/µ¯RËØ.¦£4_(½ÔHÚ ÷ èg%÷Èr /5UjRD€L@ ÌKýÃæŸ¶z€§[›[¥>MMŽ Èr zDœ—Z;QªÔ §ˆ˜€@h^jqNûži;V¢§OÓKM­”:=9@1q€^j†5D€¬á^jçEôh}Ã(sQ¶™—šÚ¼aßQ;q075â@ÿt¤ —z‚ 6…P/5Æ!7´ Á:øÄÀ‰X¡kè¥öŸ1¨QŤQyÏxÄŸø“ÛÊõ÷RcÄ–J¥²—Éä÷ödÚŽO®9¤ežÙÙ“ŸNíOã¹¥QFÃ/ÆŽÁV6zAÁÃÃCä½F%He=müsF;D€¬P/µçCn½þ§&–‘ûÞ׊ôRGï¤HÌÉr€PÄ⥞¿cƸ *Ó{¯÷òö†ï1¢nŽ–÷>eÂXŒïïïGG'ÕjÕ‹›Çv /‰1å³Y8¹ÝÑ1"ãÙÙYõ¿¼ ?·­dþF²$ ËA ÔK­*uðe‚j‘ªªÍè‰ÌK-È0-sžä9âòR[Í8´nµš•jõääDch ‹x}}mõfwsDÛNǶƒ«£BóÇÇGwôDT¨c+¦ÓiŒ³h+ m "°dB½Ôf€I@­.j„ÓI3†"óRS'£NFä@ŒˆËKm5ãÐþøééyšlì*ͨþ ››o¼~_¤övØÖ6£Ýn3Á_èIa"°*æöR˽Ëû6ÑW©™—ZÎJÚ ÷Ë÷ä9@˜ðä÷¥®Ž»Äîú4&ô}êÖš5§ž;·{„¯âñÄ …®—!5b-i5i»¡ a§ñ£ƒ—j°•,ñ¨Y s!0楆§£‘Ë(‰ra'X¿þL%åÖ`UjS Òï»MøM=1'Èr€ˆÁK ß3&(LêÖŽR…awn{_DÞÓ¾ x©¯®®¦ßRÛ¨L=®RÃKÝívµ½Ôs…>,DVÀ˜—Z¢VU›kð’´}¯ßp 9õpqÓK-à—9=”Ä È88——: Ïèš‹ÅâýýýÄ3tôl.‡øØn…e¬q+™¸-"r|áøê²W0°N"@f!0ÅK-1³læ«Ô;©¬ÒùpŒdΉ˜â¥öZ2üÀe*J€\ÿ)ã§–`yô˜—šú4uzr€ âå@Ä^êE;él6{k& …øûææfÑF²< ËA ÌKý_.·l—ÏÝ/O#œìgwñÐܪŸ}t»¥ry7áAŠ=Lêj.r€ ÈÕr.ÅÝÝ]|؇|̶GDT¾ùª’µ£³ÄrºLS "c&ÖX"ž¬Š|.î^ß·ÕôEû®Ù¬|I<,Qq?QÊ&ò…ÂqNëäÞ’÷ClÌK P£ŠW£"þÄŸØZÄå¥f<ý= X2¡^jÄÒ<ûQµæ¨V›YƒN´pî×|?Jb̉KüÍ͈<ä=06Äå¥Vï2'"@ˆÀóz©Å÷1øB-ÿõTjO«F}q|ë-‡Áý2Ï9@[ÌX¼ÔT©H"00/õÕ¤MøÚ7ÞI¯‡ÈÚæý VG­Ž Èr L{_:#ŸÆ¿‡T©ÿCÖ@¾!s䥖ˆiíKöe®ßÓKMœVr€ bä½Ôß0(á!ÍE ÔK›œØCúþÛUdЩš^jú¹é£%Èr >ÐK½¹±[N¾!¡^j¤‡Ê4–1 ¢=‘^j5Ä`£NÃ6r€ØZ¬¿—#¶T*•½L&¿·‡$Óv|rŒUžÁ?Ì3;{òÓ©ý ëÑÊè/²Pc«ñp›cß0Lá!MA ÔK­9%Ì7íÆ?µQ©e½™3ïóä9@DÏX¼Ôó÷ï·áøP¦÷^ïåí ß5bTÝ-ïÁRùù‰±ßßߎ0šxÕ‹›Çv`¤™òÙ,œÜâggÕÏáó·‘%‰Xsx©åÚ7—³—HϪÔÌK P¨OS§'Èr Ä奶šqh?Üj5+ÕêÉɉÆÐéðúúÚ‹›m˜lþF´Ýétüh;X±fjÄ„Ðüññq|äE EžÏfŒÈÚT Dà_˜ÓK½“LËXä”÷c¯¦—:z=†1'ÈrÀr ./õh0<«~zz¶Ús \@iÆ67ßxý¾HíÕØãCº®×ë+Ísò¯Ñ·'ß9¼Ô?˘Š&’FXmrScœz©Í]Ž^ê-ΉKm’ü'âå@Ä^jèӘЪZ#Í©çÎmä_G:žH å®—!5”f-i5i»¡÷¬ÿ·«FcmÝ–*õ·Ùx€kÀœ^êäéTra5yÛF/5µ"ê…ä9@Äˈ½Ôð=c‚š¢híÝU]vç¶×Gäm?: „/õÕÕÕ´àÛh7#[»jt£q^«íîîJ,½×:ú`ãˆÀ·C ÔK-ѳ¹œM m Õ¢l‹JM/5è@*^Šør`k9——zþÀµX,ÞßßOS©§Ù\^û+–±Æ-ìªÑ77·ð–hþQ¾}íüÛE+< "°ö„z©%z¶Ï¾šñÃÏõ!A5s²Æ—“äŠW¢>GüÉr FÄ奶úth3rÞ=<<¨+ §§§ºÕ EYA0!ûŠ!žÆr£Ñp÷ån«z¹Ì??Eùöµóж± KF ÔK-cæ¥VE„^jjc[«™÷°ÌGÎo Èø9±—zÑ8›ÍÞš)“É yø­[­–V2Ã÷ŒÄ µZ Á7$gø¤±\.—§©Ôó¬_´Ù,OˆÀß ê¥ö3èáúï«õÃ$Ó£—ÚAب-ÄAU'â@Ȉ9±—Z{YýqÎiÞ^¯÷öö+ϨÎìÿýïœ1·™@lùiÛήsγ ‰@¸—ZÕ¸þ'\ߘËÀäžJM/µ Ã|ÔkÉr€ˆƒqy©Çcܿ쀹 ß P/µÿY¢„ÒÈúÑïÃ"jóRÓCI]– È9——z!•ú; <"@f!ê¥F$í__0{|Ê,!£'ÒKM}Ú€:½\]Ä-9bñRS¥f\EˆÀB½Ô2À‹g †Z"ꄈÕü¨”:MŒ: ñ7´¦Y ¼mÖßKÍȃmA ÔK-’´ä¢–ÿ˜TÔâ¦V•š^jj´ÔhÉr€ˆ‹ôRoK¤Âã$@¨—ZUj?µìÅŒLN/5õ°mÓÃx¼ä<9°V —z# 6’l sx©µ‘Ï`ôaõ`^jæú ‡˜ Ș9°þ^jŒØR©Tö2™üÞ’LÛñÉu¼C™gvöä§SûÖB;&"ŽcÇ`+[À­G—·%|áquC`/µÑ¤Ó˜'w’i‰©}•ZµjÌ×J·`{ Çx^è-&ÈïÏX¼Ôó÷ãþü9>”é½×{y{Ãw Q7—‘eÐCûðýýýèèãŠëOâ­Ìç¯2å³YäqGOtëÑ1çoK"°LƽÔýD*…\y*M~˜lO#n6ò4Æ'W_5½ÔÌwanóÌuS®æD'÷¶œqy©­fÚ·ZÍJµzrr¢14†E¼¾¾öâf&›¿mw:?ÚV¬O‡˜š?>>º#/Î…1´y,@ˆÀ2÷R›xZœ²›ä“—:±ƒ0ÿÃÜW¦é¥¦6O-œ È9——z4žÕ#?==[í9PÎUšñÓÓÓ“ÍÍ7^@·v·ÅPçù<|%ð¼ððð°Ìø€u"°Ó¼Ôˆª1  R›ØzÇ bjÛŽ‹Z-‡Ù–ë%Ä Èr bD쥆> }Ÿz µ·Õ‘_ܹí…áÄH§Ó;e´ÜõR#¤n·ÛZÒjÒvC#m 'W™~“ âu5ÜÞÞþþý{¡€…‰XÓ¼Ô"G‹ ýCÇJôãiDÕæ ÅrT5½ÔÞ­îû{cÔ¢„Ä™þxr€ã@Ä^jøž1ACR´öĪ.»sÛC#ò¶ºí€—úêêjZð-ŽÊÔ…[kÞÝÝEHÝh4–°""@B ÔK­OÃýÏOˆÐ:·£'2/µ C-ßQä9âòR4ã}n±X¼¿¿ŸX`†:›Ëu»]»–±Æ­dÚ¶X?1Ú^(*`a"@þù¼ÔÆû¡#“cîž((R7¢nDä9âòR[}:´ß­×ëÈy§gôžX8==Õ­fÄ¾È ‚ Ù÷P ñ4–Ú³»-*TÏ äp”l6©R‡ž «A ÔKÝ7‘ô×à ÷y…»‚?z"½ÔrNâÐf¸_¾ ÈrÀSy¼ñ}GÞ®IŒl6 3&óù`±o«ÕÒÎ|F ‘¤V«!V†]©¬±\.—ÝÀÝ…µ£þãã2–÷÷V,°V"@ÂóRÿ‡÷M/—›'/¹ê+äñÈ|Õ¬t»¥ry7ñÙ¢~ΈÉHØ\&ä9@«å²PÁ@ŒÏòrŽ/}Pùæ«RHÖŽFÌaá¿#2¶Ù9ØŒE‰øÖ\ܽ>¾%n«é‹ö]³Qÿ@f?/u)›È ?T¥†2­ÿ7ÏÖ33/5õiúÈÉr€ˆ‘qy©O븈Gþ×Kô2Rä¥Ö±]lDÕɤ—š^jƒ:s}(;ˆq ÈH9——Z½Ëœˆ #Œy©aÛ]l^ê>bh3V¢Ü)Í*õ m€šMŒšœZz»™{ ¶†ç¥Öî“*5)"@& 0î¥F8m†u1ÂcòG*%ñ³O{¹¨S3´æ¦^Kä9 "ÎK­(Uj†SD€L@ÀÍKôx&’V•Ú¼Áü€JmLNû¡J-º5óRjÒÔÅÉr€ˆ…ôR3¬!D`÷R«Jí Ö•Ú,&1zz?Dº¦—:R¿ <åÄ‘ó•ûU¾â@¬'è¥^£`‚M!D`ÔK­~ÕªU•FÆ`~Z/µDØ)ÉøÁ¼ÔŠfÔ¨ˆ9@Ñs`ý½Ô±¥R©ìe2ù½=$™¶ã“c¬ò þažÙÙ“ŸNíOXNPÆï”ÈB­Ü¿ÿþùó§I}]Ðae8"£^jý*ÑK2m€)Õ¤%vìëˆM˜!Ìö—©áñr€ Èx8‹—zþ®ã6ÊôÞë½¼½á»F p¨›£å½O™0ãûûûÑÑIµZõâæ±éF¦|6 '·;z"†WÄX0WWW½^ïææ†>ïùÏK%#0ÉK­Zµ Ÿ?$|¶ã< øILÔF¥¦—Zž?˜[€:=9@qp ./µÕŒCûãV«Y©VONN4†Æè†×××^ÜlÃdó7¢íNÇRÍ3ú9µë&„æîè‰Ífºµv³¿¿Ø:´U,@ˆÀJ˜ä¥V­Ú<'¨@½c.h$ÿÀÇŠrµ•šy©Í)‰G›á~é/'È-ç@\^êÑ`xV×üôôlµç@9WiÆOÒææ¯ß©½:Üm____:X>ŠÅ" + X) ó ê¥6¡u‘´¤Á®¬¡—šú´›#®XVK6Q/õðû=ªFO‡¹s™ØZ´jýzQr…øn0jÕôõ’ä9@DÎû¾Qu>Ÿ—ïöL¨RovhÂÖMD à¥vTjõRÿð4é©“F«öUjÕª1§^BÍŒ Èr z¸¹§ú’3ús8~ÂʺdªÔ+ƒ–MFÀÉK-c›CñR{ÈðR›¸ÿOcnrç%†^êõùæùx.Èr` 90⥠uÆÅ*õ&&l;ØL/5,hž‹Zÿc¢ë&†N ׇ„Ú©|Á®Ê4óRG¯ÇP#æä9@X¸^jôI…BÁtOôRof8ÂVMGÀõR›;‘¹GùG/54i³6•„÷C& œhÕôRÓ7¹or u8¹æˆ3¯5r` Ü÷¥f°ðÏuóRC;¯T*{™L~oI¦íøäH<ƒ˜gvöä§SûÖB ”Ñ58^Œƒ­l·­mÓöŸl*cy©5ª6—®z©Å?­~ß"!÷ÀjÕeûiö¸l£¿|è "7Èr€XìûR›ñ#/õüý=Æm8>”é½×{y{Cö= …¨›£åú €±ßßߎNªÕªí|»ðÆŒH$òÙ,œÜîè‰Rƒ_??<<ž¿y,IˆÀ2x©M(4ê¥ÖüÓ¾‚­É’„y©©Q;$Èr >¸^jÉø‘Íb—z~%¸ÕjVªÕ““¡1,âõõµ7Û0Ùüh»ÓéøÑv°—·¯Žš?>>NsŠ_\\Ôëµe†¬‹ùx©@í\èÆK¦~¡è=ÓKMšï%Èr€ˆ›#^jÓí‰ñcõ^êÑ`xVûôôlµç@9WiÆOÒŽ 3^ÿð'SK`[­ê5Æ?ÏfW5fäüqK-EÀñR#Ý´ Ÿ^j_¥6±ö0䦗š¾^z|Ér€ˆ—ÓÞ—®(ãôiLÐŒ]ײæÔsç6ž@ˆŸ–Ò&Lh¡ë¥FHÝn·µœÕ¤ífÃ/œÌª‰Gu­F‰zKc9öZ àx©‘nÚ“õ¢õ¼ÔæëiïO5†ÐKM¯0ýâä9@ÄÏi¹§&ê¸ÿÞéÂ÷Œ £z µBU—ݹÝ"oûÑa`ï/õÕÕÕ´àÛëíÇî÷ïߨ<—£Dýï'™5¿E è¥Ö¯7Nn–Èø¡#QiT=T©é¥f ¬9|kÁåxµ:âOü·®—ZÅ[Ì"ðR4ãÝo±XÄ'ƒ ÌÐѳ¹\·Ûµ[akÜJÆ·m6›FãonGˆÀ2óRÛ{”©Ýz©]w½ÔôPÆí¡7W—C€ùˆ-9°x©Ý6¯H¥Ö^×êÓ¡p½^‡ãááÁü,œžžêV3Zˆ¬ ˜}ÅOc9.¶¥Dz"X€DÀd/µhÕ2I^jã¥ÖÌzôRo¡¤<ÀŒÇNÈr`Ý8±—zÑ^_ Þš)“É yø­[­–ß½¾9ÖÄ pE#ø†u©¬±\.—Ý]TjÔisó-ÚB–'D`iLöRËè.ê¥þ/·ðòÒyîÚ {°ŸM7çÍÚÙG·[*—‘w3ÑïãÛF0#—‰9@äÀª9H¥vwwñaŸë!FT¾ùª’µ£U‹ÛìKëŒY ŽÀÅÝëã[â¶š¾hßÕõ <幦¥\û½ÔªR›O¥ óRS¯Z7½Ší!'É­â@\^jÆÓù°ùD`5½ÔfôqœeóR#œ¶cS‰%„žZúªÉr€ âà@\^j͗lj"0‚@ÐK õÙ}\ŠMôR÷5ˆä qWSÚ*}HùÏ;q ȸ8‹—š*5)"@& 0楖ÑÇí‰IU©ýÉ=:¶¬×ùF|Îvê{â@ÈràÛp â¼ÔÚR¥f8EˆÀÆòR›8yR^jO™¦—šº,õir€ Ö€ôR3¬!D`˜â¥–ÊK}z©©ÁSÛ&Èr`-9@/õl  S¼ÔÞí“^j—Gû¥?• ÈÙX/5Fl©T*{™L~oI¦íøä«<ƒ˜gvöä§SûÖ‚”Ñ58^Œƒ­lä±=­T÷•`GÓÆ?g´CˆÀʘì¥楦—Z´zü£_œ8ä9°nˆÅK=ÇŒx÷øP¦÷^ïåí ß5Ú1YÐòÞ§L‹ñýýýèè¤Z­zqóØ̧à2å³Y8¹ÝoœÎÎÎr…<÷··7$¾µ•ÌßH–$D`9LöRKÞ3‘y©™×‚:=9@ëȸ¼ÔV3í†[­f¥Z=99m&™Ä°ˆ×××Vov7G´Ýétt“À+b³ 4||tGOÄVµZ•cÂ8‹¯~%¡mc"@–ŒÀ4/µÜªäÊ6*µ¸C¼üÌK­w<ü£^EÈr€ˆ‘qy©­fÚ?==O“]¥õ` H››o¼~_¤ö#l'è†~ss# Ø`pqqQ:,…¶ŠˆX ôR ¬Ì¯l ˆ}«ä9°YˆØK }4õ@k¯¬9õܹí­áëH§Ó;o´ÜõR#¤n·ÛZr˜¹Öß2 [»*u«Õ†hO6›¹¿¿Ç_+‰X) ¡ÐKM½ÙÞé·Å¨·‘‡ä!9ðˆØK ß3&(êÖVÕewn{^DºÓ¾ x©¯®®¦ßFïéÌ]…ælá½^ïã£_5¦Ð~ŸˆX ôRVêR›¥Kñ|ñ|‘ä8——zÜë<­{.‹PŽ'þê*ÍÙ\®ÛíÚ•XÆ·Œ»-¬Õ£ÕKØú7K_I¬ÄJ‰ÀÐKýºµLêÙä9@Äθ¼ÔVŸícëõ:rÞ=<<ˆÐ<`áôôT· x©Ýª 6œê¥6¦z©õ-œÌc×iØž r€ØZD쥆ïºAõ@k¯¬ê²;·½5"oûÑa  x©¯®®¦ß&ˆÙÚU©±ßãc¶KÅbñà`?ðåâJâVJˆÀD&{©Õô!—1½ÔÌgBm˜ Èuä@\^ê€f<#º@˜{?¥ó ¸9†¥²¹\·;ôo`kÜJ\•ëá÷øßÿ:oooù|{d´Cˆ@<LñRûßBÐKMZˆI=žï%Èr`í8——ÚêÓ¡Ý6\Èy§¹ícÞ Ý*à¥v«BVLȾ‡•ˆ§± k‡[À݉ùôCK6µ³³³ÐV± +A`Š—Ú{zÏKM/µœæáÖ×Ä8ä@¬ˆØK½h7œÍfoÍ”Éd …<üÖÈΡ•”f·f$©Õj¾a×FÄŒår¹ìp·…ñ‘4êGIت÷÷÷m$Ë"°¼Ôÿåö^^:ÏòJ¢(\ÉûÙt£qÞ¬}t»¥ry7Î)ù†Ì›¸¬@â@äÀÒ9H¥9öå_p.ß|U ÉÚшYb9]¦©‘±ÍαÄjY ÀÅÝëã[â¶š¾hßÕõO“ñÃDÏ¥l¢P(ÐKM-v=”*ò`ÆüÄØZÄå¥f<½ÑqOV…½ÔÙµóжí}öÑÃJäÀÄå¥Ö|yœˆ #ÐKM½“Z/9@ÊX¼ÔT©H"00/µ=Ñ×qý‘Êåm›®×9õTêÜä9@Ñs â¼ÔÚ‰R¥f8EˆÀ˜—zCµúG·Ö?J=•×,9  —ša  k„€ë¥†bm| x“ƼÔônòý9@kÍz©×(˜`Sˆp½ÔXˆ¸9¦ä¥6ƒ‘÷M¦»½ì˜Ì½ÀÜ´ä9@‘s`ý½Ô±¥R©ìe2ù½=¤Ž¶ã“cäð þažÙÙ“ŸNíOXNPF 'y…÷ö…zXþîîù¹0ÝÜÜ0ª!D 6‚^jU©u0r  }/µ:§5q™,ÓK­xÐGNÈr€ˆ±x©çï°1n‚`Lï½ÞËÛ¾kÄPˆº9ZÞû” c1¾¿¿T«UýÉÓ´œÝ˜G•ÄÝÍMõì ¿¿÷ŠÅ’”ˆ1” ¢mLHÑðzþæ±$ ËD è¥Ö@Ú\Ñ&¬öóRë­ß#š°zš6@/=¾ä9@p ./µjÆóL­V³R­žœœhï‰a¯¯¯½¸ÙïUõODÛNÇïgƒu«éúöÃ(jVÔ©_I¶Ûíf³™6Æ-Gx=OÃX†å#ðR{Jô˜—ÚÏû!Ný¦{š6@í6úoÞ‰91'È-ä@\^êÑ`xV¿üôôlµç@9Û«êzÌ67ßxýãºu·Û-äóØÆ;x$F#Çúå ¬‘yx©=»Ç˜—Úx¦½Hš^jêOèOÂÞÈ}™<.bNl"öRCŸÆ„¾P=ÐÚɪZìÎmç _Äã‰}1Zîz©RCoÖ’ªI»“—ÌÖ_…ma i4›X]¸Ñy¿ßŸ§ëg"@–ÀD/µgúp¼ÔæŠ5Ÿ/úsæ¥ÞBMü£ÈóNëȽÔð=c‚¤hí’U]vç¶«Fäm?:‹’G¼ÔWWWÓ‚oÙБ©Q!”o©ð âr/O€ÙA*•Z~ À‰˜‰^j½|]/µ“Ùƒ^jɇjîq2ç2q Èr Äå¥hÆ3ºÚb±x?±€ ds9׿e¬Ñ2XFþHÔ0Uëšl.ûúúªËX°&y–!D`™LöRÛ÷NšñCäiïMÔ A/µ÷¸±>: 53ž r€ØBÄ奶úthg\¯×///‘”È0,œžžêV/µ["fL0Ik -FË0‡`™òà™¶å!XãóDHט°€€;´U,@ˆÀJ˜ì¥ö_2 4ã‡Ó¾—:‘T=†y©©KÅ¢KÉeÀ÷|OBþ ~5òÎp†¼’®tJ¥Ùl)80e2™B!¿u«ÕÒ²3ZˆÄ µZ Á7ìÚHeeÕ¤«Õ jØßÏÙœÖX‰Â˜`;Á„bš]„ 1 æ¥þ/·ðòÒé|˜ëßdÐËe’Æy³vöÑíâ FÞÍ„óAl\öã.ë%Ä È¥sÆáÝÝ]h·®Û8—o¾*…díÈ3K,½gE\k³s,½rVHˆÀ†"pq÷úø–¸­¦/ÚwõFýãÓä¥6^êR6Á˜¼¼Ôî(‰ÌKM}š:19@±s ./5ãé xØl"°Zè¥6øŠSœ^Lâ@äÀq ./µæËãDˆA€^jƒ¾Lú2Ér€Ø@Dœ—Z»OªÔ ¤ˆ˜€@˜—Údüð¾MּԪ阗zƒ´jð|Aß’ç¥ÖN”*5Ã)"@& °x^jÕt“Ó´ú ©y“ä9@DÀz©Ö"°FÌå¥v2h2/5µ®o©uñ=ÄäÀÆq€^ê5 &Ø"@æòR{4„y©=—蹌@3“û ¹Anr`ý½Ô±ïìe2ù½=$™¶ã“ÛÜÒH>½'?ÚŸ0Æx 8A5œ¢ðÞ^Æ-0 Œ–áDˆ@l,ä¥öG{¢—ÚzÊ™'ÄF„ Sµ-€¤"ø²Ñh¸+ä"5ôRÄ·Wãá±ó9@l(âòR[}:´Ã®×ë———FT`yît«€—Ú­ 3&ÊC‹Ç£ÑÀ2Ì!X¸¹¹Ùßß·åa8An¾Ûû{wehÃX€å#@/µ`Jýx=”|·@Þ’ä@Ä^êEûàl6{k&ä.ò[­–V2C¥FZ­†à6k„ËXVù¹Z­ †ýýœÍi•ð{¼t:Ø‘ú¼ÇsZ/Úf–'Dà/óRÿ—Û?xÁõú!׿q­ ™d­qÞ¬}t»¥ry7ý~ßßJ|ÞÄe‚8r€ –ÎD*µ»» í6çû"ô~[¾ùª’µ#Ï,ñ—½ãôÍ×ÚìK¯œ"°¡\ܽ>¾%n«é‹ö]½Qÿø49§Í§¥l¢P(üÐ'i£˜\Ôžó˜y©©mÓƒNä@œˆËKÍxzC#6›¬z© ¾ôR3g9@Ƹ¼Ôš/ D`z©z©é¥&Èr`9‹—š*5)"@& æ¥þ¡_%«‹Z—SÆb×Ë/¥5ž ÓxxîøŽ‚ 6šç¥ÖN”*5Ã)"@& À¼Ô…y¨Ó“ä9°q —ša  k„½ÔÔ¨6Z£¢žïˆÈ­å½ÔkL°)D€ÐK- ‡r=”ÔÉ[r€X/5FlÁx‡{™L~oI¦íøä6·4òIïÉO§ö§ñÜÒ(£†Œ’ˆÂ{{[9‘÷k‘ÊzÚøçŒvˆX9ôRS£¥NOäÀ†r /õü3Æm@Œé½×{y{Ãw Q7GË{Ÿ2a,Æ÷÷÷££“jµª?T®ƒ—ßÝÜTÏÎÞÞÞÞß{Åbéìì +1"#z¨ÿåù¹m%ó7’%‰XôRGj]ÔºÈr€Ø8Äå¥VÍxž©ÕjVªÕ““¡1,âõõµ7k˜ìOˆ¶;ŽmëÖ/߯oo1Œ¢æ@ú•$*Ô±Óé4ÆY´•ÌÓ<–!D`™,â¥v3~h™›Ö0×q Èr€ˆ”qy©GƒáY=òÓÓó4ÙØï=½Íáß°¹ùÆë×­»Ýn!Ÿì»Ýn3Áß2#$ÖEB`/µŽ¡èkº’SÏ_³qÚ†@Dÿ4ýÓä9@l8"öRCŸÆ„¾OœÐ¾V­j±;·½0|'vÊh¹©Lê !5bO¥ÛÀ¯üõØ’F³i ÂK 5ØJŠX˜% 0¯—Ú¸»Ta^ê­ý¾^Î>ßKð 9@¬ "öRÃ÷Œ ½¡z µVaØÛ¾ó´ï^ê«««iÁ·Ñ€†Ý=*„ò ÿôÁÁ] /5tëF­F/õ#VAþ¼ÔªI}Ñudät+®nMÝ—^Lr€ ÈÈ8——: Ïè|‹Åâýýý4•zÚ†Ù\ñ±ýËX£bùC Q«Ú‘ã ÇWßýw!·"Dàï÷R›çcѧÍRü3:¥øªé¥6¸GêÄþ¨ÿæÅR©P(à3ùŸ?‹çƇôXK;w__Ú77ÇÇǨó»Û[ó´¹´úç<ï8Æ'Ñž9·%¯¢?_Äü1ËKmõéЮ·^¯_^^")‡éBX@ž;Ý*à¥v«BÄŒ Ù÷4†G£e˜C°pss³¿¿oË£Bõœ ~¸>¼›@hÓX€e#0·—úS®WhÚžJ-ÑK-gƒ^Ì ñb‚±H2Õy~~¼‚tô =–Ñ–¢)¢ËC$ 9 õ£ÃÛÍfÅÑ97ô­‘»_¾GZÊù]OXÏÒï™{©í³Ùì­™`t.òˆ}[­–Vâ]­“jDbäî@¬ ›5RYcY5éjµ‚ö÷s6§5V¢0¢v©?ŸGüûÏ¢dy"@–ƒÀœ^jĉ~_bhy¸6ºµyõ¥sj-ÿ¨µCÃæÕrÉr5™N£ÂËÓ›Û[Ý/ÔëçN]–áSD´-1÷¯s,+·¡õ¢'CrY3’‚\Î÷z¶~¼~…«½ –A”‹š–J˜kÄ‹õØãÃÃÓÏŸ?Â$%___­ÎýÜÙiV{žžü·CÜÆ¯G›“Ç¥ Çu8.¿ž‰íŒà¼¬ú¼³þï}ŽØK­ý®ªÂsNP”ñ° ¯3ÒI»†i¸±gÔ€{Èÿþ÷?Èá˜Û$êäöæ&§5j@´ýøø(õ¿Ký3Äï9ÌbD€ü%s{©¿ä™ºÿ™QÏ8@襎\ƒ¤Þö/ºo@¯-—J¿ÿ–ËÆ¼oÁˆ è° u] Äf̱¬˜c[Ä»÷÷èÛTd œ‹Z­Ž¯‚îî>þüÑ:mHØêñù¹rtÔn_êzÛ½ÇÇ{l%+Myy¹Ûh ·F¬üül2ÔNoBäÖ•‘»FóðÚfuë›vñt§ó\>:‚ŽeËÖ3±úÿnËëwéˆËKÍDupp3"ð½˜ÇKíy< ûîTó —ú{k?ßOÛ äP‡–Üïãå‹hƈgõÛy,#˜®TŽÁoÌ€*رo2™† „ØÚÜF4u µ?z=k£÷…”ÁÈ ø5ìKD«C9Èwçµ*<:*|ô”KhÒK§3øúBcš&=ÖŒöìKƒƒº>ö¢~q™ï¸Õ­!{ã)›”ŠE«‹K«üz&¶SÛ0~¼\OLÖqy©R©¿wÁ£#D`ˆ@¸—Ú(j®sÚdü —z9Ü€¦Hké:–ÕY>ã¯ÁW*•RüÁðl&£ËŸg{AäççðŒä· 0ÇkW[§{¾²Ù ÌxËäVø е Ê—ËÝ"–í}xÛbþ¶²3]n·Z_ƒÆZ;­T^_EAŸÑx4DZ²~qqwž1·Ú<ŽAvÑDÜò¾Ø¿Šm=ÛI~N<×¼NWw.Šm,^jªÔŒ¢ˆ˜€@¸—ZRå©®•Ú[–¿é¥^±÷÷ûéÄñêŸ1F5óûEÑŒmÛ$ÎöÃÍüaßÉ_5l˜a†>·ÝwŠuûæJ·Ey¸8ºˆqÍÿu[ã©ö4f»ŒèA9l‘È„Õhè Éií™Ø†^j ¢l4ÀDÛ{\¶ž‰íÔ6Ì8ÞxÏ)ÛFü×ßKÍȃmA ÜKí§ÐÓ¨ÚHjôR›(ˆ^ê Éõa}ê¹_‘ñ ~õäDÏ££gËg‹ƒ @ÖµÛbåç`ðtGò¸v{yyñüô„€e¾þü1Y® º-Ê?=<`}÷õU3a©.nùc—kìÝh>ç×_1ŸÖžñ6˜âC÷ð›­§\¾C&/c ¿¸h0™ÖNò|"μöÕ’WTž^êm‰TxœD`#õR«ï¢´‘«ÌrÊs—2/55¼ Ò/­Ïø°T†L‹‘êNÛïjưm ‚i̱l½Ô¹l?žÿ®ÉJ¹Üc?«ž}ô>â 0œ¹š"3KÏІK%ÄÓð~è¶®.n—ñ+ämÔ#u³åy©§µg"÷~qsÉzí<;«¾½½á¸P÷þ>šá]Ŷž‰íÜ óK½v 5{z©7"Ì`#‰À¶ æ¥þ/›Í½½½<§vvðy>Èd²æéÑ냎oÇùDýd8J\=ºdÔiµžŸŸð2œ]È’‰g×Eƒ×V³3îÙ Ý’xW&Ùñ‘JèüüDßÈq"D`•\ eÚ\Æ0ŸÊVÌK½žš µ´Uœäë0WĈ¯b_¬“8“sr ./µ~<<Ï×’ÙÃáͺ!bkDØP¯%«½g_\\ììdðÙä‹ÙLï;æ0¾î8>>ÄVêâ€Ú}\9ÆæøÜBÃt ­ˆ’:¶" k®}L¹\®ûñ1OkY†BÀñRk:^sRVè’…Úä¥6š¹¯R«B å0£CÈr€ ¢ä@Äy©UF߇¹Õªuäwn{e§?ðÝQ±ˆï›aư?===`d(ü)¯úë‘ØÞŒ*•Lïîê@­Úó¹ñ«Z½½½G|¬+QU©TÆ‚äò7·z¶Žc¥j›ÓŠýOÁ7&DÀÉK =ZõiõRkp •ZÍÕ’£@”iCQ2$ÐK=§Ž$©9+r€ VÁˆ½Ôª£ÄËBh†{w¡"Cx†® ?"lý£/ÙnÔ†¼½Þ‡Œ*5:¡’íf2Íí/Ø5#²ÇWЪmô¬ :Ž•Nêa´CˆÀʘËKmäh‰ª½xÚW¬}õ:JM‚û¢Fä9 ùݧãkõÚUt¢šTvÑ N ÄÓ°vè†;;)ÿõï—¤¢7R—Œ[DPÌŒuƒ ÜþŠMa›È^&=ë¢m$¹×e˜@dTND€¬9½ÔÉ´ÜBŒJ-?¼e£[›ÒcJÈr€ "åÀŒ÷¥ógÉø‹NÖêÓ¡ÛâA¸24ÆYí¬Å¹\>Ò$wíö ü!Z¾>DØúÏŸ?¿~ýÒ•z,P¯¡v«WÓQ¹¬y<0x“-‰tŸªdW*ÆQ•ÙJ³és"D`•¸y©§x©‡É¦5å4¼Ô3´j'ÔÏÈr€ ¢á@Ä^êE{c„³yá õ¢T*BT¶ÆHΰs ãæ6³^½^‡äŒtØDjÅdwäàCÌÝ8?ÇJäñ€{{/“A­žL×××HÓ6>a<::ÒJ° –m9Ë"°0Ž—Ú¦eÔKý_6—ÃwÇ·Ï]Ïb=H”ŠÙóÚùU³þÑíb”c¾%n«é‹ö]½Qÿø´s¢”Mà%2~اdX©ñ‡ä¥ör}ÐKÍ<'FBÁŒšq Ȉ9——šñô¶„Hfò ¶‘ôR3¬!D`÷R›1˜L¶UqQËÜ(ÓÓ´ê4Ñë4Äœ˜“äÀr€^ê5 &Ø"@½Ô}͉gœÓf® 1½ÔÔæù®€ Èx9°æ^j ¶‚œÓÅbù§‘"úòòò/¢;¶bè¶nI ƒÝÍ¿mhå,@ˆ@á^ê”ÈÔê¨Þ‘qÅKmæžV-¿ò»ûíþîž0—¿+àØä@ÔˆÅK=`Q©cÅNç¸ 6Æj±£ºÌ_Éü#Aº%1^ <ßóo;{X’É„z©Õï%FÆ)è`›‘œ’ôRÇ«ÍP#þä9°åˆËK¡ç )>>z¶$FGªŒ§¨k `c\C¨×˜ëˆå:5ç{{{ˆïîît= (¬ë±Œus-ÙÙÁÚ¦Êôûûûãã£Ývγ @¨—4.Öã¨Áº/.jU©mÞêsÔéÉr€ "æ@\^jÁÎ5µZ­r¹ŒAÈÝ Y·Dlê5æ-?ξ¸¸ØÙɼ½½užŸ1n±–T¥¹Ûíb¬ql…ÁÆMäÝ8®có³³3 Ó{ŸŸ(ÙÃ>M:gÛ¹ÚÊBD€ü#¡^jU©eÄD‰§ÍʶÄÓÌKmn«Ì=ÂÜ#ä9@ÄĈ½Ôª£OÄÜjÕšSÏÛ~Yãé©áý°?===T«Uü‰ùƒ¿þþþ¢‡Mïî"×Â8Ƈ‡‡_Õêíí½zU•JeüŠšmµãš4Uꌑ¸9XP/uÒ¨ÔIuQ›9™5ª¶OÏÔf"Öf€<1'æä9@Dì¥V }"æXÖ¾VG~qçnŒ *2„gèÊ···ˆ°õ×ÏÏaú,«+÷zð‡ºpc§ÓÙÍdšÛŸ° jFdŸÍf¡Uëúqç4½Ô ÄC,JþP/5†xÑïõiYú0?ãóR‘-÷2R7ˆq Ȉ9——Úû ÁÞ÷ààñ4¬ºÝÎNJ%d Klórd2Ùq‹ŠA±nßÜ (·¿bDØ&²—Ië¤J½à9aq"°TB½Ô¢RŒ•8P]„^jêCÔÉr€ˆ‘qy©­>Ú#Ý\cÞn·XëVåòþÄB»}ˆ®Ä·†»ÿùóçׯ_ºR•f¨×P»Õ+‚é¨\† ¯¯¯¶d¡Pp•l»mh;Y€% ê¥V•ÚøÕ4ËG²ï/«B  N“0bMˆçs^ïäÀúp b/õ¢.‚`„¼ð„Àx]*!*[ã$gØ9²s›Y¯^¯CrFºl“´îÎjÏÈÁ‡˜»q~Ž•fîí½LA¶zN0]__# LÛ¶ôR/zÊXžü=a^êÿr¹ìKçåùùÖx>$rÜß/!ËO³yõÑí–Êe`œöTj‰µé¥¦o2Bß$ýúün \ÐK½Œ@€u"° B½Ô SCž–ŒÔfî ÓôRS›ç; r€ bã½ÔˈX KB ÜK­¹¨“i‘ªÍÜŽ›H/µ9 ÌyÂ|/ä9@ÄÀz©—°"@–@¨—ºoˆ¾ Ÿu.*µÌ饎M›‘SBÿº±ùæG'¶™ôR/#`D€,P/µ$¤Ö¯¹‘EÏø†eæçú —š¾Fêôä9@ÄÅz©—°"@–À^j«RC«VYPþüÔÔÆ¶Y㱓ÿä@ì —zQë D`IÌá¥öTjÄÓP¬1§—:.=†û¥Hä€å½ÔK X  Ë@`/µ:ª=ó*óR# ½¼Ìu@ä@œ —zë D`Ìé¥6ºˆ(²O™ ¬Ví9­ý¨!QC"Èr€ˆ†ôR/#`D€,P/µçŸ–ÿ$æ㥖Œ2V9óNP«&Èr€ˆƒôR/# `D€, P/µfüH¦L襦>Mž Ș8@/ut±÷Dˆ@(/µñLòR##µ—ëCrT‹nM/5½ÔÌÉMä@| —:´‹g"@¢C à¥6.jtc^j8=¬JM/5ýÓ+Ž È5à½ÔÑ… Ü ³˜è¥Ö8{ÄK­Ê´Ÿ—ZGOT­súék$Èr€ˆžÌKÍ ‡uA`¢—ZUê/µ±T›ŒÔ2΋1ÎÑKÍ\j´'Ä bà½ÔëI°D€™Y=ýA?廨5?ž™l^ê¡Jß襦6¯ !Ä bã½Ô cˆX#&{©½8{˜—!·èÓªUKÞæ¥f.jêÓä9@ÄÌz©×(ž`SˆÀ–#0§—viñOû^j‰ªé¥¦FË\ä9@ÄÊz©·<†áá5B`N/µQ©Åâ«ÔôRǬÍÄ5V÷Kï89@¬ è¥^£`‚M!D`N/µQ©‘ÚCý³&G5óRǪÍÐÇ}nbNÌɵâ½ÔŒaˆX#ñRËaôRbø¶û•×$k—m ÿÉõá½ÔkO°)D`˘ÛKmpªÔôR½ž9¹õ½q ä9è¥Þò†‡OÖ9¼Ôê¢6mÆ<¥?襦VM͘ È89@/õl  sx©Mj//µDÕ6ׇ öB?15Zr€ È88@/5c"@Ö9½ÔÈH„>xZ„a^júzÁbzÊéí&ÈX9@/õÅl ØræôR«J½³ã›(9ª™—šºTº}Ûæ–Eÿ:Ç,$fãë¿AÝòž‡Oˆ@„„z©ý¶ˆ2Ý AIT£R£[7Ëqzé¤ylu2r€ ¶ôRG,pWD€„!ê¥Ö IC§–Œ¤¾šy©×*?+µ[j·ä9°m —:¬‡çïD€Dˆ€ã¥F:2›Q\´ ƒÁ6Qå¨ÔF™–×ÎЧ½¼Ô‰©Ó“ä9@ÄÁz©#Œ¸+"@f"àx©!Cûq²qjšàú‡›hV@¥NË¢„ÒüÛ¦‹ðx-!¨ÓÓÓLñr€y©ã"°.Ìã¥F íkÕ"V«š^jz¸9†9@1r€^êu‰$Ø"@Œä¬ÆŽþ oÒãyn•¦ñó=CŸö¤IÄÖÌKM]*^]Šør€¯äOÑtdÁü'ÌøÁ‡¨z©‘Éc’—ZUjñR{ 6è¥fžæZ!Èr fÐKuÐÀý"0 1/µ~8ÁK-Qµæ!6?2/5u2êdä9@ÄËz©Þ"°.Œy©Í;4/«"g/ãÇÐ?íÉÔÌK³6£Q¸ËÀÛ—˜ç]ÞÔñ¼¯ÓµO/õºDl &|žè¥%Z~ð3~$Õµ&s1†¨J­cÐǯnÁör€ ¾=è¥fCˆÀ!0ÅKí…É&/µñO3µ™GOT…€Úµ+r€ ÈX8@/õÅl Ør&{©%ï‡ÍKí¥¤–DzHüÑÇo¢UÓKM ìÛk`¸ xŒä99°Î —zËc>X#&{©ÅÓ¡_!ú^jW¥6–楦Ÿ˜¾Rr€ bä½ÔkL°)D€Ìë¥6‰©=}ÚÛôRS»ZgíŠm#?ÉoÏz©Ã"°F,楆ßC !ž£š^êX¼ƒ1jB<^úeÉr`­8@/õÅl ØræôR{?R^ÞqTO³êÛë"ôךK†9^˜ë† âç½Ô[Ãðð‰À!0¯—ZõiÿÛÄDŠ^jæffŽ^r€ âä½ÔkL°)D€,楕Z2~ÐKM­šZ59@ñr€^jÆ0D€¬‹x©“F¥¦—šºTœºÔZù8ékg¾ r ^ÐK½Fñ›B¶y½Ô’ñcôó~è艪US§‰W§!þÄŸ ¶–ôRoy ÃÃ'k„À¼^j3n¢Žöb2~ÐKM­šZ59@qr€^ê5 &Ø"@ñRû£'úú4ƃQ•šù˜ç„ Èr bÐK͆5B`N/µwLx×$§žÌVm$kÌè)$ä9@Qr€^ê5Š'Ø"°åÌ饆ßCrQ«£ñ4½ÔÔæ ßQð[r€ˆ“ôRoy ÃÃ'k„Àœ^jQ©M^jõR{ú4âqêÓÆYN]*J]Šû"ßÈr —z‚ 6…E¼Ô"KŠV-*µ¸¨é¥¦P«ŽØ?JÌyÝ‘Êz©Ã"°FÌé¥6ú4ìÒF«Vï½ÔÔ§ùŽ‚ ÈX9@/õÅl ØræöRËç‡6/5½ÔÔɨ“‘ä9;è¥Þò†‡OÖ9¼ÔÆ+ÜÇ ~õR«JͼÔôO3Ç 9@qr€^ê5 &Ø"@æðR›QuÜDñR{¹¨é¥¦‡˜br€ bä½ÔŒaˆX#æôR›Üy¢R'á¨ç½Ôqj3B Xý‹Ì·@üÉr`8@/õÅl ØræõRc4òDbgÇhÕ’x ã&êœ:MŒ: ñ§Ÿ• ¶–ôRoy ÃÃ'k„@¨—Úo«(ÓýÁѳÉEmæÌKÍ1#©—“ä9è¥^£`‚M!D ÔK­!’†ZÔhÒŽ>ýññ‘Nã¶F­:ÎñÈ?ß“äÀ6r ™D$}ÒØûR]É"sx©Mc4í‡ÈÓj¥–Œ©ÔÎëëë××`gg'•J!æðZs™8ä9@¬”èwÐ ¡Â^ãøævÓiq"D€D…À[oÝ‘;OÐ7zny¾7Sÿ—Íí¿¼¼ð¿^¡PÈd2ÆÓ&kΉ D€¬^¯‡¾)‹¾'“ÙI§‘èU'ôdoGôm‰BÖŒ°¨ÝçD€•!ðÖKt{ƒÖq2›N^´ïêúǧç$J¹Be ©ß^^n_ᢕóýL²Ñh´êÜ£¾¾¾p_Ãd¢qoÒ;—N\&ä9@KçäÄÓ;™L:özIÑ„¼ÈùãÏ×ÝKâ¥ç¿Zõ×ËkV.ëëfâ@Èåq ·3¨–ÒYÜÉÆåÍe³‰ûªÌ¸ÖJ™D¡XªÔOc^ʉJݪWµ“@iLŸŸŸòޝ߇í£ßïs™8ä9@¬ŽI¤ J¢«’ÌêÓ®VÍeÕì‰q "æÀP¥VïF"QÊ& ù»ÙýîÛËý«1]u1kTêZ…ϸžµœÏyË{Σv2®·bBä9@l ¬J-q²‰ª‹®J-!µoã(æ’óóvýŒÏ=?÷Po Eä9@äÀ:sÀQ©5¨ö¼Ô?$ˇ<È<¿´Ñª×aÜ,¶ã·‘ä9@ä9@¬?üÏ ýŒP©U÷†y)—†JݪŸQ£¥NOä9@ä9@X½ÔªRç P©>mÄjoî'¦Æšõy&àóÏ9@ä9@ä@Ì楶c;y©‘DÏK¡×”òéóÆù ½ÔÆÈC?Ó:û™Ø6ò“ Èr€ ¢äÀ˜—þŽ$òRû^jW¥6¾j>Åü ¤¹¿Í;ž â@ä9@äÀ:p`ÌKO%Ň——ú¶£^jYY6*õÕ˜—ú÷ïß777oooHDzpppþëWzw÷ÏŸ?­V«Óé`åþþ>²ï!-¿–üøøÀ¤äù¯Lz—>$jÞëÆ·v» öBÔs°Ïú¹BÁŽjÙžÙ‹Ea»“ V×Ìx>~¸»»½¿ÇµU©TŽŽt[\#¸X°Ìq`s ¼Œ«æêꊧR9ÞIŽŒ¹ôëqbl3ñüºØºø+“»//¶…^›•ÓÓëk¡¢[[fg'·¿þTœtÍïooÁ7mÚ+¢ûñf"®’vd<<<ÜÞÞ æ+''1 ¾Úm°î¹×ûÖ•ŠÕª°mø«vn0C9æ¥öãi``²˜¤ £úèýýýÉÉÉóÓpÊÍ&Ê Èf³º 8—Øêþþ¡Ö<>>âžÕh4ù\µÏUlƒËC<ò¡û, ÏèïïqGk‚Ò†óè¹:ÏÏè,qs‡K¥x®ÛjV·'O΄÷ ¸‘==?ßàFusû„éáA·m5›W-Ìèæõ¼`¡V«ññ^±(À@°ÔÅ ñÖŒs5.l;¯¯ ÷Ùù9.«Fã¼Óy‘L®‰®/¹ÊükMãT\˜ª.ñ°Û¾Áµ9Þf\¿_ñAýÔ÷x¸~QÃèu*€˜d+}p ´AúµLWúDüÝ"ÊA¤bÛsÿô„€-|¼¿/ŠçØPú>é%‡½¤wß°uv__›­@x(úT8ÆÁ×W¥ZžÌÿs»ÕÂ’zwB=ˆÀì½HïQ˜#ÀÊå²ç¸˜2ãunÊz„ÇÇLjþ×yÆs‚ Ð`Õ¼ÛL<¿.¶–·Àg§\*·3ôÚ”xZ¿g ìµp{³™“î`Ò;ê§§g<>%Óò|¥Ûú×Óã㳉¸‚F¿ƒÎ}%ýÝÃÃ80T6Dúš°n'Ó4¬ÆqýE;7š‡á×Ë/õ”yðU}?̲ ¢¨ëÍ/UQæ¸'â^¶ÈÓé³³3y:O&!ZŸUñ´„gw?sûè£z½l‹[^>ŸGÿ±Y’óëp¾À@ˆ”úVPÛƒ@ ÏŠ–«r‡l˜;¤s/ŽÂíáññáÏŸ¿¿êòÏŸ?ŸŸ%ÈÓû*î½z_Åòð¾ýôtzZÅ-Ø\,^ÿ` Â\㘠ÀE%Åý}\?à<â”1÷Ú`¿pûp'×KWÌ."´j¼Ìk§ƒÞ^w¶ v¿¸~³ÙL·ÛµÛâ Œ èáƒc×ctÛ ýZµ*¯aÇ®qÔƒgcÈ¥î¾Ð!â¦!à§ÓGå²ê߸‡àv!7ÔVC/éÝ7lй¡%çrûØÆ4oû¦n ý©ÑË“¸- L×õ —¿A G<är`C¯ÍÞLJå-0<,•Y~}A—=4cÏ%° nÛ˜뱌XœÄ)>®TÐwψ1&òsbl3ñüN»ÞAT„ 㘇^›8›^ æ÷#8FÐáÇŸ?rE× ôÍ®Jå2b0qÚ‰~G/«ìî.^w˜fØÇé2`ŽË`Êê]úK¿Oü‹v®Ã½qumH%SP²…«2¹Žö 6/µFÕÆû!C“Ïòï⾉ûè‹gÌïî°ïkä][±ˆZÝg5Ü×pG^Á&ˆzñ|uÒÙ;œ$"! xœÂOK˜cYël_¶ÿà™Ï[xÉ¢û‚X,P²|t1P÷….®4Üʵ“PMq3J¢«@¯£uÊ;ÓR šA›·/µNȇRFJþÂü›?om¥oÜÀíx¢þÚõ *J‚çVKP.Yž[-Ð>Ç»u¢‡†Þ å3ÙìÇÇoyg§û Åêw&“…¦ˆ ÷²Àµ³jm†õë Ľ릷ºoÑÁ#¶°:®¹Cž$wHÜ¥­ÆùùÙC°‹Ô]ÆýPë±w`<ù¡ËD0×W7¨—½ž$”L$­†êb‹[=.„ÒáOD¥&â‘ú! #‚G=åÃCéãÍ5òùùééð_åp½|L;G34xÔc‰ÕþuŠ€Ⴝ®í¶V·F{ðØi/»Ç¯ ˜4Ð0¹G¯ý¡öüñ!ÝÜè}”½oØ:qÍãùwDE€bâ5ˆ `NÛ—m³«Ó÷¾þLì7î¯Õë¸â|!°x"T[À"¬ÁÝ! ºf,c Ö#’ÄÊ“£ æØÖ¾÷›cLä§=nlãâß;¿î¹ÆyDì}>ðsöµ9ÎIÔüõÇœÍ}‰¦ü,=EÓ® ¸çýŽå'Ú‰‹q¼¯z¥’<'ÏîÝënF;¿}_6ÍK •ZΚÉHíkÕºjL?ÐxÏ÷¸M@T3q‚"À•Û"^==Ég[ 4üdãÏ ˆëÁ~<ßcC½TT3P_)–q͈*LbŽÎ@Ûƒ³Ž§7Õ0¼a ¹œÊ¥2J¢XÖ}¡ ^‰á6‰ íó=V‚L¾¾à;4+=EG:hCS,þ%±yÝh*30ÙP=@qÞÚãê÷ûŽ'ž;ð<Çs9Þ¥ óSßó¸–0M·­SHf9Ùïê2^Ñâ% º"|fw¢À5‹‡/W:þS*¡1PRÆ÷ÁŨòr¤ãLjJ¤›3¦G«jÄ…X(ÿ)P|+8Ã϶ïs§Àósû}®Þ‹´ŒÕÚqPºs»à>‹ ˆ„¤¨¹Mw"0À¬4!µ©º5n(x·Ž¿&zŒÔ—†9¾À@y,£aÙLF—qëÑS‹ùç'V˜:{=aƨW %q[P7#OÏnµ ¦à]Ìi¥-Pë„î‚Û,Þ€ÉÊWOéõÊeá1ªè}HKŒF~ƒ;2J¢SAÉ{Ö×ãµç‚ËãÏôø¨ ™ø¬ïz©ÿ÷¿ÿÕkyXm/€íl/µaÓ@·ÿRjG—ŠEÔŒû#4\; ™ç®nˆ»šçë{èÇÑœGe nb··À ±šy/,ÄóæizH‰˜}ï¦ÞÐHIïÝ¥Y6ëq_õzßdË–ÿÙlˈYá6cË‘!"t˜v¼¨Oiã‡ÀMUßCú×Qr7-FáVKüÍßð‹Ë·nÝÅ´:g{©Q?ºD®¨S^U*Ú¿Ýñs‹4S>Ò~-pã …µc"'ÅTóë0ð¤aï·è%ñµÚÛ_óÀ“€ Ç=~¯ÆQ»­zvíñâ ì¹°^jiáÑÒ¿Á=1Þ–@ªUðX‚Ï»q¼dàüáãN¬×wX#!õ` !¸1aËkŸñc>ØFë™x~ÝsŠDø^ï(Ü6̾6]NZ/µô8›¾yØæÁFjx<&öïˆmq˜ˆ+ÀÕÔÎŽñnéMaÂ÷†fÙ­Îie®Wþøû×ó=µÛ{Ìå¥6ú´qTOðRÛçÐôäDÒ¢%w:ø|f ¼}Á%ýŠïNÌ3ÍG·‹³‹gÄcsú'j ê{Æ™Ö0Ú}þÆ2n=öö‡„ ú¬†·ã«ö4ƒšO^`1²²Çe !ä¤ZÅÝ\ë^­Žk Ã}MëÄ—x–mÍÿµý(©Wõ/)‰û”·¯‰ÇB]mñÁûë§´ßõR+ìy¯ŒaÑãx®7Ói€CÚz:a›6…G¸„Ç<°a¸«a±<€»ÏuäÛs^_³ù j4’c üèõÐ/Ê{aÑ£< qÿ”;#nÙæ9þ¾Åõ%ÛeleÃ8Ücú.êGØ«9m½ÀŒ{£^v¿¸StóÒÏc=.Lûi;.ˆiÇ>ÛKz ‘àx±_ïJ@ßÕ}™§ïZC¿† Xû5÷X½IbäÓ ñ þüZMµÒá}À|ž8{ü;"s1Ây3Œm7<þ˜— #ý¦=^Ûf{~¥g4×o™¾Éõb¼æø†[Ìœ¦ïÆ;m|@•Íå€-n•¶±Frb$“6pŽôkNŸñc6>nlƒ’Sϯs®ñ؆¶i˜‡^›î;Û64ªß‡Éàáò êçzœ3&âj@Ú×€;пےõÝ.º¡üxyh1¬s·µ[¨ÛÐÍç¥ÖìÔSòRƒµ¿?Ã÷ šªK Ïjàñå%¤ß¯Ï¯/P<›—S…×dx†t\34ˆ¨ wOh'B㥶åq‚Q@EH,º¾$JÌ-ôoø‰Ôdbž_ËØ5Ú…ïÊ/..4¯5¸b%žøu æê®Æ‚¹}'µNõ±à¸àr³ubߞˆú@7¦[D£?q¿«Ã=î†ø¬Û0ú8PÓW·£¾‡™ ÜàÕD>#WÖéG®hƒeÛ'ÎÓÎïwÇ<,/õØè‰“òR#©Í<§R¸Ué¤8IH±)N;'/õœù‘Ÿ<ÀóžDE0ù€õ»i,ã¸â®øN îbØ.¸—`9Ê}}á­!VšŒ§¢‹Ã2‚WB¸5©'i|fŽ:?AVc°SxæÔ· Š`Gø1½³ƒƒÒ‡< ·W)™ÉÀóÈùš³0Æü¯l›òg^º¯pø€c䊥ƒ³³a^jËÃñzÀ´;$hÿø@†Žê¯_–6_¬Ü6à[1ܪ@3Óæ¥Ö2¸•ã)ïé$·(’j]à 6’o ç?§óœ÷oVÆæJG‰\η·÷;IèÃꟃ/Ĭö©ª›O}â2úT÷¬ù˜í¾Cp.ÜÉ=mÌÉ_¸õÞ‹:«ÈÎ~r‚mѼÓÇ·z¸ãÒ@n¹o??á;Ë?½Þn6£·h­>,›3¼íGL^° ÞU*ìz{¼¼Ô¶_³|Cç‚ Èè;Ι‰yµ‡!•ѽ^RqsÛoÒ™Ýà†€¶AÉžÈmì׿¥ÆÝ O,pÂh4‰ýjþc·ŸÅ9nx ±¹Šç¼®Ûµm=/¾|…CI ‘†ùæ íDwlòëIº^]Fî^ؤ¿~xÀ§¥¸yâÁ Q5ÊÈGÈ‹:cØüÊ.>¸÷ŽÇ6Óò¦»÷y,×jgæ]ÊÈ=ßžÓÐkÓå$(¡.,$)FT;“³iÛi2›]_KvÈgÆ#.í¿”uZO\`ꟕ—ÚcòR÷~. ÖU4vš¿[r¯ž–—ú¿ÝlBÙMç OKxļ˜O6ÍVM>LñÖ©ÓÎz¬—± ¾š³nö¹‚úY'±%Èr`¥€ÏNb @@œWŠóÒûßm8_Œ1ÈÉÕñ¼qyƒ÷/=š%B.fä)×=qL¥¾©Ÿ­ôùÕÕE6ôyz¥øê”ä9°Î0²™|0cµdž¯u>_ÛÖ6ƼWÇù1•zPÊI?/µqQc„Ì4ûǪýX3¾×––л¼ ßÌòÉsr`39`rrÈ«t<›y¿ñ¹cŒÁ8ju÷%ÇK-^“1 SÒªÔ_Ö|ZʧÏçã^êÕÅû|–"¶ä9@ä9@ëÏÉ*u^TjÕ§4lŽÃ¨ÔÆßLíz99@ä9@ä9`9`óRËèK¢RKÈœ´*uçK 0óé§û‡—×|räˆFMqƒ`ÈŠ¨—ÍèèÆ‘"m()ð¼Ñ¿ÔzŽv¡UÒr/s§y@@ŠmÙÔëz‚‚ÜÀŽÄ.YB½o0ÍW·Þ¯Þ²®ÔÜчôiR2_®0éš(—6¨…Çøg¼‡#3μ÷Ü4ú=(άÀäA£çW‘0cjrNˆˆ ÷^ä©?z[õ5 Ûsɽ«oîf^_ ÷±u¹?GÙp_Ñ÷¿Ä|«0Ç`Û2æ± œååiQ/µÍøñGGxÁ=(³“Ȧ“ˆ=%¢’;”FX~T=\^V¼5©þá~…¨~,¨Ò¹„Šê\ÑèÖ¹‡zÙÓ&DÒ[ÛÜj~¬iŽy<‚ԡļ{µ{ïÖ7&v÷ï×n¿Üåù2Á·ÂFÌf´Ó?:/ž9êáGä‚¡=KÒc™7fÙFÛ“—u[Ó“1þG@yâ½ùYƒå 9Gþ=A/Zªô©oÚ²<ÆúeüçÃpö~¿’Ë¿?¯äºžçº˜ÀU{w¢ê‘û³ÄÓý«Â"úËd…e\sùÛ5JVUpV±Ô†f·sv4óv®]ȘëÀíéÆû·ß™¶ìõ°V³ò{Ϲ×/šÌïÓ=ŽÞ ¦Ÿ¯ Ýé<‡¸¬2sC2á#ÙvºRé"<9& ;ïËq¤«¢ú܉ܦpÞ §Æ86 ú4ŠeïÓwL+åà²(!u£ÞÈŽä7SÜ—Võ¡ËûöP«¥Ü¸jþežµÎá²í½èPâèŽ"ñù)åÕ§eÂp›øC–ä~êÊÈ~¤éþW¿ZpçzŸÁ˜»:‚—P{aµŒ#û27iÉyîòZЋì]õz.6é“^ñ³—=ç»÷$42n°/¤{™bü’£ëâ*ÀN†GÁ…íšE{ WLªÇ«“ßO` p·vîõX:g¨skÜãoš$0¬ñšÎõ¼k ]^ô¸­ÕåmÿÜåý;†2@ï!aËcß~È+]–¹¡)ú¹^ºßà²ÛßO+3\¯ÝŠê5å}Ûç]_î§ËÁ23Ð œ÷NÃ' ŒI;÷y\ äßq5ìa}xmzm–ÛV3½¿©º!Ëî] C&™[›÷ö2™–;šÒ/Tø·ç+ìVÚfö²Šåöû·\¿Oñ>—2ý‹»<|×j1´:‘ÿ•Õb}v„C jxûÿû¥©}è<ý¦£0Zà(bþ/ÎùÒ³&s‡,þ­kØ­ùöz{[Òz¿G–:ÿjÙk³Ûþå.û¬v¹máÌ¥¿?ÿ“¶‘9]Ésîå±xÉ»‡øq](ÿÇË[FG¿Z­æ''Õ××—ûçNçc0CGÕÖèÑë¶cË^”lÖO[žµÿ|äVªÎoûvnÉôÂñ(ëæÞªø´if<­WHʹ¢lÿ=Ô¿ý½ØØÚ‹³ý€6äžµèY̧°gôhýc4P™.é®I{ÅÚçI+nÃþÉÕ­ÚíõM;лÅÖR|¶¾èëŽþûaL¦Ûþíz ôˆV³ÜïûmKèmXvŸºÇ—ÿ1jшÙïÃŒ›õIOÙèÛ¹ôÙÏÚ¹¬ÍkøÌQ,/Úf·¼´_TÛ<º¬kÌÜJ¯AÿÝ×Äe7 —wþ•«Ë#suý¸Ví²Qã*]ã/‹Æ\ö£öYe¼·aº­Þ\&-{W„WÆèÊÊ›[€^k¾Ví-ûå½ ÇFØÞÝÌ+oðô¯¾!Úªa<-MÓª½óbžQÇÎݤk3ì ^îõ2}yøÎJË B˳±i³Q½óhïr¦Þ»5,›;žê¢ècÑpr8éßÖ±ÑFº>o-£oÿ¬V=uÙѳ­(*ÛN[?»=öŠS‚Œö;ºÆ„Ùþ]ÝïSœþÅGlÈ[G3ÒÈÛç¿»¬uê4{YËym˜gÙï1½ö»}è°Ïõ¿qïOGÛc[8ìs]^ø‘´}àçÑS¬cYöµjÁ{홹^èãjíË^Ö6(JóŒU,[æ9×ãüV?rýÎÏ·qNNáðϽúõî1åZðî-ÃØlÍ$îP?ÆìüQ¸KÅây­ööû¡˜MdÒöæmîTc†£xëý/e»Œ(PãfUP\õÈ]Ö9 zƒWF÷¥åí~M¯Œ;£§Fhyu[­¿úvëá×x饨Àk¤¿‡ºñ³aa_ôi£éjÍ&^ôò­:Ù åÞ-xâ—Ük<ײz—ý½Œ-ë¯#swÛñe]ãµÙœ¿ËÞ¯æ ò¨ÇCŽhÜõ¡Gª'Ó‘\f¦†±z\"øIÌ-J~l=ât4õ+òêýÐ=†-›sï•_dYùaùYn¿þ`™àzë qÞ•»e¦-t¸ß‹[g\ËÿêÃqq?§ú«žw½,M”0my²Sßß ºµ,ê\ï'îòlˆ¹ YÝÚ]–ûÌø¶Ã«Þè¾vºì]*—½kÇËžd‚¸á²?ñ5rÿZÞ äôËO¼é:·aÿ:ÕûíX;=‰AÉ~>"Ž>ïêóÎ…ž£Ðëѽ¦F¯¯I×Ú$>Ë52›çú묹Ç(sgö?Ã{£¦wr3÷îløSb2Ó#ê|È·Õ,[ï‡iíPWž¼ìxEpކe¦-Ïn³ŒæÜ÷5Ã5zÒ¥xý—§ö8N_ã/ÛŽÍ@éurvÙÃÖy{à÷G ºÔžee= ÝväˆÜ>wlÙ-o÷;ÒÛ“ú=FžëÃúvtžÇè—}“m•ÛÂiËÚZ/ª^ê²òÓs}ø˜x¨Úsª‹«¸Xà¼)ã_³Z¿ ÿqy{§p~Úµ0¼jÌtèÓ§»F­‘Ëf²Ùì­«[ŒçþõÕ{~~V¾R=±°(8ë0w®Lmÿ a~áÎùõöLŒ®÷NÑßÝȃÆxÍë°Æ¿1:È*ïý‹óNÍ;Á£ÏNž»ÚÜq¦,OzÞÒ®_Qu—ÿçUsÏ{8R Æ"Æeÿ|̓ɪÎÑ4.ýÝz'(õOï(ã¼§|VzÖÊ×mieeîÝìì\ÿÃÄô¿?~ôû}(ÓÇ'§˜«vöö6?®›ìç ž¿‘Íi‘9ýÔ\Õ‘ªºXãÇ ó«?û$ûú[“‚Ãé–ÒÔpN™¦ÛË™z»Óó×ÿYŒ£€ 0Œ€ÝNÞ1ìó© ·íV±>¡ní¾ÍK”§iÒ/kfötÒ\€6§Û´ëü[ò'’õÞNøÏ>HEv{ćÌ›dWiÇQú§OŸþý÷ßQ4ÞÇÓmoo?ž®qŠãÝÏ[ß'ŸnÂÍÿœüøïèøûááP’ÕÕµ¹NgöÝìÿNNN Oíïïo~üØë5£J—ú3*¶—’^7â¼Ä˜r¸¤× Û:VN?Z9]m$mãÑELÖÞ¨gø¥“ ?<´ffZò”u•¢hå.¬©\#í!ÓTÊÚLÀG£Z.u¨{9ÓŠ ›Ö’˪ QÅwFR)€–µá±Öµ,ë‘5·¾Îúë™… }XIÿ!ýœÓ_*ë—3uŸÇ²xdÂwƒ>øš+úZtepˆh±Bg Â¤¶ ÖòD¤ ìòœ±”Ôz›?ƒ¾®ÙRgÎTÏU’Èÿ8.C}f —¢T<%Œ­h{fFåX˜TKøºCå-MÀÈkðr‡‹ç1uŸ¾yØcóO»½TDý'´Fî'Œ¯°T|[SO©¹©çäºæûq¸ª+r;¤ ûR9_­&W×ÄiÕrAŸknªü—uÄñ°9ŸÏ¡.ÿºô¥ÒCÓ›ìÕ ¶Ü¢l¦5ÀX¦Ö‘ s‡øÀèêdÞö¬™ÇÁÊí™¶ä/9ó­<¹¡VŽÛmH³8ÛÚÞú||ü â‘|¥RÔxìñpØyó:Æ\»u||¼½½Õë‹0 º›K=$kzûxîæ‘`žk·Ov?mî|¹¿½Õ‰Ú:;êQµq1 –&%ý'DdÌõ“'ÖóóÛ»ŸNÿëöÙö³³ÝíµÍ͹¹Î_ŠOŸìÙßXÿØí‡¾×•“Î !ÃøÂ3b•ñÃtqc· ™hU‰üâeîbéQ`V†ÃÛ˜2~s.GˆŸé}ØêbÌ 1\zÈhèñ³ B³4.e¶Ã[¢p®G8Jõù SÇcÏ mhï輟ŒQÕÞRÕxr§Å-¡*&ãR‰µõj5¿ o‹áк\YAnX‡Ü·(Î×[1…V°&Q‹¥F›‹ñoÖ§¥™lØ•Îüãóþžý›Å<®J_Wó†9‡ÎŒbúk3ÞÀWY_¯Ø>ü(&I3¹äñ`¥ù\zг‚$%fLf4i«ºoS­:Ó§™ƒä&RH%q5Û"ƒÞ¤Ç×Ãý=êtß{@pp/m>”ä¡=……‚äD­Z§d³Z^Ÿ–qêlNªìO¢õKfÃÜšëÐ…ôÞÖeìðDõæ!ú·²”RI-|¬ËGÕ¦ÈiÆ!ÏP®äŸS„¤çmÍ\š¡‡hØ™þ]ÞJÓ|L‡Ö©‚¹ÌÂ!F[úlK®º&Ö–L>‡ÙÇÏàáòl[ÐqÝ,l»‚ÿóŠÈWdö²ž]Hã±Ûlˆø<9sQK ûTQ¶XãØ 2G%¿ÉŸr8h{JëlžÒöå~¹wAoÖí¦ñ¨Ok<ßfOæçb2Æb<•JÈ C?Ö04æbX¤™iÛ¦U›>­ñå0ó ñÖRjº*EM»m<©U¶¹‚Žô²xȇÙ.ÞH¢5 aÁúB¤:*®oEY§DˆE’˜tÕ÷3~9l_ñÛÜSJc,·ìí@KTú؄ֺÜô+kk‡‡ç÷½ž¨Ô¨ÉæÖÇžÃz¥9BD؇]‰fLáw”*ãUBQÆ•ž²²ñù0—´}ôi_®C.Æ×?æ·È#¶Q½ÅË·³ZºvvV¢|Ì(),£[i™DiUñÔ¯->`ù”’Çu-ôi[Mº•b¹¡Ó˜5{/³D« :¥ Ô`úøóa] „Ðñ`xžÑÍrR9HÜŽ2:JÐZò»öù2„³Ëúœ¥‘—!VBɨɑ¬á'ªI®?ŒW¶1à¯a;™™£FÀkýzFš¨² ò!Ü -IàvÕ& 3Ë}ú.·“kŤ•iâîAá­¥ܧ}=£VêâäRä‰'&­mTlO“FÞcÅ ±ÖA¨ÃzpÝÓøV3{ ƬQÿfœ§uðˆf1õÖà¾Iüp/³‰JiŸf€ùO;F?¶úkEÍö:†EnÄøÐR¶WGPü–:72Írˤ(ÓKáío k³­ÜÉ… Ž:®ÑÄv*JíÍÑÊÓ-OÃ…]þBÛ°Z”ñ9$÷$Ÿ0Þ‡CŸãÉâAæÈÃ`ŽÈϹpàÇ~ʱ|Löš¤õ#(7š¬¨Ê¹5Ì¡Råü4eª3T§Q0ÙR5_geÅù(7öãèãÜ‘çq [žÂ6ƒЪ™L`8Ô4ücÃð•q(—ÂR(ƒ1Þ…y¤ƒßæ um+¼”ZûËë\Xs&’ŸŠU[|&æ­’G–ž–Fû!ŒÍŒß¸Ó(ž9¸';À±üI9ãg gTÍ¥¹aŒ«\Eóì´LDLäMDgÂÜ”Í&"pÃx´0e¸­Áö12×ø|4–B¼u%wI+”ÂèÖ?˜ïv»ÿ[ZZÚÞÝ]]ûèÆ1ãÇO]ؘM¾ös'{*vÊa]¢÷[ÅpN+åǬtøåj[¶ÈË…H#ÿsŠ´«LCR×iɘ ºnÜÍa6>ì3®Ø’/¤wC«EoðûàÔb5&6ƒó½|I~ÊÂÆBNçp ÓfŒ¤9t92§vQ9G¼aÜëu²#J0Ôð,ë¯KÙ¬fÚ©ÄZ˜¾˜ ×W*háCªM«’‘~;ñ    z~ô,oc¾Ð‘žælp±ËÌKZÅ47®³Rrõt-,ñI sn šN\ LuÝ¢E‡×PeàQôE~æp•êr¯ G è“V,3rGÝ(÷ñ‘7½iÉA"Gž±R´V2æAc¹«aü ‹ÛVknnVë3'ÏYÔ/9©J­t…\un2œÂ$AÓMÉb2[‚˜¾IKiü9Î !Ø8Ï\>#„f‰Ë´2¹0ÜždÌÞáë®_еËzÄú¢n­XŽgoÚʹ.Žè¤R*·Iú¬Æ>¬³ç¥rØ”ˆúé.ϘaáHaþE¶6yVMªy|Êͼ>ÿ¡áÐ^“\S† ÛLÊ9_ŒüF·$w?¿øμ‰öÌÉl?¨Ðö£6,t^é´7·>ÿ #±ª‚éæfÛߎw·wBžäpIyvrruy=„æ+++kk1=û48ÝÝÚØÞ½¿½¢?.FL²‹œ×}?Ós$åñM÷&šÏJgvkyþøª{©&)xË?ÑúfÙúioçæ·wvŽ/.¯ÄTZh>7Ó¾üqvttð^ÃäC² óõ½V*+Eþ6aY¢xŽ^˜Á5·Œ1°ê(sÍ¢®-¼õé˜[!˜Ƕ¦qœ¸=ö‡ïε±'â6ó­Oú,¬r¢ Ê””w²¬·¸7d;8å°%}Ú,=LñÑ®3ŒYGN3^ŸZ®bcƒ} v¸û–Z”ɬ˜’ë3ÊPJ¤dmóZ‘–ëX5æãêl4ɘ·Ò–Z[t/ˆøÕU ³ì¡ëת÷ø§è4§(OÓÆ S”>1}:k¯µ=4€ÍpÉ 2ïÒäôi#;çgƹ~,ö]\…‡>eMÂz)ô—Í^qËõ]NŸŽ¼a…çøÁóFlµÒ_¨¤uëJ—ú‹îh|¢iîõ©}û‡È¥¹4y> Ú³}ËÔ|¬Ç‰(¾ue!D†óü#—R«ki"Bƒö>$i+ÃøŽˆÎlX‰iéR yzÜ1æÀY:´R€Oü ¡®mÉúKj%$~  €acsq6=æâ©'Hõ‚4Ó ¡9 CŸ>9ùvzòMž§ÿ–×——(&Ì5ÔÅ•™ /ñ‰Ý>•¢z`Â$*Øk„:Ö˜‡ôéX4iêÓ±\Ñ­%=TK/¨Ì_Ëõ@”Ol@ÂÖÕü ÿÍf31IaÚ•„mV(ÅksLyˆM E±2|løÂ(Í2•y›¥!®â‘›/EsŒosß2#£6€Íà,žé‚}ë]ü‹ puΕ[Nr¹‘‡D¥ÈÇhéä§øDE#iÿ±Ï`‹£làÂŒ‘”öVç`ìÇgë^K”#Zú„)‘ØhêÌÿé?ƒ¾XpöQrÉ=•; ë0}HnÚ¶éßòe#A+Pí’}8Ú- …iµœÙ.«¦¥6öfÏœãaÿÔiö=ZÕ,SÕ>U©! 4‚xyB•´ÀåɰØpÏ]KÐöæû…:¢VNbM¥ ¶?HJÖ®¢žÖxY—&ŸKwmgY¤ª¼AXòÒ¾“'ê¬afºÖj¥Ÿ± îi„¯ò}”kÇ…pN1çC5«¡pŸô žÂ¸ÆW÷Bgt(/ÿýãSzJßÊ"€¶éÙR.unyÊǹoÙƒÅþÒÔ*©Ã̤¹†m>L=Ð)1›KÂY;#¨vŠa;v{”q²ÉÄðÅ(mt•¥µB±ü6 Àñ.cíC‘ '2¶b&çPãÍ^Ð…™>|ÅF«„á|o\…Ùö\ú¨,…¯Œx.^Ѝ‹Ö҇C>.}¶ò57 ©yŽ–žÍ¨aT•¶ÛOéæÃ¥ˆº„…²¶;ÊK/ä}Î*:>É w³7IíJœ¢.ž3𕍒¤zOÛ8$ÌDžg¬\.šOež÷_ÙX#È!ò#ÏGD¦rœÆœ $ñ¨D†„<«U†BY¾t«alE¨•«¹8;lE5Mb¿dí%_±íž4ƵŬ¥™Òd‚…3ꦦ¥3Mél¬µ,£Ö!fLÅ3“]ªV­±«l©E8QD‰¼E“1n…·æ[1.–x¦Œa”Õ§ GÝúšÆr`˜œSÿSšhnYþ°œî‹6 Ï~ÿN¦£ÞÌ–= ß÷î¤: ßk|x"fy®½¹ù™B“¦>Í?¿ü@ò‘œñ•æÖǹIÓ5O”âËÅ,E\ÉŸd£ÀùßÂÂÒÕÍ•L4æ²U r¹¸¸0Ku“wí„V8¬Ò =7»²² œ?jÏÁÎ,ž±%^&ìÓ¡>PʘQrf|xK6ж[+ƒ»²|{= 犳JΡß# TµLV”ûÈhn<”s¶NfÚpV–›ÑGe  Ũ¢¦¸"`K|KÌØo~é7á‚ÙŠΘL{lÞjPÉæ†€cæ n×GæXÇŠéÖ´"ž¥0Z:b¶ggÑz±ýhÃôc¯fÚЪ3\¹ ¢÷kíRå--š”Â߆4F%Wë !(õJñšƒ‹·­1…Éç*u„–Ó‡A‡t­Ü/e¾Ü0î‚~Θz*±½&Q áõÏúÅõN)'J.×QCþpmºuÔ #oäì¬TæÔ­šr™¥²™4ÚôS~æ¹Î×Ѿ´ÜªÚ­ÇΌߪægR·£åÃH4 BnGJS_ »ÉÚâF•;žoˆUçgR‰e }z4!èoÁÈò,ÖÏߎ¨LWÌ­#ùGÒfgÄãÇçíí^)Ïo{ûÔh©æúþeüÆæç½ý=ÎÑq<¾››=Ûü°¹·ß»¼`Ä~d8FÑ~¯#¿tï/{ƒ¨I39õéyÝ _e(pèid Ï.,íîí_^ÝBó~ÕVæÛ‹‹Ëÿ›_Xøuss}èøþüülcuevv–³ÅßÝ]ÿÇå%Üò•:=œœ‹±bpHTÑ7éSÇøNoލ¶S\²º:ú–ØnÜfÓÊbz–K# ‡š8ëTƒÙ£®Ì„ö vN‘G*"߯VÐÖKð åY¡L—Âtgœß#˜ŸF«„\›NŠFölÔ­©ôRy–ŸéâÒ;ògEßQÄ*Û:Ð(‹£bN¹¯z<í¿‹zdÕj'¯=GM+ë ˳–O´”0&ÙR1*=Æêœé F¥låtY!¡é©%žÌ–JÑ;Kiâ,®L„gsº¯dãbhÍBÿAæAÔÆ‡+hÛÙþ`6?iÍ£ ±äV |¾Ü*©;ÿšCwe5YœDÓÔÒ 6»[?æÖ0¦%‡Žésþgüõ¿l]”µKG}Ø+Ïø±v0³(͆NUYÔ†9 tĬy¦OGéϱMð½ì'È?Yǰ¥nÏ̉&Oa­ prþÒÝVc«bZZœ"ÐÐx»Äþ>Þ…9¦‡ÿØ£üùðcßß×åã⽤œFø‘5Fµ0«]T– ÄP^,F¬Ú4ݺ>ªšÜ”è&KÃ$” »(iƒ,•”¹R‚]±Ú‰’°¬gGil·që»øóò'k£éñ~)dSN˜£ˆÊ|ÈhÖÒ.ÎÃÅ2ó Ù\OùµJx¡Ë¿Èô ‹÷iªÃaM¢s™Ö¯fåâ]õSÆ‹•X¯ò(ý$®äÙ²]µÑ¼F3¦-õ·£ow@gƒí;Šj…Sv¬©ÉdiJahÀGßwwwº–§ùEƒ½=ª¶[[»[{;*÷äm]<ßÎεϷ6Öw¾ô^¨M©§›$]ÍYÿ“nÿêŠþ+‹Ò—çZ›ó³Yë2Ï›™Èá‡Âï–V>ïîž¶Ôød\\^(õ]V)2äÅùÙÖúÚçÏŸ±“¹=hfâ vffæ¿ÿþûùóçû÷ïŽÏ66×3”š»=ê¬|Ö¸~òëdµS”×VËLÇŠók¶.È>mœÄEÃè»quu´øaoõøæÛj'[uõ.>/o\ì_^m-ÄÙš¹…å êÇçõ÷ùÕ½½‹_ÛK™šàøRRÛ'ÁœVÍŒ9!SkטliI}·øŒè`؈Ǭަ?Ùq›ÈWºç‚î’TªýgÛ-þ¤µ™ÅºŸù‰zCÔã³1Ö*qœGvŽº¯Žü<9õ«ÌETœ¼ˆ]Q¥U² “&Q³ÏÉV“סjÚÒ=¦k€Ø_J%ÙÉÉ´«¨¸Fý5k{D‚#v›­gfÀ4e n+LroY3ºVt+Ÿð-‰›åSж+uw.Œ3Í~ÜàÙV/EÔÖÆ]Xé9$ÃPІó;:þˆ:í3Ô$háC¤r¹õpjBçF1±[ý6ãÉlÝHžÍ3ˆ'ðŒ?ÏWFLy®\v÷D«6}ZHhü©š²”<”~ñhG«S¨uÖCM1†ùAÔj‚Xk˜(5ç0;Õ.þõ"–l)\òHGèÔ x†ÿh%_U…­€ØÈjM1¯ÃY>~$`¢¼œ÷éËẩ¾5ÿÖÉv/çsÂ%Ð*£a™&Ã)µÃl-׬#|ªB|º0Sñ—… ºuÞ¯­(¿Œ%çôéB[‚^eZ¼Tꤥ܊÷ÔÚ×e¬ÚƯ/÷1?ÖÞ&­Iá-áý%ª±j=Íe~©»Þ¨ÿ·l’Ð\¯ÏÏàný;ÎÒÚY2êcós³ßOŽw¶?_w{âbåâä÷  ùÖöÞÚÖVÔ¬Jñ9f]œ÷ãÃòæÁÑí1Ôq›Ý">5gM¥úÞæùÎgç/[sÇZ4K_iݯö»’^aQ¢ƒ™†&;‡AC š±³ÎÚÚîþþi÷Þ£Ô«óí¿—åx¢ò¥³ú‡©Rº\…¢gÁ(Ç}Z­I œÉŠúôýùÖÊçýËÿ={¿6O¾_G¹@m2è”ñE …ÏÞpÝÒøô%ûB±ç º#¿¢ñ­h“ç›ß¯Õ–œS÷êø\Â:/Ò¥¡ù…Õ²øm’– ‹…õ›ÞÍÖÂw›ožÝ…܃̑ÌßjË:»ú07µRCŸPÑYafXÙ’Ê~1ÞÊöIb2Œ$|òÿŽ6Ù9¿Ôš5¾s4Q N5yeëÄî)_ç"UP 4‡¾´öÙ`±â–ÂÁîVøFßã^†Ñ^DH«iÝ«ß;oyk&ô+U+aô‡„%FÓk>!Fþ{⢠nòRVŽþd2ß;™U®æ¦< ö»Rl Æ`k¬!1ð}&,£ÃöËlµæ“VÝâvXR2KÏÌŒ…¡„&°ÓR•îÑSHß×.Õ°ñ‰¼5[yWÿØG¦áIzŽÐê'ÏnnW¦PSsã·ÂÛ /ZnlQà[¥¼R2£°rµ·£½¾šÿÓ¼]c¼ªãEméXºZADîâè( •¹‘ÅQÆxJ†0îT“¦Ä:ûЉ(ÙDŸ¥g½hJcR+ØD‰.k™*6£T­Ò§‹c<ˆ'“3¬ƒhØ(ONtÔƒÏú£ÎºAù TÚeOWÕÙ¶¢ÖG²Ñµ„çj¡Š|À—ú« óe¾5aK3*È·|é:aÄ<óÂ…U>M˾uùd§TmÏùË ª2½wßf„píµúªÉT í{DŒvÉ'&ªníÚk}{ÍuP¾£Œo9³Ö§…°Ï?£Fèã Ö!ó¡k½”€Ô²,[¿‘»Šs™eÄ;¼Å4ÔB«1Òœüd<¥Y¨Oßq‹cXÅt„'û1!{¥Åø°«9ß²-µáŒÎ™$Ñ‘™ÑßÂÚëùRص¢Ôì?en³œÙFß^Ö$PÃ…µ]”ÜP°o}L1Kß6P•ÜÖ6æ{Ÿ2_ž}èÓÙ Bž)Ì?éˆÙõµ…ÉQÚûy¾Ò®!=Ï/NŽÏŽ O“׌ÎüÊÍ8?y&*èçwEeG¤®ÔV5'á¢0T¤þz„Á¬ü‰ýXM$½ùµ¡•Çhï$ ­Ö)Š}Ê¢f‹)=ä,0r6ßkof#7î,}+Øi¶ý¦Š¥ '%3’ôÂiÖF“b[,…(Çš´1žV¹{ŸzŒ‰-ò­ qRHÖïÒjÞÿ¢ÄPZq+Ý´KèÝ™œìHˆ=™Z.ÇÛh2G«#Eê§®t)çL„*bWˆ:‰ï¡'+Ú‹¨2fu:tVgöN˜×´å‘†Ê\œS¬]ØÆ{˜‹‡ô ©ŸÁ¶Í7pe†péÌÞÆÏz»²XX§y}NŠÄÓÑ?¬/l$Ê(& ‹¾cãÎæŽ€:± *aB¬lŽÙÜâ9±„FÇi?zG ö £y}Xó´ôYØÊâ¸Èɦ1zú” `ŸfÁF!k»ûÖÊeé|æü„°nú¤2^r99U,ú‰éU jæ$7ÞM ’>ÒꨆV"­X·HO“ŠA¶dã7è ‡‡{G‡{ûû{­%,ø±0‡¶ÝÉ ³kug5öÕõTþŒô¢¼(m¼x·pÙîÄüwvTƒø‹Yñ G~°Rl^°±ÌÒ írw ˜wjö…¡ÔYf})ùÜÝ݈ЧujkÌÌ¡&‘M©K›'ëŸÿÙ¼À†)znæ*®ÒëöhæÇ·­Áõagn¦3ó¿ÎÖEá•ÝVk÷Ÿ¹™Íó»vëî|óx5‡·›ç]”r±5³ñýhŸüÝ™;¾´nŽø[>Öº&ͦíÿ{²þõðüNã[ÝŸ_×Nþýb8x¸:ùÎ-Ê󨫺l÷ûÜßߎN$CÄánµzç[s»?{Ý›+›g­óþž;ºÂ”{u,i:ø|yñøVô¿±s99>^D#Pi¥éÓVYÃéXw%óš´’ÐèI¤P>à:/Óª3MÒ›^åÒ„ž¢>*òÎ(Æ!ík­†ì5é$Ó&5˜õµÕÖÐÖœºµ¥ ée¸Åo…kc,=¿Â“ñÀ2µêÖk…p¶ªV¼“­™1·ScÜøM[͆…ve44ï{º®Å[ü¯”Rv€=£®£9”teë£YßÓMò$%-çÌÇ…§0JÏQ[¿2ÉêZaš½Ý¹MÞ´jã1pšFqÝìw bÜÕò[-=²³§™=UV®ÈAÇ ZH+ÖYµŸÐ³ÂcNÊKéüù´=5Ï]!l¥RÞklÔS3Š‘Ìæ™®Ä«ž ´%JMD_iVD} ó¾I'Çò;n´ÆžÒ¹Åõl(—ôÑvËÌ'WÌJX•)ÚL«6#õÏpA´Yö@ÌW [Âæ´Æq6Ö¤¼·žÊ­!ívû•¯HCÇi¥03*¤ñÔ.PÞ§·J¸<Ã:$æi¢\äø? ëú$öfušºo ñÃëHæ8ÇïäwKH8'9ÃòÎËãdã'AÄ–îÕcO±£*䀗 NÂ89CÊdH³ð‘›5tÏÍÍùRÉa‘¥œµ§Ä2F=ýã)_êSâå94ú`‡™oïì-·î··÷ø'âa ‚4L)-9Qgi)ªÒ[K|Db"JMË@ÅA´¥ÿ[Xzs·}cÂø¿8=ÛÚ\ÛØØ@¢¥%‘í³³××¶w~$~Øß¿æKšãØRŒŒD>k·úç›s[çí½‹˜"C?¿ØìœlöŽ×Þ‰&}°psÐÚ[<Ù¸:† Šê=í–hÕ­ÿîw–Û­«£ÿýÓºémÃîºuçt³w´pµ½øálíôêd­w4óa_íýèm·¾Í}˜¹„yʵTíÚ?®æ–OÖ‘Êøßg›7[7‹HùqörwùÓÂ_Ÿ—U“î|¸<¹<^ë}›ÛøÚÚùq¿½|{ÜY¹>¹9xߺÜ]<Û›lhÕÿœo]¬ÎÎô.¶?¯ Ÿyw}ÜÙº>ùq°Ú?éèç½ù°â c¼Å¹<Óã(åˆ ·‰êB Ûç%eÂÏß9”.)½Ã9B–§B~ó)ðGXóIδ¥&ã‡Ád:“Îß:dÕÞˆ¼««dKÊ"ßÛHp †$ñ#Óõ×$ÑùyF“¶ ²š°…ú„VHÁVHXƒJ+ý}“Fí@1%³ä–­Y뺛Ÿ#¡­©Öࣕ¬žM¨›jLöŒuÐÜ‘m`5;¬Ö–:Ó§ÅN:ž‰ÌÆ ö¶âddeGæeEÖû9N üìö‚¤)Šgg\š¨- |¤AœwMOU‰Ì®&+YB•³¡yÌCz1ÓËT”Wl©'ƒÝ¼†å»v½äo:w´ŸFYyTnäºR¤DíÇ0×êÌÏ&b oc«E<·çÄï‡iÞv1ŒÇÀ‚®í"·D™ð”s‹<_s]j%ú°·y`—”y¨•›UrƒcM{VW åÑc|úá᪺ël-Íð×¢|ËÚØ.ïÒˆÎĉÀ¼²°³}Ê+}fd¦ ìoÂ=ö—Èÿ¸©:¹(üo–ú¹œ¨€™Vd`E`V¢”kVLwK¨ëpTÆÕ¾äf ²!<Ê·²Ÿ&Loù6fã«~q¢ÑsÕŸ ?eæú4ÐÁÆLsß ä1n½ñgÛpCSaqól¼½¼Õ^ì´¶·>«-µ¸jUÝ@ùQÃÛRÏÏ/­¬ææ¾v{~®ýýäûÎöÖe7ä¤÷íõe÷æ¶F.HvæçVW *éüüìŇ•Ío°¥>Ž’_¤›ëœEË‚¨Q\<ÌŠÞ¬­ûéVÿb¦sÉËp3#1Û¾„ìèD¢Š$ŒýÓY]¹jl©³i¢ÐWb%#¶­½n÷áò–Ó=x\^dŒu§,\p<ÑFºÜbÖš];ù?¸.i­ü¨3NI.l®ŸÜ Ç›ËݵÍåNgíèjódñè&ªa°×?°^T”º€øì¶Ç…Ê—µYØp¿ß§>=ÿ0˜[\oÉ¢ ðÀàdûËÙ§óÛVïúì|ë}G¨9²ûýlõß Ñ§!æWOÖÎOQ-é‰/ÿm-£”…•ýÖ9¢dkV›%P‘rbz×ççëßVç¹=¼ôádåü䶯Sí—ÿ¨OS£åháäϹ\™ÊôBíŸàóNe–r€rˆèÓ¶+ôi•Dº×ä3”+ôYucœ|¥ú„^Q,¼¸’Rrihv)‘aDGḠRgË¢>Í·–O–Òöñž”%Ñmi>aïIǼjE¢ù‘>F™ç$wš3“>¡&Y}¢Â•%â žÝb8%ÿЦ†öz=̪ž³µ0Ëë%¿é‚ÔíiÔót %UcȈÇp“IÖ½:ÀC_Û$ÅI23bð˜tŸv>FÜ ·ù,ða´6Vëë)¦)ô5×0䥘ΎNãövØsd_³ØÀù‘÷¨˜õ­2”“C‰äOÏ«AÂøÝö¬<ó°gXUò-ã™&<Õò1´Œ7C$ÉÐð½b,ÒÖ|rÃ$\æ%z×Îùض:PŸŽ%òB$93 ’Ú]‰³ •RMä‰È„Xî$˜®¯¬@j˜·9Ó§ÝW–ÎKò¡a˘¹›¤ÊhÈ!aÔ« [þZ ëýŠéÝXËŸ·aeâÊuyº¾3YjTÍÒ(õ¬ž„seåj˜Õ?_ó¬/ ±„‹KϸŽÒ—aMe•"‡“Fað9¬:ßwÖ©úAÇ>b{c„µ—fžçåXŠœÉ TâYX<;èÓšÒTlápU+‚ ^P½U1“&e=âÎÄÂN,-_‰¡pg)ÔAâ=§›Lз,%P# g´e'?1kŠ%ÍY…GÂYMlde㌽úºνÍFTÄžµu‡Ža©UÀª}8ë…â>0‘i~PjâÁÑä&®yNÃ+kë«›[k[Û«[ÛxæÃˆ>m7Uñ[‚êTÏ“?Œq~\XZÉòÙÜ á-äÌ0õéòÜj3ËܬÈDØÑa¶ÐpknV4ZÖ‰?%yÊ\+‡ÀV̮Πöçz{s½ýÎ=ˆ_kß#¼‹˜¹ÞZ{€41=rc2q!gÍŸeÉ 6ƒ'¦²9.^‚=ã Äües!>ê‡%ÌšíÖì,ÌÓq׆|s{;XYz-ÜEÉžùáWö­›ˆ[K€vÏ7Nq‰dge{íìèööâhw}kéôêüÚñeëCg¦÷†»HÄ`ö¯þ¯÷ð÷òìÁ[×:VT©Õ”AågÖ£2$¼üHøå÷óó/«K ¯P„[ö­Îsš\¦tÉNÞZ˜ ³RbÝXC):ª¬Îj¼øÉ"k µañWýƒ» ²Ë4]í+ñš,݈lçtw‹.¬˜z1+K+4 Éš <'Ó7þ•J OÌ©DA¿„©$,Vª2¯K%¤ÕÈSÍ<í ûÍI_ë·`€˜^rÐ,¥Éñ©Ý%u³ô,‘ßúð¬rªhV(€ö†'4¥žšµ…Q·g¤›ÖMjõ9¡[¨•Ä3Ì>bJö‘“ÁŽ–t`"¥°6Whˆ**i猶¤°ôöé&]Ÿ1^1Ú l¶Ýž 9óWúgi4,µ’2¿ÔœùÔ²´ß%cááØûŽv·š´˜Fu\¥d ›ö5<%•”,EiKc»ŒÑÈnÙ³\!™ò ËV[¯aë_zºàˆ å*Ÿ³xÔ\JGä§ñäCÖ0ëAÓÎuõ%?§OS{ Ú°„m&Rdf6úî^C‰Ñ·ªIKù‚vkÖ*!EôHyBØ‹2b¹‰ÞRLèšÙ,=X[´nÏuf Xs«Ô&I»${©¸¬±ETã/Ôœ¢È­®5žtÐŒ&¤A e/K¯јEø¸2le¹±ãÇQ!­±–ËÚçÏ6¸Veå2}lL1,­ˆí-†æ,-п:lµB?FÊZ=I8GÆkÊl>*×%fO[ç°ÕÅ•slaÖZÖ„¿¬¬Ða6}:¾rõ tÂ%¬“mF¶<#6iÚ˜ð0높¯á¢›|"í•խ޲ÃClb8Ó¾ÕaXVÂ<)$mÑû€m'*œÒjÒ‹(?-ˆã”µ²bYVöd|ˆ"íµudäs¶"væØáع±ãú(ò¶çsv}±•ižÝráÈ„…@ÞþÞøVÉO~¶gÆ·Û%†Ú¹Þ…}–›D«t™íÈT‚:cjzבìßußæñxvðœ“'fŸæa~Ëç;”âò¥$fnn^pS(­ÒFCÇ³ÎÆí‰¡·Êt[ç‘Í[=¸îµ Ðé;`ÖýËËûnJ6~X¬êu0ú³E¬†åKXNÞ2ܽÜm­Ï£üÁìêÞÁÙÆò?gû+h)K\Úé^®Ÿ_ m_'~exùð&Ô•ct‡K¿æ(âG:Ñ¡Ú,Mz˜_?YB¿þï&4rý£º³´¾vöiÿâNGøíÉÆùúæ¢è*:ãRVZ˜q¥€ƒç–Ö×Ï>í]ô•›»Ð×¹ÃvEµp©H°a(²pB¬Þkeµ­VÍ?Ãùd¶•z| ›xs” ö­æiW™¶gz5<ÕḎ š4‰Å§‚ÒQtkÍVCÓö²·Yš¬>’A«`ëÜSj;„þ­ßräðYü6Ó®"õŒžämœ%£„3jЉšW5â²R¢§ŒjÏBmÍX{„Oö‚­¸ª‰11>êßAó¦þ5cjçQ%Í i2Ðr‰ßç(™ë}WÏÈ\MÂÑÂ$‡K’÷ Ϭ½ñ­ê¾¶Æˆ:·ö,zÍÖl¡…¶ÂD®þ~ˆI>´6’%ņ¸4F‚v o¦†IKâ¨Cëj¿ïc¤×m¾7´LcâŒtZCŸ$9œ[ñ?|ÚWÆÑíKE¬µ"ÂzÿmÇP[Ó¨7:žáE·ž‘•¤ñþCœ>;i ¼PÂÁ0^ãÙ–ú'êSù¶OFË5}Hƒ>Ì|Lûa‰öUömˆáÜ‘«[)Ƨa¾¥¶”c"m+ÛU“>€Z)Z6ÑÕ¿ÎÛRg4ÉZg¤fÇ0µUæ¬á[ØgÏ|h]ýÚZhµoç–9ÿµNÕdG&åíö´.XrkxöçÖß'¿¾­ÁlúçñÜÊW`ç—ÿ÷qþ'Œ­ÖºíÃúGFQÝ—7»ïMΪÆO «˜–ñ±!FÎ@ô`ƒp[ǧ³¦0M(ôEHä—öÙꈃ;tjœé…Ú.:b:Ò¢-£”›™ØöF´Š ó»4ȃºô1>î’pÁµ[.FÌÆßå’¥ Â&ãC–lúq@-#Ö3^j䃚2g[ÌÄÚ^Ñžð49’œT2º‘üކ5ñYß…±“õräÔØ¿–©®¬rémÜÙJŒ ^¤C¾«sõ émtf(Zè댶®´O‹-² ã¿Àš4HöØ …~`qè.É';ÇňåÃóy¢=ǹY5`ýš–Ó¡«Å\öh~eýUÄ!Whæ[”Ú‰q®õ¸N*ÇÍL~H‰r©%ôåi~èC5}Q]Yל‘&äÉ„±57ë„:s}K.ÕSŶ¾eq&9NÆ,:å­3¦ïú.|쀕È<‹Xc>ƧÉóOäéÕ}§9jr‹=Õ­¦±ÜüèrÎK;?‚H-¯(€‹RÛñyV!åy‘öŒvó%²ZJ¨Ä˜Låž~89d—rNÔ]œ˜SÞЉlaîiç¥Vm1ÚòD­Z?Õñ[aÌÆ—q²¡6ìBÒ-öa8=¬h:1uâeñþ„"ÍÏ9•çOx(ïyþlÀÛ#%És‹çœ|¸Ff§Ïá¤\k°»³·´¼ðamí]g>rEàgŽbJ’r¸zÔãöĽmu…ëFÁÂgk+['§÷··|a§%4N¤·ûª³lovØ ú#‡Ë o»½ÓãÃó˾NÊQÑ _jÜž¸ôë×*z !á³Óïۛ뙎Uuâ!Jv¨Ôkë™âç+­JNV½^\>ì@EÍK®²žb8,ÂØÈ:7ƲDYÖq{VÇ$50î”E•!ëìmùÞ“šÚ‚&a›ˆ ãY¿u§ëë --HUO'•³yîtcÅËA“ª¦ F­·<ûTàR¿ûQT˜Ëýœ¦ŹÜâ]ayÍ9îà­=ò°mÕgú1µ(·;ïîÂ0Ñí4~ áÂP—²z]Ô« ²F%º<—æfj2¨òs`VX²¾ssXE|ÔDmH˜äòº{N½Í¯¬ß½èÊql”†¹a§r&¶_YøåóÑf•„"9<·3Vv6»Tû"'Ø¢în|å92ÔMñ〱!kÆ}:[³Áºè#:CK`û£ZÂfmbÝbš¨jejÔ‰uæÐ×(J´y&5KÄxì…9C÷ƒY-1r‚®Ü䮕ò7L×anÒª? ;êÀÖìÔ³1{ù©&D‹ê°yËølõ5û²T1¾¼²zJLN$±­Afáü=M‹Òør¸F7-­ ÂúAK4½ªÎåïk[ÙêÈáú¶n­R­+{îe'ås«À8­ŠóÏ*èÙÏ';frràO2µÙo¡"ÿšVmò9¬ñ e_É·n~´é7ðgðç˜ÓqÝèÈéÓÔÀ¢ÌV­¢˜£Ó’C¢L{V ¿¤#XDÔ¥8ò3:×ñÞ“ã½Æ?œoÙ›c¦(“U&<ª~;²f¡W­Ñúùk¹¿Ÿžwoo{=õ=Lòó7^8Ü^GU—’í)áÎí…Lѱ†3>Ïa^ÙÜ]¬¿W5íŠsYœøó³sóËKëÇwZ•€ŠV¹Ä Éáñãê —Ÿ»A¤Cðââ¼Ónñ&—áüÑ… ÈÝàÃúZÆßdr¯;Øýó¹Ó­ûãÕÙØMåqSÓ<©çýÊf#•;•8¨Íó¹5±_gÆ“¶bvùÄ>*TÐD1€ âd‚Î+N¸l]S=;”ÃÙWyý&§fµ-a]F¶Çz¼Ž‚àÊ´´ ‰†TÛ3uu²°r“”ŽsZ~\ݲ(soe±EQxÄyÈçP!`‚^<Ã¥¡f™>mR ¦ÖÞœF:Ì4ך“þå&gPlN¾ç{+˷ѵ«°žô\Q౜Önï*ú½ÌW.õxh0´ 'µÜˆ(µ ªhËaz­ÎßbBË¢ ­:)©OœõM¶›/µíx‰4báÂrÊ*ŒkµÙ”ªæn9èD”­8öÃý”‘‡L(PS±‘ÖÌnž Ê :G³ÈiÕª3ûü¸cþÅø†³~yŽtFðZE]¸aé~\‡rët\?Où4¹ùëÑú4`WÏr¹EMe¸lqótq¤Ç5r”àÖ_¹9TyÕÅȈ. ò6àÓÂoÌ1 2åLáö¨Uç¦çLW/é)u0Fí‰L)·‹V¡¶ÑáX£Ö0C©I¢®óÉ·JL«^…D”×–9Þ®Y¡5áÉ&iFâÛÜÜQ#çèÍÃuk.Z•”9B Ë›¨›²`½8?+}êyìلɽ*KkTýâèËÄ^^dØ(Ç ·[Ý;[°n…*uJ »«ËŸç}8UyŒ¨8ö´úaMf•¼>Zha[ðéÏ­õÿ®Nà,£¸õ¤:…×+ëæ ¯_¹#§ÛƬ¤Î©¸ué£1R¡W mqõŒKýLÆUêÓ¶§P´|`;Âr‘aê_¨L¬ÙU(åRŒðB¬b3I­ùûð(ZxN>Z¡ºe=ohV•:w¼¦®2e˜nêGôصoôay-ñÈguÂ2|æy©–¯UÍx̆Z<¥Šçpݺn•5TŸÐ8Ôû8CŒõß,ÃLâÝTÓÅKÅlá·+m½/Ÿg2Z|úš§yÕa|êQÇ•ô©¼z0 ZµáÓÁwaЧíÖw­&u—lþÊiQW“^ wñHùæõ%"X¥µYÐB´gÜúGõ›“ri Užhê3yjªáõyjîUßÚ;FÕR7³ÈéŽ?Ô`“*om™C÷]3]3‚ù‚àYÀ€ †s †pS«Îñ?Õa5T s5ãLÎS*r¬É)^y„“KQ¦5vYŸfY:¾ Áµ|bfÑa'&ý2ë£ßfÊT¦Ó{ä‚Éêæ…IÇ“>ºÎíOj5mŽ`ØÖÉÃÂE%ËšR£;fôÔ—R9©’[«³&ái{\ÂTɹ¦b¸¨˜[_˜0ôia…–ë¯Ç vŸ¥ƒ9º±9zrí—í#Eh"ËÃÙä¼ìE³Po"š…3}ÌÙ5TY.ì–:íÅeE©3¿Ô¡2£²ÇHتpœ¥êô霜òù Õª‡Ió2 ~šÞTÙÌ*«¤gŧÙx[‹ûÊ…ÑžÑ'," ±—¤ Ì*Ìʆ 1¦€g€|Qï¯Õ¹ƒÆSê2ýøÖ‹º\ÔuQ.>7ÒÊ:z-Î46NP·Ò(°OÌ¿’­¢ñÈ^x;iÌKFÞZÊ­ŸàlîÉø'¼õ³ß3 [Ð;©}f^íðWcӻ黑KU;^1ƒUF°¦p†r”oA?6]D§åp=ňS‰ª*­CA*'à´hâÁ&jí*ýí´ež‰”i4Ÿü]E¹y7_I¯ª»éªçõ®oµžTŽ|>¾.”[’Éur¸Jf¡N×Ir|“r‡×¡IY¾ž…ëšPŸŸãrëóNi]DYçôcïuÄÇp ›˜½V{ÐÂvŽm1¥˜·#bÉ;IÈÌÿ âv sú´ù}wœ&¹D/ïʆí®Ã&rÖ#$ªõ˜Ù“&Ô8ÿzŒ<Ð*£g#ýµ‰Ž;­46ã#èÙó[ÆÐaŒ“jÙ´œiÆAK:he/ä9­B{.éÓͼ?äÿ2F>VLþY½aL”³¹FB&‡eãÓGc8^˜g;Öyƒ÷ó³°¥U:ì)Ý猩†„ãWºé¤+æ€æG–’§šAZþ:GY‰¯1Þgˆ÷ßóÏ—eç⵬Æ$i{I¥º•Ó³,Æû0)ãkb´Ò†Dº!ÂgœqsñFá ²´˜ ‚æ$Ÿ£êd’™ËÍ眒U¸²­JÃé°ò¶i9J:§O³÷À#üeüÿŽÃ?æ¿ œ¦£Î$B”qÝÉ1Y«÷çê\oåsÐq§„¤ràç¥FäÒÐRí´`˜;YÒ)GíÁáégYz~9[ÓW†s«üÀ{T8sútèßl08RšT±ñb½c=•àgËŒâ0´ìV©¢Œ&ñˆµö er¦WÜ™‡Ë{7çM¢Ä§y³iƾÊÛ”¦NDZl–öò­åL¡ÉôZ–‘#„zÍê $ÔX#XOþa_‰>­ùT2Š#“qo:´¯“ípÎO…Ž‘ØÞfa•ò"/_Ïs9½« ¶Ë3ÌP®n”96se¥(:êSöÂÚ…cÛ=²RÊõñ1ÊÕ,Ýj5<½ö”JÚŒz,W{ßµ"äã4“±¥v#˜`Ï'9p•¨ç­õF]ЧñžÖ‘ÕdèðFU[aÊxTþ OÒ“§5?%ÛÍœhµDiá}Z=T‹‘¬½a?YëVê©0Ó»BJ†­º~´QOÌõ™Ä“CXŸr¸À?µ#%S2Œß|J—sÆ‘» c<–Ødx{¸¬`ºr›„mŒg_¯&ãÃN:NªŠš gçTL›GŽLŒÚ¹ššX9Ì÷tëOgëoëIM§?Ä–ºá怑p´iSläå±j÷A60=þ_À­=NÌ!îOÛ ç0æÐü!ù,=ŠÛ*Ò0«3§›º6OdÉ«$–ý›Ma¾G¤Bú ¬šôáQ.6:4Ðxlo®°êÊëYZSúý” ½–ôÈáÙ6kƒ}8ÑBK5|¸H!³ëø3-Ù¯ªÖ CÖÖ•qÉŠ ‰†h½¾¦nØï“Õ㟞[i\äxéQpÃ+U9wxoZµH«¾ÎI~!Ée¥éÄ¢$s‡:‡RÛx7* l‹§æÝW¥/°¾<ü]†–!{¼HmV*3…CÍsˆÙžê™­¥©Ç”žÊ€}ÏöÙY=7\˼]F›lœêWÅô+çó6?Ï#ô‚¿Œ;M«Þ/„—3ÃÃAº?"Êí¯2Ž ŠIôsi‚ÖÂ9"œ¿×?ôü_0L¦¾³—(OiÂqñÆPúeÏïAEëHã@å™g;¬T ·ŠÞxܳMšŠlQìÎf?ø}ØÖŸìMêýÁV¡45A^§›†I¦dÃw.qרœårÖõ>=lߣ”žœÉ·ÕŠš‹7þ´tüª¤ó”õ@¯+ú&š¼z´Ø*Ë«t0FÑ0íéYñ*=Ð<@ÄQ1:8ÅŽ5Ž£ÖSÎXø½zü~©}£´3ãhñጾLSzR7µ­fQ{¹AXå¥Fýó’{KD¹WV¶š÷­YŒoQx[ÊÅaù‰Üüÿc‰ŒeårH^†ê·]çËÐP‡¸*ó…&¥<‰L;€L%l±¿"ìņef( Ã>Æ:ذ%[ŽiÊ(Q@nÈ𯏋a~ź¹˜ú°p`\ßGôìÇöLÃz ¬òµü=Êa`_è/¯qòm<ó7ñpäøéð¨9xìÜè“çäá=ËžÒïìëð”954nÄ—²ù¦X—j·ÒC¼w]…”àmªU(ãGÌ^¶‰LûZïp±x¦ÑyZj¨7¹ˆÇ)@ÓŸfÎE”ZêÁ:&ÈEf5³§Ú‡¨µ´a„) «NŸ¶¶x+jWqß&¾¥¶u¸ }:¢wѪ2œKs4÷ô¯ › {*ÌÁ¿)q¯¨Û9½™½![åº)r9T†¼¬¨ f ì„IáßBnåãöÙ´¤)*‡Ô#j êcq,Ðï­Ýá%WnÐ’žVCİÝ3è|´¶·SjØî]Ò… qkÕe£Î@B«ËB¹z’AyÛnnb#–Ö$´BeÈ&¢]Ñ6®Ù§Ö›nŸ®~\Õ”õé(nØ›!ž£&kELä[å¶Ty5tÈøœi<ÏÇ9WsóÊYM8äÀÔÌ­¤‰{«©˜2fÉÔŸþÃp˜ømVŽÏÒØÙW,9hØŒÿÃY‚œVmkKKÉâyQxfnBÆÕ§³»G‚'7ÍSë—·¥®¤`Õ¢šÖ.%)*¿áI}š§…+ú¾¾ð¬z±ë•騭%!–‹a‹âÐȘ®†z’i-Óì£;ìuZŽÇröÓló¯ÿÅ6’¿Ë8–Q®¢‘¾;‡wX '¥$iëÃA²—hf Õkµn¤v9\BÝšiÒe¬ˆ3„ÿeºH=GÕAßNúÍ2ÝÅí7áO×ï}°AvŸ Ø¡Ÿ Tž½ žqM+ cdü§ó7˜/l?2Æãß`f×ÙE9/F›G¢´ÏŠ:!wý Õí¨mhgS“(ãÊ¥,(31äA½,ݰ>Þ;ïúñ[G¥¡_UÊ– þÏí”ûžúÓÐÉ3ÈCÓQ‚¬¦fš„ä`á!|× ù–夵“ÜÂá•#º&Ÿ*zÙ:4Ÿ »s}[±&4ú°½œ_žW¸0ãÕÍ„µñ”ZÅ™ÓÓ3‡ÝV)%Bè°nTí¼¾ï¨ú°gWƒ,4Ñd¯öi9\D‘CšªøÑN¸e<«*£K«¹ ¶ÔñDJ _µbʯ|ÃÓû1ºô.ï—š_ªŠe]\®èn ¨ZÙpR‹:ë¸á²FûˆâH' C'Ã8 øQÑZ–.Ö–xïkŒr:tØ@«8:ÈrS€©]®naˆetÎiA{~¬õ[p4;´ ¿Þxš¾vî ÒÙøÐl»9ÊX?Úk äH)q$™x‰ñ~4,áM'óÈ\n6h¶þ‰Ÿˆ<àÒ ëðä’>M9c÷˜Êø¢Žkk3³²¨ê’ÌŠ$â=:ôômòX®³påY.“‘#ŠA6­šV§Ô¾ÝLöÈú°¤=ü.}: ¼€å´êœ¶ôš´í) ÍÜ\QðÔh6NG R¦FGôö6ùqäùÇ4ÊÏJ­´~ zvóæ0 8§«ùy¶B=«Êǯ1H–P§C?U—­AFÖ‰'“ñhnØ©b}ˆ;äôÈáö!e_~ ô V.ÞYQyRæÈkÒ#kÆKÝ1´íœ®+Él7#w›uÀ7U*så©!jÒ£…s¥´ÚóïZËôøQ´¥ÆÕê‚ë4^¨<Ë”9Í;*ͳZ¦ÖêÐ~URÖ9X©ÇŒËÕÅ—U53bØlÓõ«r¸ÈÁU¦w™^-È^¾GˆÜn7$!xÓaMØšGk?ÒuÔðc=“›éªòÏÕÍ–H¿u½0j_¿…ô%Ze\Wæ·l[¥Ì'š‘.‚mëOY“6T~€t÷lŽW‰IÁ„½-dáD)Þ… Î}XJ”‡¦y;èˆLg÷F-ĭ܋؜c‡Žº1UŽÏ­ÞýJ¾®‡†úçÿ‰âÚÛ\F5§³åù5ò¦ÎO×*†¯¡þ¬Šûæ%3lo’Ý´žÃç”æõ7%§‘áóàËM“Ó GÔ žø­G0]Øð5‚¦C÷–ë0…&{Ñ>_—ºx‡ÕVëv>6Sªó:a“xÜÌÝωð…w-w{â]ö7¼œŸŸÿ¸¸t¡¹bø”…ÔÆ)òƒråïÃ^ªKófâM* ׊JÚÃÕ4¾Ußru®U‹ŠdŽ:2ËWŠQ²kVQ«*Õã²u±êÓqÿ´¸0§¥‡yq×±T"@ %[¼……ÚDƒ'*Cí)š¯sÆOYÞøc‹R|¢CâçÏZÃÕ•åõõnŸv22|çgJÝ…ºÔ$¼Øú¼w´¿[¶­ñv6)œè3)s‰jáì ÿå§-aªÃµéë–¨uy>¾¤mšÂ/q›~S•®Îâ¥_k!g;°wlx$nIíðá ®Êv{å¾ö”ñ}÷ÊŽþm]ŸæàŒ­•êlO›÷ûðvŒšÏ¤øvtêŽð…ïuc¿"MÉNtˆ5ð£>þ·~<ÖÍò¾F_Å}ëÇ·]G ð„“Ž*oqa… i2¸¾ö=•æëIÍ×)ðÒîþ·ƒƒý^†º´f[¸=1xüà–*öÌlÕi‰•í«¦p°NK4™o»¢ŽÃ>¾.õ†h/È|jŸÁ$mxzæÄx6<,Þ§ Ã& £0âà†V6̆N_¥M´P«Úöºô…KôÑÍ:XmftË…K½céËÞš ‡ûZϺRr}íkëÓä[‘ë¯R¿ÔQ2³Ž}íú1§Ì—zÊèY¢pŽ[|9^*§É•U溚~®ýTpéÐ|Üìð˜ZåÛâ)æGA©žKëúkØÈõº—ž¨òw»ý¿……¥«›«î]&CadM”:­EÒºöð@”ûs~_ ûzæ°í:Ý`Tìä1czï‡ã |›{–0-{[F 'aû1W˜Cp¾Fc*jaÂúw(äºàÏáÙÓã„·’³Ã)ëö©f¦h¿k8GÛðóÓ¨˜JYÉý1M0ªîå¯2níç¡I`ÕÓÀùñ›ç¡|ÒhïâOôìs/³Éx©kƒû¶bŸ¡4N›cÏ£îLƒ?%Ϻ±ãã]˜tʬM nÜ8õõcÿùrK>i°ÿPU—Æuy.öýÛHn¼d|´éy•ÔÆiâ÷Ûûߎöq™`Ü%”z¶Ô¼§ÇüRi8®¡ßêú#Zk%¼ù¯õ½½ls|ºŒmçó©ÃTª1lÿ­ÀG¶‰£PRÀ¼ á8‚ªÒ4Á=.h¥¸¹ÔãÖùÅB†dåÐk‡€V`H®¶ñmo«Ä°½u †WFs6! ÌaÛÍ1’šØÄþ^»%ë»I…ëøad\¼Ž¯ž?jªqÄQé6üŒDiœ6Çž½\jnÍ~¯M߇.rN\Ñ(ãÇ”—åx?®ýNEÔ# ˆui#'+ʹ5ÞOŽI†Cçå^‘—FÝ{Ša—mÌþðœÕ\v%ýê%Ó*Œˆ¨pgÉ£ÔÁßuD©.›Ö»ÏrXcsÌïׇãítßí÷ˆ|šáIõêdò©ãÃáü×ßMe™\“ÿs›ƒR}F–·#Žý‘ÿ#¶Ô¼œƒ¶ÔaØj~øšoiaa~~~aIž 3ýÖÖ±^ÄLë-”ÅrËù#ž|¿±±Áú nccÏ÷÷÷ûûû«kkhßÚÚÚÑÁÁàA.q«k—/ˇ#M†|;AZÅö–ÛéSI“§ÐjbõwÈÄ#vuM0’ŤÂ(U³ÇÂ=ÖâÏ­¾uxR§lŽýÄ—ÍP%tÙ§)ÛVZEJógÙòò ;hBžJQSîm€Vú4Â%D3‡[?Ïnn5;|?¤„™Õú«i@¥VcÃ5õrf`|ܽ\ÖÐñU+—ö F– ñdö£OS°b‚C—¾­¥컸*°Ñ¶º×Q°¥¶ùQl©—–®®®nïdŒó~Å‘l©¡Ñv»Ý!ë¶¥¥¥ÛëëX¿'®?PÛëëëGm‰X+_Ö£õ’'ôòoß¾ÍÍÍ!Í}¯w}{{yy¹··W×_ÖSÊ}"­"ÍËu(ÓgR´šT›çS‡>ÿqk¯K—±j¾ÍaÛuøYÔ<†ø%(¥)ZtD›Ñ2ÚýübšóÛ#UÔÀÿë0Ý1ùùÉåN{þS\áqèáu¨Ã•kÆKÝx¬›¥µðounxú6ÙhoÇíŠL–''6Æ_#æú¨N•ú¢À?´¥.ú¥[j¹è7Ó§õæ^ýÛñÍ0ï—‚ÇoÓB'ÞÜÜD¸aþ®b>À’¡û.¯¬œžž*0rE²[ÄÑÇ(·×ë‰.¢rs®ÓYYY>Í| aooo/,,lmo£HãË*”±aÄŸœžâ:øÕÕU\TÉ6ÆúÇz^\\¬¯¯ :¾¾.ík]ÑG¡Ëõ×Þ´>E=Ï/.–—ÐeB+<›p#ÛÙìÍcl¼*Ã`êqëjœ» ÿC*ÙAZ>aˆÆ)—5ñ8–ýíã9®=æQðR|¶íеWÄ¡£†WcŸšC7ëÒøø|±lIÙÛ.aœãí:Œ¶qü˜üÜ8ÿGpå ëZ{å2Æ\Ssß³›9óùÔðOaD×"÷q¾{tWÁ‘ã¥b<–Æ — >}9ìǸ…+÷ٴٱíµûf•­®Û‹ËÇ×ÉÆ"&]â¥:ÞÎÉá±æ¸‰ß ñªÏxºÊ ¢[¹ÙÖ³ý—´\õEùKQj*ÕQ¢áË¡aý¶"M1þðð$0f(ÖGG‡ó—Ò«ƒ¤AþÈùò¿ÿ5Ï×_qôž···c”»·³ƒœ¡éBëk5æsxtÄFA›?::B=}Y¥r3šôº]TêøÌH”z¨ÿÇãÏXèîø]_ßîîîþüù³ šôEÌ¿\mWVOÔçòòzmmÕ+´½a¿Œ[ç¦ý^Ο1äa¶ú—â-çaýØrȧ/ÞìS]ž>ÆçYÏ96Ú#æÃl¯ýêÂN p©]¬bØ.Ê“Y¸*Ÿ(Cd.ÏäI}ØõQ–ÞË¢B˜½ãÇu î㛄]¿Gt BSŸ4†ú;ÇÅïlW®¬&ôoЧ6E¾Š}Áo}¼ã½ÜøuiªãË|ns¢òÿ¨cÇ*?s£Íèã×q —äI“øL†TÉÉ&òMÒÔÈX/KGÃ5cðwŽ‘TÖK™ßŸI=g8fM[ÁÁ Þž(QêýCî+ŸñÈÁðu[KÐß²-uĉ£„à íc¦ ÇøQ×%¾,o|yyñþýûöÜÔß!¸ìå®ml@¡L ½xíùéi¬óÅù9¬«A:7çÚîñ~Þ…ÝÈÌ 2„ÚÍùõ'}|ýñ'ôuW°ç•V–¾ÔG‘>Ögg­[_ÿˆ.c=Ǭô¿u´ )}em,ãU£bÕuée˜9 IgÕ Ì©.¾î[NÕCm¯m6÷vœ 0lU[Ø©Œ …g0l•-YKsáÐ"5"…ùôüÊëcrñ®G|/—ÖÃP<õIi¦=JùÛ ÁcóS““kiëzª¶_JûqtÔbÌnDÄ4’à·¦|UÇŸŒ/óüð±ãÒ—GGÝ^SÅø-áÍuû]Ãeˆ§^5ÆÜ Ÿ.âÐ^~–%pyo°Ä!&«kÖ«Ït®ùíã7Ñáê9[êhQÙÎPê O‚Àaņ$d ”BÀ¨Ä€£]uOR9{/z!ôoN@Gl˜yíèËB8ææÎH§ÇYMÊ€»Wà£0íN À*æéù9k–{ÿðû , bؾíuáò >Ö_¹ÇèrrºAÉÞØX»¹¹o}VÕG–ÿ£õŸ›Áb¬=33[g½ŽÚ¿xIðXYijóXuçŽù4Árü·eúÌIçáG0'Ÿ†ó¶Ãºfà^9ÌÛ)u˜\.Þåo4tõaÊ\¼Ï¿ümk,~[Ø1pؤ¯NYïqñ¡á&ã·v\7Áeÿ.þ”v5ù–½æ÷‚ráR¯U¤/áÐ’ÆóR'”Ó°¶Œ÷á˜Ò†K Ï—ô᪈áØsé ?Þëöš||ÝØ¯ÛïªH_ [àùqd—“–u{}Mö+öÙ¯˜‹3Kß–û%ÑäwÑ$Ô6ÔEÎHСԺšÏ$AC<²6©ê ™›ë@%UýüäDãd×^ÈZ)ò}ÝcÃ5õo݃õL5`ãÁúCãÄB-b»ÂhÏÚîéSG+Ò!Ö_l²Cý±<ØÛÛ‡½õÖÖçÏŸ>Wç!˜ý£õ‡Îº=p¹òbíÌ*pk‡4È8ܘkcSÆ•GŰë±XBåðì&x•ß2B}nx3iLŸC¿œBPϹR˜°&íó,¡éµdÈ¿klŠAFlÛcÕ­ ×aÛ5˜«ç´áX8éVN5†øv¼4>ŸgE¹Cé@ {:çÂCé“ñF–ìßë_ÏcuùÔ¥)ógÞ®Ò kâòS¦Ÿ>›†‡îYU`Ò~ôÕ|û8&íåO=>]Ĥ½ü/ÉFÏ¥µò¶4/¿Ü9e¼¹8µ÷ÕÐ-ŒMÈøp(5âdpÉ?òºñZ°Ï.ÆÓ4£ôææˆoãü›®S¡ìҞȮÇT¸2†qаk kfäÌCаþþý;`iÖ§Qâ±ZØß …²òåÃþQÒx¬?‹Fþ³³³Ú=6 &1B¸XnÌ¿²þãñVëûÙºo<Œ|”z6íëQóôÚ‰GJÈçv]|ÙÞzT{DŸÞÛwæ1¤&XTEÝ{+ÙbÖáÖ#(¬ƒû ®>å4dã°F‹5/bÞþµÛˆ;'‡cä±ðŠ<‡¢×>}AÛÎdi NœãÿršÆoeÕáÊ•XrŸ.ìQÔ¡ËuXr© üPäsÏ-%(soŽ÷<¯Ö„kù¿f/è‘q˜˜ÃµzÌ2g÷¬G5ßz™gX ó)¦©éS/Gŧ+æ ¿‡3Âü5­yaÔy$¥¹:ÀÄû.ŒMNN6[>ΖZT }•ÙRgè‘ΰÆráܺsXmT_ÓÔ¤¯ÃšáÙ9œI×¹Œñ¨Ã>~„°Nê–Þ–ØÄ£‹¥T”K… gKZIJÃß55ÌJ2]%àÜ.½K£w¾ÆÚf5ÏaÛ®EÖ’!Øj|º~'!Wîcøk¬ù0¬×cÞô·cvÆ]ǵñëÒXúíòõ© ÛÈmL\Ý uðýÒc®IoLg\ñ8ŸD(óšryÆç&~ÌÂÆçn\”ÇK°ÜÎèYñÆìˆã½“ÎÊQæäñfÇl£}úaú@Y&?Ÿù¢BÏKWIù¼â>Íe›;! á—úýÕÕe·tíA«3ÛÚÞÝ;ÚßU[tD®hSø÷жP¬aOòÇi>YŸâœ Xµßƒs8Ÿ.9•ÛsO.Ekâý±4QïÌp,.fý3覒¦ô–eUäcqAWiâb9Ý>eÔIbúòÛæ1¬í?¶?öYÔÆ+áý·ž^ïÓt HÓª,wiMŒJ€—‡•yMJi|ú&ášsZ)Éb&™3®5ÿº¶TäSÁø•jÊ ý"õ¬ë»Gú±4*Ê}]Ç?£òجüÔ¤~ìÔ…kƸɄ&²¢NÎ8™ãçô¡ò-È4'}z '=áÏ׿G?ùãóø3¡sæ—Zä›È›…wí¿Å/5‡ã¤G¤ÁY!§ð´×[bãÃV †‹‹‹‰þA{˜æqʼnz>7ÎÏøŸœÏ9ÃÂ9;ìL³ o3œ&|Ë—µs7Ê oYVÎäp)Ó˜¿[7Ó ìR)ßê¿i?ÿÎámq¶Î Ž Å±×Rñîñ2BS¸+q‚똺6¢Mîq|”†q͇›\ L£!Чaã"Í¿ÒÞ§Ô¿á]•©ç3_û>‚ÓD 8àÍF“ö⃮ÙŸ–¹³ÉÎaET"˜røÏ§i~,¿¦ï›Ô-—WÔ %øøº°ÿ¤žvåÝ)aÞÌ­œÅ‡=ñb®Ö ÇuX{/§äôØsùOê[WŸLï ˜w™Ãš`ÿµ|Y¤íp6¯O›²^.ÝÈ|XSʨc§IúòH÷²bD¹áåR@Ô–ZK)½e|…쪓c>ÏNûùo‰vöŽºý8ȃÛé‡ÖÒ¼ ÔÿƒOé_7W×wœ- ýÛ0üØÝÝÛ߃‰j)Ÿâ®w`­†i/†gü.v-ÿg«ÇŠý÷Ú½û2}jó©[#U®`‡¬Q}úÚuWݾí¨û¹µóqIà\^;¯×é %Ý¢ÂÖÅk$å5ƨºTT§È5É¿IŸÿ¨uõÛºúŒZnMzëë:½°®ŠýXµ­á«Òú¶n=ìuÇ ã¥±P7rËñ~œ6ùª&½Ÿ&%¯Ò÷*渤×eûÒãëo[»Çßn{}½q\äÕB§½ˆ ÉE' *ÎwCPÑ/µŠ¦8S˜‰¯š¹3éu}mED8evØÂYžþÛœî+PgÄŠ 2¥Þ\xfzF>}Ô²ôš§>äIÖŠWï¿-äó÷—JE Õp^«I8&êjh*¡Ù‘ë[»GûòB|æ$Ÿ¾ÑÍwu*2K·T&ò)|=ë¿ÌÞÔ¥©Ë³Iþ£~[—g~¯#Ÿk5µjZãû«.\ÑGÅ~¼Jvpey>‰¼¥qÜ•ãI74¾‚ÿëø¼ï¿Íó_qœf²ÂÉ™Â¸ÎÆ¦ï>}NFå|Ñ:YTemjîåC9œ×ãÓ|÷ªç»¤Ïج=:ŸÓânÆôiüEñ“ÙRËõøÝ¿l\¥pܽ:S¢I¢‰³\Ô±ó$?ë6: ZÆp$VØp—|øôÎÙ|ÓêÑ=kÎïçì¿GMS´Î—Xõ– ¬öñ ÂÅÖÕ—e&%[ØÌ´B+Q¬O¨«Ÿ–®lYëÒûºÅFJ6>M)\AGŸáßV´¥®Î5yæ¨äû¥†¾]åðð¶xÈѪ®ßðI~¨.+ýpÜeu« ~RbÅØt3HÝèÎÅûô û9¨´3×äyýø1•æµ4¯%hÈ¢C·aE-¶Rî,G¥6Û‘X>ÂÔ¤šð@#¬ºÛ.aW…1•ãù?.áÜeä›cÿIOW·ˆ{¼¼;WU)à G 7©³¡›e,ÓÇPt8h>Z—~hüc8ë°rý·£æSÑê±Ú8†æ_Ñ–~+î«dðã|RÇoÃø°r¯¦«®Ü#ªÆ¡k0fãöšQ9KÎÍÑ#ÊœZ©ÒD¾¥4iL<ç8A©[ŠUÛœQ´¥Æ ØRïìî–ÚÛ•¦pîË'š¼XšpÒ—[ÖÿÄO3دIØ×1—~Uïð×ÅçÛQmC}Ýš|KâDýÃEbŠ“ðZWŸººåêãê–ËÇ×¹.ܤþMòi¦ ÍŸDÏRùþ*‡›ðçÕmïè¢+æN¬¯ŸÔÆ&ý•Ò¤3|‰þ4ÌwZÛ+³ËósN:º.·µ'¶ÔÝž^ã¢'áDoÇÍ/õ]<ç>˜ŸmÓ/u¹–Ò$ ¼8 Péy÷îÝŸP§'\æÝÝתs:A]ªNÿ/ž¥[ ëž“J?a¢N4;߯Q3•>£æÿÜÒ7i¯¦¹êövOó3ƒåù¶¸haùHØŽæÇI0Öw1Mò™læV›ç`f&¶Ñ‡‡¶×òÔô)üè@Kt:\u½‡öþF{y¾3¶ö²³ÿíè`¿×ë3ãÑA·¾áÛ7N+Nëéó›‰c=•ï£ûû{ø(ôZãØãª`‰ñûóA+àxÍÌÕ¤nÍýˆ7Ý{ù½išàÐMj5©|F-«ŒkÚºâÙcQ#ÔÓ­ÍšÐç7¤Ù9é‚ÀÇÛÅ:s™.žÂ%_:‰>‰7”x`ëèàÏáæüض[»û‚R÷a­ ¶Ô˜7çg[¸mÚ£Ô¦U{”š ¢^‘À§ç©äþÞZaÎè÷zP½þ‘h•TÜÙž[u„µÙìììof$驾,$cO¡ntp.«Ëd½ã5]¯1·Ûh  E­ú1Íx8ž]7ÇÓZaŸÝëdú™ÓÃʘw.ÿQqëIáßOÉç÷ œ?_Zcü¸¢ªu8}“øš4«ÝÍåÙ­Õ¹?O™TƒDD—Fã‹ÞÉÕàbw~Èúª® ÙÜܬ¼ÅPjhÍTœ[­…Ù–ù¥¾¹¹ºíf!Ù6m©‘ˆxJůñHa¡´Cèj<–hUà ð Yj® þT}úðï)” Öê¥>½¯h¶¢H2*¶5w—õ~jÇ —ó–3‘žÞvM8îêD³Ö/¯óê[—«9ïÙ2­\+*x Þ·h{åoëó<}ŸßM¸´I>ù4¾nY¸ŽnÞs}šêÑÔ€þž¼Ot‹/qQLÏš+@$ÕJáD‡Ä‰šò@ç1çïAxB“Þ?8àö/câÜê¤+P‚í¿h"­w*†;ɃÙ4 X}ù NBDÑSÉ)¬tˆó±’H,d­"oäiBž“9ïð“2znòá–'#Y’¦îÎ9ßÞÿ߬Ïßc¥OÍÒÔûÎü÷ ÷ó=ŠïB¹VO_çáeE¿ÎÑ· 6Ø·«²ÞaÆ3õ´ª¦ÏÐ~ñm‰4Bg´ÂܕœÃ4䨔æµ?~@ÍÏw F¯­­á9¯›íˆé——ùTšíBŒÿ ÷¾(ƒs‹8½ÈÄó` ¢úн>S˜tÍ,8÷H´*òØÄô<Ò*"©€\éÂØ¥®@LK¨ç3OCQj¿æ¦¶]®?ÓTàÙ>Þ¡ƒ/,¯C*0ï¡ùø¬†eìÓc“C±êGpMÿ­;¯™£L)Þ£æå¶óÛŠr‡rŽ}UFGǘ3<¸îÛØv®®£m®ìiX×w“Š­3d:áÓ §O­Ú÷Œéà¢]Z‰[?ŽºPÂÑkqèòÝ™£–Uƶ‡çY—~hÁ›ìäú7Ÿ>@C†UãäÆÁ·o8&±°°„çÑñ1Žºü~ìvaiIÇ»¶ñÑ:Cý‰ÈëZÝõû‡‡‡ðÂ^ÙØÜ<::"9Íß§AmGJ?<ÿ:š4o{Ì<0¤,»ÀÀ¬ðE-ô…ï¯VVöö÷ïºÝ˜>CövwA7ð‘ÇðÕõõu¡Üï''uíBOUì'8~6d×YJ±a,ëÍø?_ÊÇ‹”hüàyuy‰]¤ÏŸ?“ЧÉ—××?~ü·úþýÞÞž}Ûnñ0„¯ÃÉÉie8[g(µ³¥ŽëéL/qHªÇ³›„YŸ§`ÛÏÎaÕ5ößiJعoc žršÏÓãÊu˜t Ïömn]‘¦®¬Ž[­[]YäSWn k“~2=sû$ko´R@©>‡‘…uqqq{}}zz öààÈì߈á÷0´Q<œþÀB³9V4…UáHtP $:üƱi2i^oK ‡`âæNuZJ$„ƒø8#O)v NÆÁ–Ún*çŠ"ñðRm·Î¤Cñ*rÿ./!¼ (P:}1ýo‹—Z]H½Pç¶êÛzÃêFœ²Ü®ùx[¨ßJ]O«ÍÍõ““T ú4:æøøhTZÅ©lKó:³Üʶ×GeÞ`>äEÑóÅ*©VZ¨?Ò°¿ðßósè—û_¿2M¯×ÅRmyeåüüÔÃ+ž`ýÁuþ¸¸¨kÙ¹°Ÿé@^7|WÃè#–ˆÓ»¨ R?’òœu»¿ø„?±rE˜åF„XÂ¥<% ܬ®~fz04ä€?;sskv¦XÇ ¹ …Ç|@õR±™åo<Ü–ºd+f’bÄxk#¥LÝ· nЩŹ=Æ?æÊvåòog¶ÞÈëãëÒø²jÓ+ÇZÝš„Gnûˆù»:<¥uýØ„ž9^±½a†*â¾@©M´Û“lVÒñŸChËKK@š–Q£ûNX¦tYY]Õa.)/¯®ÖÖç^[]=×±xlmÙxß–33š'Ò`T"ži˜'ÛÃsŽú ¸a”ˆ‚P‡rž÷€'˜Â4÷÷VOÄ#Œ×Ö×!sÖÖÖEú m×ùù µ„‹ÿþ=ŠDóCóü¬íçDú%+Ìqˆ¡ÿ߯»$µA¯²ÆPj^Ì*(µ(?*#ª}#ˆÊ.3½Á¬¢'„c2boPD0ø¹.Çe(»»Ÿ¡ (ŒÝÓ$PÛú;;;g>x»»»çRJRhZ››««k‚=œ3%å ü )myjC$Ÿöx¥ò—ÁkhÛ!ýùÙJüüsV·Ø®ÊúË­9e[ꀠÄúôûwÁœ`´A+hc ÌÆÆ&šåÙïonl þòòJ¸ºŠ¶C2"ŸUÅÎW¡‹Ì­¦Ò@ üðaT‰bm¯¤O™nX’¡×” Ÿã–Ⱦµµ­•ÙDwÎ æ ”ú{:çóŒëàư¨êþR5R˜çêÓ¤9>>Á¤¸¾¾†BA7L{ûû{±-0¨¸½½ŽýŽz Ÿç\}Ž‹µO°¥-‡#‡ùH[B,³_¾ìp­HíO OHFUñ'8ŠÛ¡&äÃê<ñÇLsqq¾¾¾^Ho#¹Õ{@ÃŽõA‡‚fùÇš—l©µE±]$ÌÒ]ŒJºÍÚÃ>ÞtP¥gm8ôÚ°4ÒËÆ¥ÌŸ» .OÓw9rËñö•æci†æSNï蹚ա.ܨí òÉå_Mó ë³EžV%ºY?ŽŸ¦.öH¹_*úÝÕƒ6{¶ª)*ÏÃ[+ûû‡bج0G9JÄp¾øï?äýý}æé···M0ì˜q9xŒwˆÏx"Ìñ:C½6|ÄaÑL§¶ß¾}{ÿ~•ù€ºryÄI9Ϲ¹™Î|ëm‘ 2¯µõÙBLg~~nffoo D0™Â =ó¯k—\QqrbrÈÕÇ(ö§pÁTnê‹gÆQòŒ1Wr4é¬Dþ[j*؆RCI´pÁ7‚&¥#Cã`Ø_BÖD¬z!äÒÿŽ¥þÆ~fe•ÁY±þß¾ ¼ ¤@²§s.¶T¥ÖUß: s4 Óà(˜¡.=^žþˆôŸ:\ÜãÙuXx†‚<ÒF_Ÿºð#íµzr=W5Ð'O‡:ûˆã%ßqÜåâ­wÊéó#ŽC2_vvNNq X‰Äcm ‘…·°‘ÄÁ²—êðœ% x2J6ÅG™Œñp ]Â&r"Ùúm©‘ 0vƒ-5Úo\uyŠ©‰®Ï±ÍsA–ެÛQ@$À†°6À1¼]0csR"koŠ7AŸèóvù$Œâ§Ì•”cq¨)W)J-úZD©©wËÏáyCœ†sm©7ýñãú.¶æUg—42ž5Œi~YasÊ—o!Ý N3óÄ[H ü ZQGÁ'X‹£ºØ‰,:ÓL…éÐkµQ»-äó”¶…ü!à°‹§ÈÅši]l òË×M'V«y¹>…²(ë+°Ï<­ð$/c ª¨3Äý¶ä”JøÁÌaÐâ^0Kiõ>ªVÃJ­f}ü³’V®½³Ò›öÕ¬ªâ¹¾óýèÃ÷½{ö tqôsJŠ) Õ&ÆÃô°žeJOç<}¤%[jãߢ`K}C lnFÞ²äøÄ·e}mýû÷ï(x9¦ÀºvY|i?«åsþdXÈø_cCXŽ¥‡Z EY4e=wÑ)æS™g!iVWßÃð©ƧغàD£œnì÷ÃÉËbþ¬bäCÕo¤Îä—&å'Tÿ°:ªÈSøÙJ)cá–~hš°S7$ŸQñfßöQÂ*%š`çàÁŽòÕûAÛFŸBš"OÖõoóx76uÄÙ8•0耡££ãC¬H56Í×׺²ÕÿÛ¨‰£ÕÎ IJ*å8ß¡@#vñ‚3[çGÉàÊeY&1X. ð­·Á–:Hãê<ÅÀcnª3¶vçæà)¦Þ'ˆétpÓ„ø#Â< i ¸SbÃve©TO“<)>aÒo“Âhs%Æ8¶å¡‡ëÄzEöDHã1Ï/~ e@¸t<ˆóbKMŒGŸÝ¡È?‚-5ÓéÔg¶¾7„8ÆÅdÑÒgfú ÊTO‰‡„PëïÜ·H‰º¢¶¢©«%ò„ÙÞÚBÃÖ77ƒ­$ËL)?ÀöŠu!ŸXOUÿ¬Î‘Pj¡‹ŸžŠí/ãµRÆvl…vUÖ§PiZÀ>Ë´òtÉQÆu€¡ðÙ™„ϾŸ)œ/4…Šª‰Új¥yÐÿŒªeZùöÖ…cý}žƒìV{w\–Ù›ëImïîí‚tXÛ[v!„¼ZËíâ¾,O%3×Þ–ZæÏK¾]­ÀÄ•ý˯0k÷…Ɖ«æáÀ.Ç{yž–·ž {/: ”Ñ–ÚbëΈ91 èõ™a¬ü0Û¡«Ë¼+ùh­jó,”…t[[Ÿ±OŽÆ‡Xà €uó—ªéêeum S;lZ|¼¯3Ë|hc9ÿqíÇy“á&¸¸õàpúDjDÏÈÂ!#bê“Mo5•oþ©¥§£O#š?…'³QÆÝ>ðéóSz¢Âq¤† C,ÇWÅØ|ƒîHix6 ÃPqÀãXwíjÇ{A 2°cÄ.kyüVæ‰e¶žÓô86‰°™pèÆ£ìßêŽræÑvdE”u)¾,«MÞ Müœˆzز‚Ð… ¹ÌÈsfÿÃS30¬À[ŒÄ(<¼Î,r¶Ô¢¡è@5¬šÚFðKÍtJnÉGP1öñšÐÒDA† ÎLµÛPGDºAq»¿W•QÒ›Mê \öðˆŽÚÐ>±Y®*ïœÈ­Ãþþ숵¥Ù¾¬X7~ëñÛPDÎ{…¦â[:X€Þ»ƒB»*ëÃ4±½Ä„ ~$Ê´‚Xš/Új» A…Žlj>4D‘„&ýÛhõþõehµé¨>§vu´òí­ ÇúWÒmåý{,9@mÚcœÊ9¥%(¦ÑθRN®~ø€aÈ¡mÇÚm˜aÀp >´°€GxÈx'¢-õ‡ÿàž,òãáÊ<×Öäðk±†0Ïl =ä êƒ:`lÞ./ßR8ÌÝ~ÎJáâ<ø¦øÄË[(ÏЛ‰ÝòYyÎÊx¶gß…0Þ‹3jHXâŠ}‡¢`Á~ìó K¿n®®ï”ß4e§Ý‚§ƒÃ­îÝ„‹*sšƒéµbÿ ø[çõ\<Ò„ó=rÿëÕÕÍòò¢‡ÌJzxüê)g–[€c×VE‚Î1„È4ÍÅP Kp8j(iœÕ[Z’³ t©{8l“Õ£ _7†¥†8ü§aØZ sõà-^p,¥àC Mø ÝßÿB{ƒØ®Êú”Ú+ZbäݱŽV  EÓz] ¿R.ªM Pðéóóo°KJJOdZÝÁã?Ä‹íÊÞšé\Y7ßÞºp¬?ê`=§]ªF{¢=£U‚색Ô¦2 Ý)˜=`>6Œ†à\| ³§>AaôîÝ;häȵR ã1ò’qKà«B?âsß"Ì(0†û"Çc¨3*Íž:­ç½È«œÿ°‹ŠÍ\4 X5j® ˜à'óu 8·Ž±—ôDC0²Œ9Î9”S8ÑáõòÀÚ·ÞÆr{{UmÝܔ•órNÖÈÛ”&ñÒÛáã‹ÞÉÕàüsðÐjþ8Å<á1Õ’ kvÃ¥MgÕps ßj[{û8æÔíA×ôXKÏÊæÒÿ–Þã¸C·¯ÄÄ›‡Á|§½½»w¸÷9¯ … *ýK}EïÛƒÚLŽG+êÊrŒ&ìç½êfë ½Öä=Öf½^°Žø­­^ïtp‚Þ÷}OÚ/ì¨Â÷"^F˜è8ùðÅ­^Öê%ÕöùP`õ »¹2»µ"[±é—(((0Ž/ïO.û»óQ¦á8üÿp>ÕmÝy.¬?‰Ã"áÎÞÁáÁ>4jjƒXÚ/¼k-..[jêÓ²…l(ð<SL;"_)Õ0¬KÌ–z~üH˹,Ï×VÛ‰œÇpkãàç]ýR¿L×ÛR“3EûòN4y•<`HDv6ƒ;¶Aš¦pþ¬'­DŸD‡Ä™æJ UåDo¡1™°jÆ«n-úþËÚRã.U¨5Åàq[j€©Á[‚æ™~JÐÿ^0ëm©­Èà9¹4!oK­Fùæ…[j Cp©ƒ¿7/.ŒÚ'[jrå` ¿:ЖúË:\ý¤_¢@¢@¢Àh8<»ÍÙR«7^qz!‰HÐGl©76¶¶÷ŽŽº÷<¡(Zõü»J[êÖ`~6gK-¶#q2=Z•_{jj8ÛG ]œ½Ã©J}K„¢Jƒ‰8Ý(îMÔ–ç/ïþÐÚ ã§-áD]îYг°8žH[êçc:vMx”7,Òg!÷LR8ÑáÅó€G•»I´¥^iwm`j2w)l]žhBQ˜èèPÍ—ƒù¢-õÅv{t[j¹^Ù|åÑ–º ¥žmíìîíïlÑ7‚º$ìµ+É©}o‚4¢¢Jý:ð<ŽÍ°3õ&:15ò-PƒŽääÖœê æ˜J½¹ÜÞI(õ[àƒÔÆDISàëÙí©züˆ6D©ãLâƒ7Òp«ô\í 7[»{Gߺ÷ýÑPjX5n¤ Ë~¹»ä´IõËtÀžË«±¢öØv†ç;°Äço™Ï_AÛ¹VÄK¸k¡0f‰Rï¬Éá4~°è ¬GÎþ)ߎ\Xú Q Q`t žw=J­Þ–RÏèÍÐÞ–Z=€¿ø—`îWÙ¦ÇE©Õ^k (õr†R‹æ\ýRãn:øú…\\ý:ðµ%Ñ!ñ@âÄÏŸè`Ãí4sŒXu²¥]‹H_$ $ &lKŠ3fKoO´óŒ­ñ §z÷ d™ÜËJT/ùH¾$Höà—ZÃ8ᄃwtŒÒ$·¤U¢C¢Câĉ~p>*ÈÞÜm £L¦MÒŽmõÌ›‹‹ •:þáÃ\"ö÷ßúôÉßH…CÞ¸˜¯p•l&Mjµh*ÚäWNc66>0Ÿa´…OV;ÛC òߢ½MªÄ4˜|q9?þÄ­ÌJ«¦Š¥ÄÒ‘u›ô{k(è´êó×LÙì¿ÔªsgÒO4gî¡Ñ/u ¦Þ¬[•íèkY“I”:bNáD‡Ä‰$øm< îy€Ÿƒ‚—å©(OAš›[Vç¾ÿ¾³½ í×ñÒÂõl4T¨øÁ†7õîîîŽÚòæ¸~9eŒ9=ýÁr}„{=%½Çóׯ_ðj€[†TÏûã‡eؤ9PÖñCJ¬../¯ðí*q,䃌Mê™Òym~ow€1 @MgJP áEÄW1’à7œD’–icFãC`ÈQ`Zþ,¤GUcÍcõ–——-—¿˜12A 5ñäEë`óz” õ· ˜#aMØžŸonlD¿¢ô9­¹V‹³ùhzü–rþÝpÖ½†RëIDÁ›Ã$oO4Ñ£º5wÌܽ‰ò’·<¥ÖÔn¹FóbTîž¹„O'œ>ñ@âĉ~+Pƒ”=Yõ£±%ÆOo~ŠšÕüÛák‚£££hÕ0"Ö…VãÕÎÎÐk®4ŠÀïëׯ0žÊ\]]1üû×/$‹) ø \y?¦,”©Í:dZ€iqyPè T)®+bõùÎŽ@×uߢ&ø 5Á3VÏWãøøJ³äp¯¥kñ‘†ÈŸŸ¯¬®bÂÏÏξ(qö÷÷ÿ¾ÅŸÈÙN‘RÎÏ”9[ê°nWV&?›´yÌ–ZtkèÌj‡­æz{âÍÍÕu·˜à­Ö|§-~©¿lßÝu—–V¢-5¿‰œÂ&}M(¯$˜(¯÷°F9óÌýRÓ^Ý3*ÀJ¡–u@­˜vžŸ_Љ-b >ªy¥Ž ;cÜ[¸M"¾E‚b D™~€sSÿô—®ÃÌ#§„™s¬s!·˜~f®ýñãtâòš'¶¢®&(_cFM°(´ ;tº9‹9Äú !ü "^þ¼²ôº²%uJð¢)PòKÝ‚EÓ(~©?líît{êeúÏLká]{yy寸Òôõê%ýՖ:¢ ›ù­ØŒÇcR˜KÇD‡D‡Äo’^ž-51ÚºgÔEêLW =+l \Õ,’1Ù×G„™52®¬,CeŒZ,4õòíl^ÇaØ?ãŽ[ZAôz]É¢m´âÐÄ•ù*Ö¹›ÙRß߯¯oöŽo‹þü :.âhRWÆ£æTß ?l¸SŸÖQP¬> ›íø9Jq†”^WÖ‹ÖSå¥@ÜïŠh´ÖP€Ò^CŸïl©åreDýJUgý;xüxèËùÅûÁ=žÀªåU²¥V2$Ûñd/žx ñ@â?”ÀoÇ–:ª˜È½E2P[ÜîV©(¬¯¯½¿tvv-<³xîtÊ*¸·xŽaxé€Þ©z3~¦È±¥Ž\en¨!Œ.¾};@ž¬- µ–ØãØâðoQÖÏJç$t^Æl3í<e.¼Å«ø9JÇYI+=ïk^WÖ£:YJð²)0![jºùÐWžªØ(û;<ÿñ€ d q'[êâ:&a¥ÉßKâĉ~#Èõ–l©©ÒjŸØR+Ú ‚ÏÎΨq"ðùÓ'¾ÂŸ««k@[½­M„‘ 0lºÈ %£Ã8éÈlo¯¯+S¾–Ÿ€~£Þ\°ñ8::ÞÝ57¨ÀÚÚ2y÷ðo‘ŒfÍxò“Âïýû÷×á¼f†R‡Dkëë þÂ3~ž+=ïk~{{ ûek‡©öcP`b¶Ô2XÅG5Qjø¥&&{Ëñ__ÂP¸%FýÙZðàªß ©>”èx ñ@â7ËôèZ˜ 2„1&ÔÇ>iîµ£œSóo1­‹/gõ›AÿÊðéÕùïN–ÁÝîí·prn~aáDâÙcyj÷~8·¿·ˆÄ'@j ôÂ[0gÄ 2@¸YÉJ\§ú›8ÊØÚŠ5†Rµ.G€óe!=ôûý}qQ‚W­ÿYÁ‰ÁU– ú«®ü5U7Ò\^^ă’ž¼ÈðÔ9*᫸LÀŠ"|~O7JéÿT•÷ÚZs`ÿuç>ã‘ôþS€ç‘GóK-ž¿øÑ†¬Æú‚çrWG_ÛSWŽÔ‡×VW·÷ö»êrvÕøçý|{qyùïæçáVçüòÚÖ|3í…ùÎîÎÞÑþN÷î{"ôøÁ2,M +!M?$Hñ@âßÌ*y^­_êg®v<Ãê&¥O£u¼“>ý {ùwTiB¶Ô¢OÈv¶Ô¥¾…Ú~¥>Øß¹ëv—Vr~©GSS‰‰‰‰‰ªO+J}½~ÞIÎß3÷Kº.Q Qà9S ú¥6”Võ?FñKýáÃÖžø¥¾éõƒö(5ÿžãsV_IXN„d'»õ­ýö¥p¢IâĉL—¥ÆYyѧýþšÊÄÝüÄrñOùv*I™& $ ä)PÐcaã1’_j± Qi$Ú²ZSóöq@Öÿ›Ÿ_¸ù…ã‰]³ÈnÍàöD\lzðe‡s£-uê‘DDDDDßIC©//qæ§€R'[êßÙ©¬D×D ØRïîåPêì;ôöDÉεçžÆ³53;+Xµ·¥VZ&|:áô‰$H<ð›y@¯IP”:ÜmÃSšæŸ‚4?åÛ)5'e›((PÀ©½<±û¤4ŠñÁãGðæîîPä•C¥¶³ŒRþRãj)Ëì=ð‡úÈ“½¶ào’ç¬Í“_ Ó£a¢C¢Câĉ¦ÏÞ/u”½¯À/5}QûÔq8LÆÝÝÿÝÁ=ÞþDx|ƒh¸mÆ;ÜÉRw?ùÍ©òJÂÊôHùóçÏ«ã㣘:ÿžú™^hóÖ5¯6Ê-»ènþyJùl)@¯ÒQ§… <š_jÑ%¹ÅE@éà¯ÝþËÖýbK ƒjhÛòS‡É–Zøá7ã1R¢²a*7Ñ!ñ@âÄêñãuÚR‡Ûµ3ÅãûññÖö6n„øõ«‡ R¶··ùF˜P(ñÃ.¼/bwwwT}¥¹w ¤äE†þ÷ýûYÌáÇ£–>vz¬x­ãðB›·®yMbÑÍ?I)_&oKmšÛà/U²åöñøA_ËêÝšR,Þ1#½’*aÕ‰‰$H<ð›x`0À~©Ýž˜›ƒØSù}Ï jÓü[®ýïß“\¦Íù7)F܈Kþƒx¼Å-⸀žŸŸãöD¹UqeÅÛœìíîÏÆ9ß¿gJô‹‰¯b$Áo¸Úˆ,p±‚˺cÝíŠ "¾ŽQ.þÄ“›ã„[#¥J1ß² Íͬ ŸòüÜ>÷Aþ¸ë‡w»D÷MBãG+Ð@6@þ¼‡’--—\ÿÃ?ÿà|럥tÍG>h/ê‰ryÓÐT˜,eú)ß[Óu»Œ-î}êLÚTdÕŽõ†È"µ ÉPjÆ«}ZYY†rgshêe%57׳ÃZ­ûûtGä???ßëu²úøöíP¬wÔª;C©CÐX(ßëëk0>Á<Œ†±4QX°|ø€H;æˆd¨!~(‹ê¸¤Sfiáó#=Í bxc,þt°Ô¸-£¶=¥O´M?¥y>ÚC‘ÿS¾£¸Wÿ ,Å¡ ûãOl2´5ü¸'0~èÜÑ_áËMŸ? žwO.ûŸ;Q.]\üŸwj¸ä ÍðT5˜KVY îìwïU9×EØûùöâòrÞ–úð·C©'dK ƒS½_¿`/ã*ëÀ ¯ »cƒ #‡W£.ûáO ',j‘Oº¹‰¸r†"ÞëiÎ;;Œ÷Ÿ#bÿÂwÖñ# 3D}˜a™¦3ÙZÐ÷] ”áýÔòÀëµ¥~>Æs® 4„oß ’J=adBÀp°i’•mÐ{“ô)Í‹¡Ào²¥†>=[jp0Î#ƒÖ˜a¢ím±æFìÇëñ¼­¬tc†»3€“±¡ƒ4xÒB‹ñH#9//±·œC¼OƒQ¤»W38A0¾µ˜™î²%›ÅHOO·.ðaâ“Ä'‰þ¼Z[ê£üÑŠÂPXØÓ«dñëWبt`°$®I†@ܼz“ORšB‘d°¥~©M_ª²¥v?&jKÝùûoèÄXóUâ1póNû*lóÁŠ£Ó™Gbœ4F ,F0 €aàÉó<ÖQ–³~Žoñ[[_‡K õ°AßâÉ“ù%F³K8Y¢Câĉž¼Z[ê¢|¼’jÂ"ü¿ÿ.±‰ýßÿEy¯¤m©#R`b¶ÔÐE—T[jÕ)åöÄXñK-^>$bâ~©á,k>9•\Â’aÔ¼™ Çn¦Ñ·ð5‡V_À9%‚á6G‡a#>˹Ý·½nNy  Ã„ZÚ‰}CæÔûÕÕ0ÆŽwÒAÏŸÀ]¤:©Ü„ù%He–Lß& $ L›‡g·'WƒóÏæÍ«úÓÓâúBÔcAbq¢hÉÙÑDùC¯²Ü?=©<C>/ˆcRUR`묵wÑšmµVÞµp¯ÄãÐè~ëì¶uÿw[½¾>ØÙA“î àê»ÖáÏ>\8j}:¯¶V %‹(Ï)Äsx{¢Ÿƒ¦º[ï£kš[^‹A‹]][£9n@Œç|`£ÓGèÓ:}·IE}º5”]¹Uqe…~—ùÛÛÝž [˜VÝ„5_ÅH‚ß¹"t†®VÁYÿ˜Ò£]1AÄ×Q"ÊÅŸxòºoü€pk¤T)Fâ[´¹™äSÂ"´LmäËÞóqhìhãðZ²É¨ò÷MV–\ÿÃ?ÿà=¸e€}–Ò5ù ½¨'Ê¥ÝÑl>yÎÈÝ$ªZµ µô¯¨³…„kJQ4²Hµp•KúÑCæåZ=~š‘_Á/µyÛ0ÊøñÿÖÂÏ™9RÝƦÀÑuëL‰þm¡›Bþúá"mT jÈÕàd^pJBÀ®t+›ìíY_+)Ý-¦2-ß-P*ËBd†²‡>ð)£];òwÁû9££)ñ[Êö7S 7´l0†C‹’ÌQü~„ôЭ㎙¢ÔnÚPy4¢¡ÚPlóKĘ'UçßÜ멸DDéQø4 ¦1®fø´ùTR‘Ñ0ŒWãƒìsÓI¦WëWžsYV¿8[êæ=4dM@Ó‹hKÍ»*s†:ûþýÒÙÙ9´pgëÜ)«à¹=ç –ÂÑkh“ªÆ›¦Ë”„ÛQ#Á‚¢¶Nµº@q”~yyã“x> x6àpf`U3&åYL)CŸŒÔ”ûšr{+¸+ðÍäul¦{„@%–ç[WY" ÍÉ ©Léóýr®y禔ϙ³¥v“„²i´¥¦I²¥v\0E ä9óZª[¢À ÜáÁcüÛšø÷Ÿæ¬Lbu¡µ§z9Ì?àe/ýÆ @ížçK³¥nÞö2§Ð«,•W±¥V´¿/;;°K¦e”<>úÄWøsuu ª­ÜLTEXà ™Ã†¹3SV¢Ô°`¶·××å”tÌ͸+m©Ya¹&#t!ò¡>} B• 4šÐøù”•ã!×á.¶2Æ,„ äöw½U–M;@ɈsW¦ô5ÁŠbõýûæ}šR¾ U£ÌAkKÝÆ‰f­ã6ø¥ÖÄê#쪘½ˆ±mGø{‹áÁ%©’‰‰ )D?œ/„Á´yDÎŒâšâÓQZ"˜Vãâ>Ÿµàâ:ýF¥@íüRaK­sÐ4?Å–ºyáƒB¥Ÿ ú €•3>\ÓwÝîí·p0n~aw)à'n4–—¡Åîë¥ øíïíÃè‘ødnf†à4@b ÁˆAC€p3e%J Œ¹‰#Ž­­Øê˜R•ém }Q¡k?T(õÁX‰à·¶¶Š?‘' gªÑø!ôlD"ÏX%¹2ƒ)·²”ž€H|꜓ð•·Aa¸@öÏ•e¡¥''Çðø—Xÿ˜ˆµòùcsàcÍÁÐæR>7 PÚx™3¶-5ýR[ne¿ÔЪënO4nvëB™Q©^q8ù¥~nã!Õ'Qà)€õü‘ˆAø÷€ÿi=Ì=§Ìå­ãk©ÚæRk{¥µ4û”j¾¡oÇòKÝÞ™Ú…äÐ2Vý†úxܦ6ôKÝ<{,xøòÑO’_êGIôBLÌ/õ½SÔEXÞ/µ€×EоÍöÈÈöñé´‹ûBÇLªv¢@Nn[ЪáÍú4!7FVð²­»ÄP¬WŽZËÇ@Yé×€ÕóKÞ–:K3U¢&}ºAý¦$8"ùí›AòO)F&´QE ¬Dšd¥EôÞ$}JóR(öaLž@æì—Zuf³úp~©ÿ·°ôþêê7ðW‰R³øWŒCÇÚég>ý^V¾·®»r¦p}i2øtçîÞµŽ®ZPÜÍ6AEçl»53è¿k fZ÷ëYrÁ,ô\¹)õêßt0+Æuñv¦±{4ÜV+ÖÎ;¼£ý®¸Ã108ཿ‡Ð7˹¿À¾Õ» âî7«B¬…gÞgfäe{ÿŠßžiÍÌ⪦ ßÖÁÅ>Hs4)[«tÍŒóEóàÀÊùÑ#lü´†ÛWfw`j3_B©§C×?™+LÆŽnnºà%(Êѧǟ¬S*ûQàðìöäjpñ¹u?½="ͤ™Ê±°}ä-* Ã*|"K{‡ûÝÞ@üE”zqùó K77WT©Õ/5›öÎÞÞþÎÎùº¢nnèÓZ’Û ¿{7-Áý‡Ø)›(ð¦)ðá¬ß¸ÛE”Ä©Y¯Aò_ÜÞ_Þ·Ö8ùÊ‚V}uS-Šh¿Óê‡ô¬a“4NG¶vÍ·ú]Qæ9'0ŸrÎOŽÑ©ÇrŽ „ÌÛ]Ô°¡Ëë#|«}õe6~æNr8 ön~Þ9^xXûÖßXn™šáÇ›©ñ‰¯_ÏnO¯¼œ2çǼž3zŽ&È@ÝZ±A@D¾€ÉþöÞþÑÁÁM¯O}i—æÛËË+Pj®az3;ñæŸl©_ûøJí{C¸ƒ!õ¡Ü+Ž{'eE]—ÏíÙá«ng0XY£»{‘ÏA(£õ‰{T³„‡ÏSJAIƒ˜‡0ÅQL ) o–Ü(žë?´Xîßµ8½Žƒè³À–î–|d“RÒhYºa©êóAJWOä„ÁÃŒbåpa†ÝÃCfÎà‚*’Û¬–…:È¥?´fµÎÒ"ŬqVšvö úvŽß¾´’-õ©©‰šÓ³¥”ú×ͶAƒ”lþ(õ]·»d(µJõ·‡OO†šÿSù‰£Üq¬·–ÞÉŇ”kQcx¸{ô¹suÛÌnlsÐ4Î×´ Gôô.éhÉ"a¬'RÞ·UD†ž|ÜÞQ•ºbÿ«üàžçÉ¥¶40üH(õĆDÊ(QàQ  ÔXð‹Žyuª(µÎ´SŒ#oúÑÖ«ì÷>líì‹Qe¥þKbø=â—:Ú{ o+üÆø-57QàÕRà^X˜SbR„N)LK½Ü1MódEp_esÚ…ÿÏνkÍÍÎvæÛ°îî,Ìuæç—æ—:‹ KËï–ñ\š×çÒ²]7];§h'Ñê#—Æö[§Â¨O¼=q*uJ™& $ Lˆf«ôXÈé‘ýR«<×M>;>"É–úq»ðdK=!6NÙ$ üy ]·vÏårxç >1E¬zHøÌù<΢´õp¸!ÆÁVOì@̦¢Zí4Ĭ§öû ÝL”°`Àª©Ón' % m!V]^rPÊ}w>Ý7»H£ Ë•Šp¾1›zG±Ó“ÐQJjéö£<Û&JL¹¥uÞ_¡–Ô^]ÛzѶÔ8t$î/.Ú33«kkðÌËÀ zqµ633oÍûû|_ÔñRnR çJyy!9¢C‘'îôþó&Õ QàÕQ`r¶ÔŠR+J²ð®Ê–Bw¾ÓÞÞÝ;Üû|ssƒƒâ:ÞùˆŸî 4ín¬ü“-õ«P©Ao—{?[‡¢OC«žª-õíõÏ…£ã~«ußÊj± zðŒZEˆ¹²„ÕAÏb¸2¤¤ì _cTÛ*º†­,µ×f¸6jåÃÓøÒ™uy†íÛšú4ü*WÖÃÒËçfKÝ|x æk««»{{¸¥„>×®Q-¹ãù†û6Þ+Î{°})Œ^Ž›M ”c‹÷âÒDèèÍ+“R& $ 4¡À$m©¥2ÖòsÓ„¹ ø1~úÀáJ³çiEÿŒ¥ÂZ£RŸæìóùûï¿¡RÃðƒŠÊœÿñ°åþþáîî.>Ä·ý=<äŒC&ÜÔ”]¢À[¥€i¼Nž¨€17Ì rþDu`žrüÑ)&‹$:#•‡¹Üo‹Ç8Âå:6ëtlÛ k{û¶ü{Ô=«ì—Ú(ðz( þÜZó³ªÙLÍ×G¯×mõ Laÿqך…cjÞžð¼U–½ ›ž,ÎAe­t"<Š3‚øéãžçñãýäþË‚æ-Ǫ~¨yÌ`3nì«S¾u2—Çû÷ï777Q&.--A«ŸH£R&‰‰99ãüQ樀ÑS×Ñ*N§Â!ŒWÝ:z’uû¡ÃW…=þ”š6sŒ—â¥FÃæ‘ïZ,Ár~­iC& $ ¼x ô-ü_®´óv*õz°Ý¹I…ûß%Ï9¹UeçSJ]OÀUÕsç "C~ 3ÑæI\0̇åŸ;Õ_|9¿°p{{cF ÿ„V K’«««åe¸\úÃÍKÅ' ¼F ì/ «UÀðT‰ Ñ:#”Ú¦Õ­U©ÎPj=â\Á/µmp‰›¥ˆR£ˆ ›“¨N0Rguõ ˰ò¾¹ºòߢÛÛÛ ú;>þŽ2€ ¬¯¯Ã‘)ž<}?Иyñ‚t9C¢!gäÏ]9„;¿õ)?”¨"¼¾¾‰0Òà6ÂÌPOd7i×kd«Ô¦D7Fn_i\8=|šòó]¿'¢ª3sßž™›Møô ¬–܃Jí±ê)áÔ£ ‰/_¾žÉL< ðéÓ'f‘Ÿ=sÙº?xßC,ôi„qa|ûóçOFâÈãÞîîhµI©P È“'Õãû¥Vz†R"Jmšj¥¦ŒêB 5= e÷àà;_P©?}þlíÕ| Œ LcQŽ4ðÙ‡ø‚–Œ0žWÆÄ:à+ì¦!gèåræZó„–,¿_¿èÀhÿà`eyiðDiPa_ŸG º*%IHx¶€-DÚ<îyQÁ6)Lºœ ©‘÷á¾ß‰ž1’-u5rÜRš_ò(u6‰]ã3øšá¡C 6ËË‹œìX¯!(5ÐLXP a® ›i„邚ñÈjkk ÓßBB©ŸA§*¼> äd‡Êœñm©e'2 Ômµ¥FîAgW[ÿð¶ÔAWÂ:›¶†aœÉ€òŠ !&2?šÐÆÆ†é@½F Öè0&Cb<¹^/ÇÄr™3Ò ©pUÝPÄÖö¶d¸µ%HöÌŒÄhÛŒá¯I»^g¥% ¼% J ì¾èhû1[ê^¿ßîkþÝÁ}VÉŠzr X’Ãy”:“ÕSŨGº=æØ/ÅÔvs“3˜.xž.Œ3Lÿ÷ß0ׯ“VÚøA±À„¬ðŒ‘oi€¦¶& ü `ÈŠQ™3®-µêÌ¢Ul©³ì3[jÄy‹“€ ó^®Ì²­yGže-î¾ÅöÖôXèC|Àz c/L 6:Yî‹iÌ,ŠDð”“%MÚò;z9•‘((0] ༹| öÓ1ÏîÕéRÖtw÷­wóÉ×Çp X—×ÈáJ]²¨ž»$uvJ„MÙ& < Ø—“'#ÙR‹nl¶Ô‚HëÝÜ5‹(u¦²W¡Ô´66ü`Ì0¬¨qÕªXŒ¹| ìÂõ&l0 ÞLj„óÝhÜÌ vú ±(ðˆÁ`¬h®0êÀ™Ú¢Í@G‡Ú í|eeà4òQ+®B$Œ!£³=ÀYγØÞçÀ©‰‰O£m©ß)J-²p:Xõ\÷V°‹Yüf·r'”z(¬Kkäp¥ösG…'º§1‡ûz$”zb¥¦Œ~ Láuòd$[jÑTÕÙ‡>Ä–Z|åIpnOz+?™e€ïìîíïlA7…Í1P܉¥z§Þ&¨úî¨aZ˜ÁNø±ÿ[f¸#ŠÇ¹6cà 1³K÷z}@Úxk1³³ØNeL¹ØbƒHÖØ¡nÓ…Mö?§0t›Ÿ‡[«ÍËÿþ[ZY9??ÕW½n—¾^^þ7¼]'gg¼‹¦)ü¥˜DÄ /nµ¶ºƒÙÿ>¶.¾ŠÔ{ q]ypˆ½³°3Ó;û3NšÏ­n—¼,·®{ýÙUlå_#gJׅů¯®Ö²,æ-§Y[—«^*ç”öÜTjXA`âPÄæ µãþÆrû˺yÉø-³p*$Q Qà•P w{¢ê´¸¦öâ+çüèé~¦ vDÕá‡>lïíÜôD†«†ÝZx×^^^ÉÝž¨žžÄcëöîÞáÞg€ÇØÿê÷ïx”gììÌp†¬±ÿZ^[]]Y]å (Ó˜¤[Ãê*x¿×ìæc s{Ms!~óó££cˆ]”b1ÎÑñ1ôxfˆS#›ë¡ÑósføñãG¨ø… +Û˜nO»ëÓ‡‰ÏеzýÖÕVkþeåtž»ŸÚ½‡ëùÁÍÌÜÜüRÈ”ËéY¤oO¬ä {äØ>ÁÚ·Þær{gj*5JÛöã)ß>«Á’*“(ðZ)pxÖ=¹êŸÎü¾_\ühmnÏŠ~Lü£0=¨4ÐC8ìììóâ0èÓXì/áöÄe½=‘ßãg^¨õw{"Þo?oq¢™2ÑÛ©Ñ ðo)A¢À¶Ì™<Ï/µ¢ÔâAOÔiü—ù¥æùwÕ³©¿;¿ÔÍ|b4Áz_pš1z-}’((ðŒ(€{^ð“{^¦æëùwôŠ×Ù™ÁlòõÁ“=<‹Tû’ª¸=QçÙ¦Ã_OAš›»¹¾çTu÷™O§eȶ”ÜdŽõrOd]yñ€ìBñ›@µR‰)üRg2g<¿Ôv–Q³Q‰”Ýž(³×ýáÂD™tùøT[j)èàÐCñòÆ]•& $ Ò@==9cbd (ÊÑâÅ› ǰ]Q¬>µ" ·_¾|Áçp ¥œyžžžÀ~Ú6pb¸¬eæz ±ý.›dà^ˆ…sîëÏoP¢Ù„lnÆÒ‘ÜšÜj¡]¸‹)ã·ãê‰ E¯Ó/Qà÷P`B¶Ôê5ýYO!ª-µ6AüêéŠrg¡ ñdKÇÔOG§R¦EzЛïL×–Z$«ü_mæX§4¡¿_-5õãºgäo7ë%hò‚Ko³°‹èá’µI´X¸%|_‰Rõ†Ê‹o‘!n7cÚÊ”@Ç 4>Á/®`R¾“ züªŒ—£t–…4hK‡Óqx¶… kâ/»@$!¼ôKø=˜-µÜ3 Ñ¥º5m©åß{üƒka8HÌýRKA/ “.á%¿§ŸS)‰‰S£@·'YÏjþS²¥¾ïuuíÖz#mbIœÒX‡¿1[j´ºo†6¬Ö P§åIâ@¿¦M3¹…¿J”öɰñà·ÈcHJ(¸;i$Æ,÷9´ZxòVѶ"ñ«rýQ:ßâÍ»N¿ÿ 5*ë¿ebBxé—(ð{(0A[j©°(Œ\ë¶ÿ’q-pŠ"+ŠOsìPj¤{±6Гò1ò{ú9•’((05 ÈT”úJ>Š=Ø'îw¯Û98?6ûîQO Ÿ&ì÷Æl©ÑêJ¼ (ì•ñ&áj³ÖêÚ*#qûo¤e˜;CIÝÝýÑ_|Ó¤¼½¾Žf•(5,˜‘'>ŠSª%´Üó€xØc”Ç""£ñF¹þ0ð`=ñ¤±~ý»;d ÜÖÕ±&‹ ñÊLd¸úþýÔÆ}Ê8Q H[Ñ9™cAÞ.®:°ðªz+ “„aÒ¦›/jM/aE©á—‡ŽåP¢Øÿ ôɵ/ –èñãåcÌÀÈS& $ ¼` ÜÉ6ÜtïMÁÙ7”ð`B©R pÕë±¥n8N*ñfèp‹€1ïÆ§ aï0êˆø.N⃊µµõˆþ")ÅaÈÖV´Ï®Ä³a] ØÖH BY[[€§å¿ÊV VÑë_¹þ(W­¡t\~GîíïÿóÏ?+ð÷±¸u„0ö¿''йyÒ~T¯|é—(ð{(VƒOòK­š·ž?„Õt´¥Võš®>ÆSšôüRƒ ›ÿ=JIH˜ p6€Üó2|š˜w¶¤ŠW´Ó½‰íÈC¿[ê† 3¢Ù1>Ä<ýï¿ÿBÙ…žSÄï?àñ1†J øA'ö9”?÷o}§‘'rÀ±ÅXgä6?¿1æB[ð :lK_®?*ýX½—üˆ€42G)WW7ë )6 vúäV %»ùý8 i›’% ¡ÀDl©Å K”fQéMOl©õ¨bkÐMšú´àÖÉ–ZI ]’ŽM¤±™(ðâ)³R6¹çEU’)ÙRÏéQ‘±í¹†m²¥6Þz{¶Ôã *ï+c¼†|íÖ&`®L EœÖùÁˆw32‘‘.óø!šxħgÞf8qd¢@¢À‹¦îy¨ÃñÄ©ÚR ^A—I´IÏ _éŒS1¿x”Ú§qÛ«“ģ µOùvò-I9& $ ”(@é‘—9¢ùrzàYCC¦³c7òVaE©‰›ˆ¯¢ÔâwOýR«ö-GiRQ‹Cô=uó·†IÛ›X2Q QàeSàî^m©g¦kKÝꫜ¥gÓ„R7£g·šýÏè ¶&»'e|ù¤ù)ßN£-)ÏDDˆ9;™ŽÎ˜2ü1¿Ô‚RKJ•Fòl©£¿ùp^GÑl±¥–"BðFñétQj‰¯€‚RC¥ž¢-u~ýW;ƒå™ält6¥!8»Õ쪣¹_òi²{R¦ÁœOAš›‹KÇYy\4¨ÏáçÓ¸xÅßku›››w:‹¥W¾í |Z#n­áúÇ1ÿæ$ŠW"+R>ý^Œ³ŸfK-ÈtD©Õ7«ØR2-'Û³úô¶kª§›÷7U¿>žJ-Jx3è <КŸ•OÉ×ä).þyÓë^tïa@—PêÆ06|{¶ÔQe·èä÷xÀs3ÜnàF’n›7 øð žªÕu¼R±0|¡ˆóDèß——Wðc=†Jo u½±‘új)òÙùÀ1l©Å„¾>ˆg µ™¡ˆØO«=ˆÚhg§yp=ÙR¿ZÖJ KxýèÞI¡Q«…œJ>;Ó=Ép¿{+žó8¡ˆ“-uS ÿÞ -µÍëa78ŽC¨Ôð¼Á?öq"¯bÁÌ«^p“KyÄÂÚW¨¼{÷87Tóùy½cþÀ¼qO¡\©¸±ñïóósxÍC$î_°<ñ ø–x9®bAqɈåõ Ç7ÙB¢ÔO´¥OÑšƒ(5l©‰Ù̪-µ>“-uÙvüM2]jt¢Àk¡®WAQU4¦UãÆ¬9Ø—–­¾dKÝ”œÝÞ -u´«ˆØ0Ùh=r~~ë!Ãw¯ðroàÜóŽùÃ÷32Ä¥†+««¸¨…ùœŸ}ÙÙAäþþÞŽ^?î¿ÅŸÈp‚Wº¼Y’Úñ(0![jbЦ3›-uDkx>^BðL¶Ô9?ܯ‹RÞ(º=iø,w榀O3χ~w¶/GÁgíK?R@™òUÙRÜ­{ÆAìÈeæõ¶ÔPvŽŽ˜ 3oðþ̼´´À4Ñ:æ@šz9žgggLöíßW×ÖX[[ïöt„äñò………ëëëÇJNï^&dK aoÃFö?Ìl©«–µ²Ý.ænOL¶Ô/aR2 ô58m[깇ÖåÚÒí’^јPêÆ°~zE¶ÔĘ랑/ël©¾}‹j4l0üQźQ-l˜ñmļcþÐË£;¯xS N=~úô V8é´ðh-ÒB¦w­LO‰“±¥–Ûmĉþ¬kRØR‹nYT›­á$ýRcXbã©Óù»l“}uuµºŠƒÉœ¨ÀÙ `‡Ž8wP7ìL­¯o" 32„=žqzz¾²²«/>D¨!Œ4(áùùEÄ#\gÃçãÿ`ß§¢žH¢Ôs°È˜¦-ukp¿|)%5Dg’í<~$[êjÇÔvzzú(ÿÃl&ªÅ™~>› oñ*úôÀœ»´´("=^Ë‚*?Z” QàQ`2¶ÔÔ¢;ïÔ\ÏŠ-5çy2NÿÑó“ñKu°ªÂþ­á©Cúýû¥ÃCÛêêõp¸Ù~ØM=::€XAš••e„}>8«qssóåË—-Xƒi¥ã·È‚fÿ@>G`#ÍþþˆA< îƒ|A¬’ªš((P¤€ÙRÃÞ4m©Íf®1:›lR@É–ºzØbj;<<¤µfn-—“¾ÿþ:œhÌPênm}–$xNb4|‰0üõë׈gc²zÍ···Ð¹“4Ix}˜”-µP&»[QýR{?;nŒ™ -5†è‘‰(ãÁ0ÕâYæÍÍ-8ý =—³«ûñãjwH#6j1ÀÏüŠ˜t¬zLµ{ssz3ž08C¼ÆlJÌÆ†˜»=îÏäõqTjQ¢À¢n#ÇoÚ¶ÔTåæ­toâ(PF|U¶Ô ‡V%6\ø܉þÔ5Ç"&¬x¾Ð§„‘ôéù9cʶԀ®àB9àIßÕø!ŸþÁyÅUWа ˆ Ó"|ö! t¼ÉÉȆMÉž&eK).@Éïl©ÙTXÛ=æ“ôKíïª1L ¾ÿŽ¢±øV‹±Øo!r~Q¬2ú}äˆG$êƒ'ÓpeúƇm Ó,,/CF`)/«í™HŠNGÔn<ƒ4“’‡Îü¼È‘&wC>^H5IH‘}]kÏëÅÓðõÁ<Õ®ÿ4õt‘Pj‡R×ÞÑëïIð²:³ù‘š$Šó¸æßÂ5+3ÜdP1´[ì¾ÞÜüúæÌ¬}C cr¤Zs‹ùcZdxF+m̰¿~õ`{ T {¶pÀ'cd~þòJ|Zc®„¢ÐüÚš&TMiž &dKMÔÀP$¿œ-µµSU÷€Rë=æ“ôKmзǪ±P†›žÅÅE iÕôgn¯®°ßtuy‰¨­-ñìƒx±çx€&ÍõwÏ€MÈÎÎTj¤‘oõ½|{{[m˜]8ÃHbfÜÛGî†|&|ª‘((0è—õtm©D€bÓ+¡Ô#Q€úýRÁÉC>¶ýí›9È{zΘ—+áð§çœrHøã˜-µiÈîžÑöÿ––n®®¸1ÊîìÝú¼w¸·usÓåÚW?€†­æÕ*ýÆV‹êêoñ Æa}/ipŠpQìº`ñüÖÖüž2ƒü\>˜Æ —«˜ÅC@½Æ'XâÃ…P§3Ûëõa:S“#2fx»°é•i`Ó©”b5È ‰ž9'@xÍaàZ{í£\Úüeò 6cã’CţǴÛc§Ù~趺÷§‹3wAKËï!åÛQËJ鯠ÀáÙíÉÕàü³8ÀàïââG»=ãp*ÙA7Tz+Ø,ú°:óÙÙ;8úvp;¿€RãÔââòÿæ–~Ý\uïäK¾ÃKÛ»{û;[ÐPq€‘ØH[ è­3‚ìŽîüý7àä·’që)¶¢°&FA0óB ~P Ùm^è¡[cs ³ ”`Ìß±øx{kk~aGLC÷EnÛÛ[óó HåûüôtgoâxëãÖþá!} è߸‘j_bomW4×£çÒ'‰‰–mÃÖÂlëÒîxV¬:Ôi’áÝO­û‡“ÅÖüÄ&±êô|œkëP©ï*繰݆$‡ösÇÆqc¹ýej*õŸåØTz¢@¢ÀT)ðõìö*õÖlÔý`ìDÿ6ÑŠCôh“áÔE{V}ø‡ ¶÷ö¿}ëõiõ!iF//¯˜Ç~Êwj2 ¿Ô9[j€€Ê‹.<¡++Ú÷õŸÎ2¡Oó24` ÔŒÙÛÛõ¶ÔøðÓçÏÿ½}:4d®Ä|BÌ߈÷ 33P²qÀÆÙˆAi4æœè1&ÙRO•wS扖ðœ)`§yÜŸ¢-5¡évîÌxf%=QÀØãù¥þ³ ŸJOHNIÚR«Ô·ÿœ_jAmìÜ¢8ôž1A[j±ú¾5àgšN< OÃîæÏaïxfcm ˜L«OÏë‰öì,ÂHƒ§z³Ïìž‘Éåÿ!gàÓ‚%?< æ?9“˜E´€7Â@Üyhš¾«F ⃟dKÆ`¢Àë¤@ïAÐâ¹îuÿçÏ©ÚRô*-ì ŽdIñ7ûÙ.ÙR¿Îá—Z•(ðü(0I[ji]¶ëù—Ü@îNÁGÓÚ¬¦­VêgCl?ž>;;ï<ÍsRu«Èçù1DªQ¢@¢@C ¥î îÛ§Ç”u*٦࣫ýÁfl Ÿ‰T¨«ç—`§N¨‡9(Ü¢ÐFMÖÜkG9ç§|;j=SúDD1(@”:“9á&N²ÇHí—Õ6a˜gó†7c)ŦÃîtQ¿Ô8‹£È¡ÔÅ W—K΂Ж§ß`xŒKŸ$ $ < À Ä›\ó¼Z[OÅïÇ}ÿnÙ8Š`{÷f±çæè;'·êù%ø|•ùȧq¸Ð4¸ì)ÎãšÛé¨[G÷ƒ:§Ð0\üûïÎçÏŸüUäØVÅ,¶Ué4¶É-å…Ì›_…XNcà´šÙú4£-|²ztçW÷óßÒ´á®WLÀK7¾-ÓðѬbéÈÊùjxô»”àõPÀîc‰:mþ&–0GÈT¡¢ž§EÁ–§YpÀFôзØµsÓíö_ræ]~jE-ˆµjØbK­±:!LŸž"–MÄ…¦ÂÎOAš›kóºkÁ÷ãc\ »GxŒ^YYÅI}¾„† õ?õN-7Ë) ñVÅG¿+§Œ1§§v#›Oƒ0|`ã‡'œÀ§V¬yeYþ[wÅÛ£õjAYÇéÄæóRüg—iøh.±tl•AÆGóO ž?LíÍl.xõvæa:‡Rg÷#Ê C©Õ¾ƒ¨6PjbÐÈAPjhŠRó©v!Ùí‰æ×ÙðiZ¼«~3áçÏ!©†‰‰Õ°‰k Ø:·{¸ÀB%ߤ±êÁCOPêl“ÐNˆ'”úQ h·ÕÍ)¶ƒªó‘Oc÷'L‰é›#Íå 4ÿ–h–ÿý{r¿®Ôùàä*jçp ý7 ã-Î ášC~ˆ#FÿüóÐkÍ÷ÚüÞî.cø¶âejÊó„5_ÅH‚ßpP!Û2vcFãC`ÈQ`Zþ,¤GUƒ¯Û¬zp0h¹ümÄŒ‘ êP¨‰'Z«*\Iƒr¡¾ãÎGqR¶çç¸ý1Bø‘8YésZs­:Î_˜Í—@Sb¶”íï§€NŠ7›K×<å²Ld:›*Ô‡Gø„Ÿã?G鼿¢®ôº²Þ†h|»­$;{y2ž-5}}ˆâlKÎ`K |Zu<›\?Û]‹oÆfºÖžïí²^jy¢À §dÄ'Š|ª ÊN«Ö3áæËÿQëa¤Mi"”¿’-µ 3 Í€iQ<æ2î;«ˆëëkïß/ü!³xîtÊ*xWfnÀ ¡wªÞŒŸ)²Cl©£.\™2(þíÛADÁÑ–……%ö8¶8ü[Ô„uƳÒ9  ²µwÌs.¼•Í(µðƥ㬤•’úš×•õÂe^ªþ# üt[jÌ'’OD©Õ–ZvjåO+j.õ’-µ·³Iš((ðB)Ðkmöî?Í´;êáîá¾+›ã·¥†J ¹ :¡Ô£R€“[²¥`½3bXM@ôà Á¸ ö ÐøüéàÏÕÕ5 ­ø0"¾4†î ›.2âÌ^Ã\„ÙÞ^_W¦,¤70®…»——£Þ\°¥>::ÞÝ57¨×0ïþ-’Ñ\O¿œˆtf}yYWŸµõu^·ŒgüvbJö)0[j5‘æDhÐnÿ›277Ww}ú‘}ÑÎl{óÓç£ý››.FBðÙ®GyÏ 3¾™0Nx¼D¦IuNHø~q±õsuëápu±½Ú½nÝ<Ü®Ì/lí«ÕoŸÌ³÷õSçæ¡»Øê.¬ªV-ØDz6¡ÀúÇí»;XñVÌ)øút¾ÅÅyefK³ö­·¹ÜÞY·Ãjçs”ØÜ^¡ô§|;ñ†¼² a m{R32Pv`ÿ8@ùʨ”šó(ÏnO®çŸ;AÅaâí6Œ6feFt¹0=èÊ‘KV‡½ƒãoòG°úx×n-./ÿe6%AŸ†V­È ¼|¨ò­³B²¥~´‡R‚DDgHž¢bDŽïÕÖ¹­2qâø4ä <¡Ô£R€ºr_©×ì—úŽ—g^%¨¿0ÔžT%aaÝ¡L*ϔϋ ÀDl©í^*àökÿe'3ïz2÷ˆ„Ój#¥?’_êÁ)©’‰‰ž"ÎàòCõ]‘j“löK ¾j(*1¢×‹·œ^:äMÚR§‘:*0L`R2êWuéx¬À'•sÊç™S`2¶Ô´—Ö}NûÁ–Úüø»)$ù¥ö>PŸ9¤ê% $ TRྯѸ7Qè©T»›ê‰ÛR‹?\ÏñQ‘Ú·œ^:åMÚR§1›((ðG(0)[jnwrÏS~ðK\9òOÆÊ”üR{Ÿ…¤×S¡‰‰O¥€¹ª©w¯(5¼~: Ä›TX±Š6ø·Œ7×víâWå—ú©L›¾OH˜&&â—š2?Î#Z_A©Å $©ùG²¥ÎÙöM³oSÞ‰‰S£@¿¯º³ÚRÛåi÷ôm†p #Sq›|NDíüdK=µ12NHÈS`R¶Ô"íÃl¢%Pê`ú!Žï“-5áúôKHx´åÒÜü ®A߉LÈà&ʼnáÓ†së?úH¶Ô£Q@Ø*ÙR¿ÀÁ•ªœ(ðB)0[jJ{çÉQüR›GIUÚ#V-"NI•üR'¿Ô/t̤j' €÷”l‚ÏÌ̵ [?< º¸gŽòmBXu_=ù‹-5}ç%¿#P@zâMÚRw:vÉ_ªpÀÉðÍŒ ´á0Î߀oðÍW¸“¥î~ò!£¾òJÂÊôå”1ù‰O#^´Õ½6ž¬^ð½[]ÿ-ýC7üapÑçôÏŸ?á‰[iU¤á£YÅÒ‘UÙ÷£Ÿ§¯€“±¥Ž3H¤H´¥fLfJl©s¶}¯€…RÞ"ô3*7°ÀïǬÞ;Ó½½œ¬-õ½Ürþ wжgJ=*”/ߢ-µùpãòû÷ï;ÛÛОonnp7J¼…*ÔGüp ^Á-.n,u<7÷nQNcNO˜¶¹9¬—™ë­æ÷¸x÷noo©žÏÿÇ˰Is ¬óŠG¬../¯ðm™†æKyÇ ã£ù§ÏŸ“³¥Î4gmµ³¥–¿W´¥Ö›ÐÍÖím†Ÿ?‡¤& $ TP ‡ ¬ôGäXPj@É÷݉áÓŠs÷{¸©DäçL²¥¡×þ¡-uy~)û¥fšâ}“åþ±ïyA5šk^\Õq­àêÚt>üp"®äK¸O†þ÷ñãGåä6nGJ¾Âæ¸=QnU\YÈ3ÛÛÝ`Œ ¡¦3üð¢Fâ«Iðw˜GÈvÈ퉣ñ!°a¤á½å¾@UcÍcõp 8 åò·3F†¨C¡&¾[Ñ:Üûƒ{^P.ÔwÜÆˆo# ÛóóÍáW”>§5תcÑbz¢M–‹RnÏ–ΖÚä‰ uÞ!’œÒôæ·2åÄ›üy0_”üáW²¥Ö+o3[êÞ„N[7¹<ñí…Ÿ-_¤Š% $ ¥°iyo·$šÝÛ,-NÔ–zp¯Gyø;ÙRH!]Ýü¢wÙ|äÓØ9ŸiqÿSÔ¬æßº™¸¢!GGGÑ(ªaݽÙxµ³³ô€k¼ˆûëׯ0žÊ\]]1wPRðï_¿,¦D€à7påx ø”šu-È´Ó÷ÌÙ7UŠëŠX=d¾³#ÐuÝ·¨ ¾BMðŒÕóÙâf(͒ý–®ÅG"~ŽûÇã.çgg_”8ûû{€ÿ ßâOdˆl§ÅF)ßçJÌ–š:­˜žÙ !?óTLôéAHÆd‘&ÅM‹š2~¶´`Km‘úï.W ]oO$~@‹·Ã÷ƒÁÁþ>LÀÊßžœœ¬è {õÑD33àþu]4ã‰0Ê*Ådu8=?ÇÒ|~qÏËŸ?‘?JÁ·ñ‰˜û~mm iÖ×ÖFL¯ßG1ˆGøñv=W¶HõJHNóòPê{êÖnµ¥~èK9"…g’-õVÔ´;—_Ýœ¢÷ºÛ|äÓä¡É‚æHs¹l|K­ºî?É+¢YN˜Á€¡Eº#ÚÊvâ:ALdxèúöö–iNOO ‚C¦õKPÈ7Þí†nÍ”Pyù9æP]›m´âÐĤ™>ÖÙ#Ù÷E½1¢Žzj¬Jévm8¾5Შy¬‰o2@î¥%»‚¾\Ÿ¸ê@&gggüð›¢þ¬­­w¡NèÏ—¾°°p}}]IØùŠ)`R%ê´²¨Ïx#Ó¥Ö2‚ˈŠPê¨!{2e¶ÔÎŒZƒØ.5Èy=ŸÆŽ¹¹œÆ†¬¯°DÆR•=:8ÀØFÌÊò2ÂPí!VV–%feY¤ƒÃ* ³þý÷Öâ_¾ìlmosµ¼÷럈Ù?8€ŒC,^FŒd(1WˆGøqÜýsVjZ¢À«¦U6¨# ´áROÎ)MnRX5\ˆ¨¶¡èňmJ/ X·ÿ‰XC©1•¹=ÒpògJÌÛi.WßR#¯{ÆOêì€eòظªY$C¯­;3kdÄäE<¢Å@pË*¸Ç’cöÏP+iÑSû%ò°ÙF+M\9è£VýBn1ýúú&`ïøºûçÏŸ À ÅéÁªíkÂ0jNð»ðøÕÖ£¬a¡>ø„‘xÆÏQ:ˆ3¤ôº²¦ÄW)ÛgB o6_CD©MÚˆÕ‡G©>8ŒYê™r*'ø¥vÊ{ü IˆÙRk*¢ã¸Cqk°5Êå|¿¼Œu§´D*:ÓúqqE*onm!ŒêCBmnrñº¥Hv–ÏÑÑÁü<—­m¬iãRÀÂåó dŽ-„ñ6Ä´…ïã¸û3á‚TDDQ(Q&§ ˜£-õ½¨ÓìêMÖ–º¥€œPÝ’˜2ö-?¥Këö?‰Ñ•OóJm©#wcBôÉ€™NOO+y}}íýû¥³³sháQg…z]VÁ+±a`И UoÆÏYŸ2h®¥•¨Ì /1§ûvQpÌæ K˜sŠE=º®&¬3ž•ÎI0‰Ç3í<Ôg.¼Å«ø9JÇYI+=ïK¯+k1“Ò¾< ›Ýù ¢Ô£ÛRk>΂Kl©©‘fDј¸Ë#š8XeGŒy̰¢Ô•ßâ¼–’€«!8ÁâX8"%ž#}!†X…χ^{ˆ@ã+,Içç%CŒjÄ`ßy÷éççß!l1óï♦œ§ÏÿåñKªq¢@¢@«uÛë &ýП3”›e‚OÜ?B…ß“íªÍW´ØVô‚¸xz>JÎm•rXW69d²GI'ÎìOD©ÖÇÍÄö¦-†‹-µ¢ÝøÁ øððö ˜øüé_áÏÕÕ5L|°?Ž4¡‰02Á”GÊçØ0ÌE˜ííõueÊ·±Î²ù¬G }qtt¼»knþP–À¼{ø·HFs<ùIá÷þýûëp^3C©C¢µõuP á?Ï•Š÷5‡© tî†ý•’½ ˜¹Y<8¶-µ¬@˜²_jÌ:úø¯Zˆ-µJ4[ê1Ã&7+òQË/r"!,4ã™n¹–(E<åmút–ϯ_70ÙÒÜÞâ0†ü°™…]3Ý*±Zg‘)£Qx0†øG]»^ ¥†$ ¼) `ÊW”ú!bÀPÐhÓ6èÝ©|›Œ_j~¨¿Œ§¥ôªJ‡{m­9°ÿºsŸ ;1%{‰È´Jóˆ?¶-5‡V@¥ÛíÿaSVÅðª³íÖæ§ÝÃ/Û7Ý.F‚ùlÏôNUN3œ¸iCEŽÖ|‹'¬¡[C:àhE§3ßëv7à{ò¿ÿp~ñ¢D-½666ÿûï?Z?û:ภnŸ?š£R©‹5(.tfgq‹WœE` QpÆü£º-dbçÝjAØE*¥˜D 2Câ„gË Líý­•ûÛÕà+{3­í‡ûV·u±¸x+æpm¨Û{_v ¶ÜBxF•â£J÷ãi>¶»sW÷÷‹3×óK·78"f"viy ²[cLXZ~bRšé³þqûxgÕœ‚Þ€Û4è|‹óóBÁfí[os¹½³n‡Õ&>©G{è1r~Ê·c÷¦>Šmg.'Òj¨]²=ž~oŒ‡g·'WƒóÏÓýä0ñXÓX?ìaÚ¶#g®¹d…b¼³wpüï­÷õh΋ËËÿƒ-2PÞ»¾ì¯q— o·>ïî‡óXàýû{Ù• 8n´» ZÛ4æ°¦*¤Œ´mœn¾¸¸¦-û×0N›·* &p@àÍ?’’ΧjA4‘z¦L&N¯g·§WƒÓ-Ø›‹kƒÌ͹jÏ€[ó’œÇý>8ðþá[Û;û''ßü cU©Wþ¢Sœ¨OÛÎ]hpÛtl©?|ø2Ê>äâ&$hÕØ:‹CF ÖúБ1?~œrFŒlè8[jhü0ÿ> }ú1d.c1ôæL~ü&%13F`“l©'β)ÃD?NJ3˜N{›æÖ¬œ×ží÷(1UúQ`ަ¤Ìûu²¢… ÜÛ´¥þãäÅUyRú4Ú|-éÓ/Ž&RáIÙR³2Ù!ØRc¯Qy8&›°-5 çh¯ ŸÓëë±>€ÝØ{œ`ƒ 7ì1€UCý¥mÒà‰ãÌHƒø6|s:»gØrÁÞò€OÆ@þÈXczˆ8@àÃÃ= Ñ4fVcº3›l©'£)“DçFœž€x›ÓóÑʹ'g+Zp€¯roB¶Ô¸I Å ™~뤟ŠO¶­t{ÛÈ“—‘) ´«ñUòKmã|6 žý=?¦Qó”g¢@¢À£àîDæ;Î<~˜!gK­~rjüRçö6͈PPj)!] ˆËbÅqÇô2ÔÏÆ3ÊóÑþI ž ß* Á–!Ç]Å€¸ë=´Å0ΤŸž=?ÜêóÛä—z„^hW7”üRÛ¼ð*üR?¿“j”(ð&(@8óóf·'Žè—ÚÍÁ5PjÍ:³#$JM…:Ü3¿Ô/“.ÕóM°Xjd¢À«£€8ðл®=J K½ùjfÐë=Å~:÷­’nPê±üp‡¹­â~ž˜'c¾>¿Ô¯nÀ¥% ¼ LÞ–šJºØR[ îÖ)Z£w3j‚Éù¥~)˜t©ž/ƒGR-ò[jt(ugf¡Õ™ÁM‡÷ÝÛIÙRŸFɸz.ù¥ƒ ÝÛ´¥ît쎠ȶ08ÃdxÇúûïîñö7 âðÎá8¯6«»Ÿ|ˆ ¨¼’°2=Râ^áÂ+øˆ9ЩóïùaðÒÏôðB›·®yµQnÙEwóÏSÊgK©ÚR‹¾îíEˆROÕ–z¶ÎÓÆ¿Ÿ-_¤Š% $ ÔS ¯[nP©=JMój ÔýÞíäl©åäLÛîhLÕ#Q@:ðMÚR{\äâïÇÇ[ÛÛ8‹ÿëW¤À,ãád %~8y·ð”¿U£ýæÞ-’úß÷ïg1xµô±ÓcýÀk‡Ú¼uÍk‹nþIJù"(0E[jšVÍ]¬e¯T Ct'ÙR¿.I•LHð¸oÍé ’J­²{p°¥¾Šýtüö6Ù°ÏžO"c`´£úÇx}é¥ËÞ¤-uù„å¿''ð‚Åù—3ÄS’p‹‹ƒx¼Å-⸀¬×X¸MnU\Yñ§*÷vwgÃWì÷ïß™œƒð¢Fâ«Iðw˜GD\§‚˺ãhBzèñ1AÄ×Q"ÊÅŸxòbsü€pk¤T)Fâ[´¹™äSžŸÛç~#¸üâÝ.‡Æ}“ÐøÑ 4MF?ï¡dKËe×ÿðÏ?ø߯úg)]ó‘Ú‹z¢\”þ”³ªI ?O LЖš¾«3[êJ¿Ô‘ “ôK=m,yjù?OžHµJHN¢ÔâŠ#oãÛ×=8\Š8[ê~¯Û†áÔÂvg$t¶P«7û­ôЃÜ&Vƪ_·-µ9 ¨abh´¸d<êÍu÷fCùî@¯[Ç‹¸¿~ý:×éφ:´Ìüëà¼â„‘ü"¯GJ‡ëÒbÕ †"&"Á_Ž‹!z½{<£êÉoyS[ŒDæ,hs—¬í3筭͘òü\”ãÂåB×õgtëßÝñê ¢õ¨êÖÃ]n÷0¿‡Óå²ÐRìà¾9½°Ãʉ)ñ*6_òï÷ONN‘¥cë ¦‹RôK¥Àm©ÍK^´¥ÎPêV› >Pb²¥¶ûÉ_*פz' ¼m ˆÇ‚ È.¦_ÄÀãÝDl©÷=A©UH'”z €n¯Ì–šzdÝ3Ê솈Ò8kŠŠ¼oª'4EÀ¥•£÷iØÆ+@×W>==¡ œz§i¢ƒÁ—/_Ú Ýš‘ÐÈù9e]㞨¿08Á+d ôYE”:âë(7K Ùû÷ïãÍÞDv ‘€«QCä†âNO žmKJè¯þsßLÀØKKví|Vz»µóå ÑzT2¶.~XY bK·wvbýcJ`ðPG¿,-ÙÅœ\”ÌÊß¶X} ­Ÿˆ-uЖåFûÁ/µ %­V÷~Ðíõïä9èë$”l©½ÏÂ×ÀD© ‰oŒ÷¶Wô”|[j|±¥~è÷`™ •zv.ÙR‹Ýà¨è»peÝ£yüÐùȧ‰­Óaé§ìõã[èg¨WÝ3V¹lKÍWP1¡6†¢Éh´u硹ââ••eØTD¨mY÷ÖÆ1|ß[XX@þªC›žÍ·¨µdÉ;jÕ¡Ô¡ h,Ôýõõ5Ÿ\__3º)T诸#ùúÚŽ9"jˆÊœl)ÏÏqÁ3P9~î»Ûíö,cÊ9"žÙÑT–…Èrý}J"Üšg 4‰…Šákú½. LÄ–»¸ƒ·ÛÜ÷ñ4Þùk4±wƒ^н»Ç¦x©¾[Õ–Zwåĺ{so3üº˜)µ&QàP€õŒbÒ=…ó<‰ä ûr²+g€öÈ÷)¶)dÒ}}Vο§EÒKuóK8á#ó‘Oó*üR—m©A @°DMH•?@ȧ§b‡PþA}ÿ~  ¯u¶Î² ß’W™ÕÜ\ˆ5´I|EÜô} M3êƒ ŠÚzœ˜)¡¸Ã^âòò Æ'¨6#g‡aÆö6"·BAs,…O—r_Sno…Ï}lÅBË9RFúÖAk/—…È0ÞÍâK›_‘Ò÷ È(&Ié÷º(@öÏtÚ±üRCè÷z},ÄIßScnµÿ’óˆv‹˜Î X—Ê,ãty¹i »}[á×ÅK©5‰o‚š³"³æ ¸)Ðkœ&YG+·§Ý¡Øú/׊|Ѧ¯tn«žST§§†Óöi´×*5ÒÉðöQꆕˆÛÅ1=L/`ò âhxÀW_vv`—LË(y|þô‰¯ðçêêT[|i `dê滌ÅbJØN0ÛÛëërJäE9šqWÚR“VÐMc‡ úà&›†‚èEhttWâS–©ÄÀé¯// õ÷)cxyy9Z³T–M;@ɸ?P™ÒçÅjØ+hØ­)Ùó§y5ê´@¶nÒ´Ýn×=7žx‡¢~…«r-ÞR²ÍðK-逎-rM‘ijÛæýCNÇ¿]|Zq‘çÏ©†‰‰ t»w"4ú³%”º5ƒÉUÈua³1>>Íoû’}¼ßƒé¾¾R¤›jö?¹·Àùȧ± jjLO›ñ~Í¿Eë ‰ÒO}Pà¸4TXÇxÖa~aáDâFcyiöƒ…ôþÞ>Œ. s33§ F *„;L÷™®1]`ÌÈMqlmŚǷªLh­4·âjŸ@•GåRˆ•~kk«øyzŽÎø z6"‘g¬>·”[YJOv$>uÎIøÊ/¦b.P ýÓwueYhéÉÉ1<~ÀÄ%Ö?¦Üvµòùcsà£!ý^œ-5u8>ùÞDÁN¡JcwOK^~ü{Z$(uÍþgÙ/u”Éñ¶ŽiðjsßÒåÒ›K_ÔþUž•q„î“á<Î߀ˆ{Làw™¯à¼¢î~ò!Ô€¯ë†´BJÞÕâÇÇG1ºþ=?0!<Ù¡¬á…6o]ój£Ü芻ùW)åó§@´¥òd<¿ÔÔ“s~©ÿâZ_ŸíÙö®Ó°šÜ%ÙR?I5LHðxP£³tÒ_öÅ1÷N­áfÝîSl©MŸƒ¥/ I?Æ¡ºémÚR—Wß¿ßÙÞ†ö|ssßÉñÚB8H†B‰N×á´öxaóQß×GÊxEKÌÿû÷³˜Ã?š—ûÄ”X?ðZÇá…6o]óúÄ¢›’R¾ LÆ–:ØX›­-ÿË Õ w0š¯ëdKPê1@R%ЏS[j=YRá1z®Õ¡Û`ðpß½}Š-õ]÷–Ï´åÅʲRüp õjÎê¼n[jîûnY][“ÅY»/Ã]ÜpúõúãÇHŒWp^ïK8Üž(·*®¬x{•½Ý]àÙ¸@j:‹@/ ¼¨‘ø*FüÆÝã‘EW«à.îX7¤‡D|%¢\ü‰'/6Ç·FJ•b$¾eA››YA>åù¹}î ‚üq×:ïy‰84ÆV l2*€üy%[Z. ¸þ‡þÁ'ø6Ö?Kéš|Ð^Ôå¢ô§X%¹ü<)0[j½O1î¤ñðPjý—wÆà…©• ‚U î2M[j³½» ¿ë ËíÀ*Àñºµõ·>þ†ÙÅ ß_ßÜÄøY_ßDØã§gg*MþÆòýâçO éþ[„Óë÷utý'ÂÓ“˜ùÅE‰éIÌpñçÉ©V‰‰C) 'CêPj¼hú5è J=¶-uë^¯Ïš…Y6.>£M_~oÓ–š‹¾ºn$‰¦Ðð"b]HW¸Óè5pkHà÷õëW\q<ê nGf$8Mðï_¿,¦D€à÷öö6Ü0Ç”(ˆeA ELD‚#¾÷ðð°×»Ç3ªžüyúHd΂67·­ \ÀSžŸ‹r\ø¡\èú±V €ný»;´ + ¢õ¨êÖ»—þ¬, -ÝÚÞþõë´ˆXÿ˜¯bó%ÿ~ÿääY¡ôïÇÇC;*½|y˜˜-µXGÛí‰ÊšE©Ãxã¿âZïß`K!‘X‹C.ÜÜ\ñ(`\ÿëWOÿAÄþÁ»?x"ìñŒ‹ËKÜ«„d+´{Žß"Ä`ÔAeG$žÇˆ›3܇ÉËã—TãD7O^7<¨±¥:õ BNÐ-•ãaÕ÷=Aò¨‘'4z< €€¯Ì–šÊeÝ3M¯JCP.ÐVØ.G%š"àÒÊ1ëýÖÖÖð ÐuÄ•OOO¨gך w€|#%ÐnèÖÌ 3/?Çôê¡k^Ù ƒ¼B¶¨²Š(uÄ×Q¦o${ÿþ}¼hÈn!p5jˆÜPÜé‰Ò³mI ýÕî› {iÉ®ÏJo·v¾|!Zï÷FÏ•e¡ ¶ºB¬L >êè—¥¥%V uo^š¾6LÈ–:w{¢Ð¨Ý2[jÌìK~£-5¸«@×ÃÃ#»=‹cPa iðDØãª.¯°æ›é£àŸEp}'Âx[Ž©ÃHâ^¯¡R{^;pA¢J†j¿ÔPd[®/ç-åè~?æôz,9Û=®¿‹„Rƒ€¯É–Íá źgyu',¡&*l ³êm&ÆôWwš+2®¬,C(2PÛ² î­cøþ¾T ù«mz6ߢtjÉ’Q•¨! ñuL¦P÷×××°]|}}ÍÚB7…ÊýõÃDš>Šd¨!W €“-åùù`pBÀŽŸ{ÉŒ[è¹Ò¼ïôìÌŽ¦²,Df({(ç$Â-­´â]0ø$¹(ð=ò:ÂÿÏÞÿ„¶²mk¾`ì]çAî©qÁjÜ-È2Á‚º`dà nà ^‚ÝH°¯@«§U`7 –›2¼†Ý(Xn$ld‚ •`7¬FÁrÂkÈ V#Áj\XzdÈ ê€õà4ê7战ëŸe[²%kÄY'v84ÿň3¾øæ7ÇœŽ–Ú9¥ãSÜÌCgXêäè“(=Ü≢úp¾%ñ@f‹šÏ_`13"'>vAÄtÇéUd¦ãðZìü“À€IK=¹‡&Ù,Í7¤r^L¢¥vl7Û÷½=D’ªLäqðíëWý‰?766¶¼@= ËØ/*d EÒpúöômóÇpOZl³ÑLI9e/㪥ÖX}ñ”£1ø Æ|T¤QD`£}¸’dÊAkAH#ž£9È1‹¡â xã{5Ëк¼´Kz"qhÊdK$ëë“ßSK¹˜š–:~¿DWÝëý–˯!b®G>r>— x8+åÝV»ÍÇ1ZþŽÆ¥VÅÛT÷kÅ¢~ˆóðƒ­Eý—Ï)f&s†4̆¢fFÂæÖØ`eŶö-ôg>.žž^äVVZwwô?þÔiÈÑ™­­ŸŽs]$æœFò(zw±3f uó„9ôðíA¯œ º••“t&ݸaHšñ7ø‚0¿š§?­×™\PV·‚ªÓâÆÑñ1¿º4 y‹Ú²"gd¥qÎëü`švåk¦ußZ j÷-yh]nηàvK3hŸÍ­ò]«5´ΤRa:Í;"—ÍÊ݉ßÛÕîöjð}'¼.Ä[|°‘.NE4"Œƒ@àOyš`•à~ Ÿ+•##A¢Ž®c¢‚p¬ÒŽ‹³3¦%27–}åà ~s£ç­¼¹ÈÎÛ\C…(w«ÍHƒã‰Oü%¥Êý¯¯3‘é¶/ê oPãíV3—ËÓ$Õºð¾¦åÜ\.‹žX¬¯xt_Eqö÷÷ø$J™4Aá­rpß*ìæ u¬q»ICCëâZЎ¹ÓNt,Ú~Ÿ’`@½üdù˜Ë䱯Ô—­Ù‡góÛà¼”Ž°_*…Ûh¸¯âPž%f"6‡étîÑ)•˜Ù÷£Ùê|Zi˜b.…þ–_[¿¹©7î”'`ß[͆{ûû8>Š'é…Xû¡Je]“| ÇLL¦ 9ú…SF¶ÎÏñàr©„xƒŒîƒ3<ç««¹ÒN‰÷²ÑÚhl^uô|þê³ÇŸÏœ“†ë¤Oñ3‹÷öÊ««É3•ñ×¢|l3 ˜Åæ]±¶’ëÜÎ}¢ŒÂ9í²ì»½î&“ oî{_Âݸ÷Ô>tÒ}Pù´:­BÐÊ}ÉÉZìøI líÈ»fè;…"¨Ñ³çò2GͧÙüÑÞ-„{[Ñĵ©»%oCQS·êË ñóÒŸâ[‡ÔÉ—O¶ðüðñ ŸLo ÅÇÍÓ›^í[:ž/wÏ#Ï”˜(ö]4ãüñkÀéžTOhÝ;8©UZm…Ó2þ³‘ V …HKÍ 1D¬¥öª#ÅÓ:67ícà/hXBEžž VvóŽ¿~û? žæÛ”3îÓö }4gö¿}K¶Aý bÆ fÚéÎìFgD]-Œ;¸ œ4àuÒkèñ™'®kQ¼ÄÚi0 ¨ZíXŠ9ÂÇÇÔûBìGÄÖÍ+´Ô2JÌËùÅJbUv.ó^°òˆ÷‹‚ˆ’iÈ’ÔÎNÝí OOݤ/.)’ÄõzqvŸ ¡ a’]Õ2AÓ¶fí=<¦¥r8^ Ñ­).i\Ï/«+pÞMWMõܤáq©“ZjIi©]7/¼‹rIcÚÇÌ|/Ö/§üÝÝ-´|A:ԛ㠔§§§LU„ÆÎæäŒoÃ4õúOôðÓ|ÅrÞi¸3ç+¹mfŒcMÏSä 1û$;óÄu}0g²Ë1 |x ´ï$ŽG&è:æx¤Ê™Õ^0…ÎZz™–ZzOÉlZêqv~â.øˆïe¸Å]ý,šxæOR#ŸI‡‡@¯$y²Lh¾™ºÙ“ °3²€Ÿ¡§x˜Í'ÔØ>އ9ãa~•z¢‰²¾_¢vº¸ÔZ›®,µ†iu#¤³á§gÁyϨÌÝT+Ö,`˜‘ºÐÓÂR³Ç·”špQ;^—Z8Š“¦7.9Óü–]ÞR#Æ?#®ÈÝždwbè|¶é8”±ÔÓ±ã<•Ýöóg¶ÎÎÇÈ›§Z[ÞÎ1îôÀdUjèùø]³Ô²ž‹;–ù&Ê…eå%‡GäQ\jý;Ú+ñ’@ëOò¸3â°ç¤Þ·»ÏV“YÀ,0 ÐÝÑA&ŒçG‰£G_' y?“«nµîˆ‡OîT¸òd\‹ñ-Yæ_¹£Æ?åƒ'¶'ÓDç§á*CË0–zf¦µ‚Íïo˜¥Žôô@ýFcbò%õi´×wÍ –ZÓ(~߫ծ<÷Üמ¾B0ûØ|ƒŸ}ßɼK|зµµµf³9nm0 ,®¼–:êO^—z"-µ‹Ã'$ýÖPþ`¯ð!Ï/®ëX˧oÀô—/,JÄ2\Ó/ÜJœ†:Ž¥‘óSkºU‰{`:Øè-u³Õ¸î^õÒí^¦×jWWƒöZû¢Um¶Z ûd-Oꉗ¹½áCÇBÝq³éÝ>™fF,5šæ¤ZÛ¦1õ’{ï¤è: ‡ú,-Lj©ÔðÍšrðk ï!yuT‘DØJÒÛf0 ¼Øï ¥ÖgX8’üÁ(^áCžñ³ŒÐ•Š ôðb»¸Œ1šËÜu¼8ùI–· Iíf†…ŽòÓr|qvÔ)æÊavÛá¹Z­ÃnÖï² Q\k”:›{²c©ÇX@=hèX¨»#Ñ:¾}ifÄR£{õs¬Ö¶)»œÜ{¯ûI‡}BŸ–š¥­GoÉø˜¦N^¸<‰°•ηÍ,`x±LKýžzîß6Ëø-EÝnù¼°Ô j›€?÷¸ëÐIx/¸úIö—€ÌP$}·%JA~­fïö¶zpšÍÜy%¯Óî5ˆ—§’9÷=u}Ž {ƒ!ù•0=A-ÆR±€`åc¡î[%Zš2™ÆÝ…¹ÐR‹Åóóó¡Á˜æòù¤~ƒcÎ$ IæÍåsFCåÀVþ›¿.ÇZ´`x#-µ®/]>œXB]õ‚9‹5w¦`HÍÛÛB¬1ߨÛi…3*ü®ì_»ÁEkF5¼[±‡©s)º²Ì“üq*•#Žéï¹­ª™®]žkÖ­°Ýê„kÂa·kõ;VÛ IILë\ÞèWZ@Œ¿°Zêï߿󎠊ò9øúõ«ºþ¨ l|‘‘ <Í1ó“L2o©Tfz"\8ì{·GË*6 | LKKÍ4ù´÷²­d\jÁÊBÖ„nõDǘ–:æN>„½ÅEÌq4Ó uªÊ`m-H§{ëëB¯à¨fÚŠWÞêåZp|\4ƒí³àËY ÌîÇØš©„÷PV`Aû1n]Ãt˜V–º×ºšQˆ›_º‘ªŸ×›õV/'VÉÜ^G"¨qvè&{­ —Ie~¥ÔëæDKýÜG —Ë‚ƒ-›Í «è­Žd>kôQAööößH· eÍñææf2m’¥&1²6’itÛÌf[`ZZjmÈ &c…‰ÕÁÓªÑr,µhåµdZj®Mæ¶W­àÓI9 ÕàZ†Ð§¶QÚj5È'Ñàç«J~vj×p‰bQžŽí/B¯)È^œ ð¼}ÔšAi-ø±äÒ÷nãô…¨¶{·dŽåßöEp5Õ[ÿ2£¶Ú]2f p'´€°ã÷,'NúLO\#”0g÷A!UX ÚÅb'[hógVïÔ3ÍAÝÝñÕL«=Qù“´a™Óè]ž-µ6Fg"N¸…íG»Ý¾½ý•L£ÇSøøçÏŸH·ÙûH >}_^‚ŠÜ¸mTt‘ ›jÉÌfyw;|›èsîU£á^7µÁÍâx4—ƒs¼MÜd†{YœWçê¸ânÎãÕ£È.Iio`žõƨ^NlvƒÆ]ßœ™‘–õ\n%ø¬: øK} ¶J=@æ ÞùV à _º7öÍWO^ªvoÝßß­mì×ÃíZxÐ^»Ëd¿h’"ÏÒ `(lñ[rĘ‘{’þ^ vóÁåvO˾…Ø?kã¦ìÕ‚3îuOn0ý3?'r÷¹qïµS¨:K=Y¼dÍlÝŽ°Ùµšçq9ðe³¹|£Ñ ò²Âb/Žû‘k Þê¥ïïEõñ >a–¹y·ÔRGã¥ÊùØSbÿYj©1î{9³Õk0 LÝo¤¥v {˜–:ÒºÉïүőA—äxê·sŠÎ·Ôß\8 ЧÂ:¹¯ƒÖèšÀR»ç‚ÆÊëA³|_€õíJ@öë7ô WÍ`w-8ß¼~|%”ês7à]ñ,¨\EÁŠ‘=ìž ·úlë5×Ùân¶|Ò ÇÍ0·úãpõ[ 8ì9U£c†w§œƒº |Zõ‚Kãî€bSn|€OŽñXö¬œ6Ä’Õí › äV‚óÝ`#'_D»—Ï (ÕD:²“nÊÁ¯½à`CÐ9Úrî~þä­?¼õÜJ,Á 0ø)~Z¹áN I4DC*8= AmYŒÞ´ï²™™‰:œ ƒlç6hßµ›Ý¬ó›v¨ª§YpKóÄ(ÁèwŠÞAéP£º;=£ˆêEÏb©_ðØZ³€Yà-àYjÅ´|¬Gr„„:ã¥vÝ[{\¹mI‡NΑÔR«ä÷$ZץÆh©Ý¥‹2Äõ}Kx<õ{f…›|Í‹\¬ äKÉ€ûóæÐÚy°˜„u¹! Á|€QAØgÃYLpp°¸|5D°_ Žöäaa_ÃV¢"±áÆ{…`#+%³D»Mç3@‰)à¡¿ÖŒã7°>†ºn žþc3h•ƒòšü· }–Ý÷…£z/½›í¬÷òÎ@¹O½2VzÖ†|™,[¹à|K¢J U ýÏj u¦sÇrwôã€+哃3|a·A+a–’L£ *ÁxÚåbŸ ƒÓ­ ÄÑ]°_˜dƒŠ¦üä (¶eßÞZP/—;ÁfNŠåîca@üÐ Ÿáþòkµ)8ž8Ìsiò¡%·»NÈ茥îÞË:ˆAû>¨ºld:|A¦×•9Îdó-f.®JUÍ“Ëöùi¯sÏŸØjÂò—™žäÚõ&}¿švÓü{Ê¿ƒŒ¥žä!µ4f³À úû“h&¡‚¯kè ~èDâ²ëªäØõKФé—Tò»NUt2U”ÔRGJÏ ,?í¯wêN „n²úüM^ bØGÀ(Jõ ƒ@jòbIÀ Úä‚on„„Üy˜‰pxë¾/j¤vpÕÑÙ;§rr‚`m%8¹Ö^¬²_Jiûk¤â¯{ëA1'Çì78r¾(ÀÙ@4÷ <³^w¥µ¨P¸¢õ•àrW²S`e3j_pÕnìùävÕè}Éí‘l7ß㛡´Ú»Ú ~lôò(î¥$%Ä n­ª·Ý&]•‚ͼ´œ61'áÎIÆ5 ˜‚­|PÛ nË‚ÎÁ²l@s¾s²Ž·æÌ TEœƒ)°jþIß/ŽóÉÁFúÏOMXÄì¸ à*ܽïZf!+QlŽåi ÆOhœ\­Žó©Ê¯ß.Çó'‚”´™d¯Ùº¡4(v'd©Yü,½VWYs1ØÍÂR7Ûí$Ýj…ÁzªÉæÚÂ]kìä‚û‹õ1†^ïõÐ1OçQ ~(ÁgÏ-Km ÷k^Ëkx D|³ïO¢™„k©Ý’»CµÔÿ§?g2år¹õ¿wƒ?ý)øë_‚àO“ú+ÂFá_wÿÒc30üþþ¯žøë_¸ê%9&˜ÿÔïñêß×ÿü·nðïÿ1Hý9(2²<ñ·÷oÿcð?þüßÿ/ÁÉ¿ þáï‚ÿ)þé.àæý§FPýG‰ ¬yî¬ü¿¡…èÿëFð¿ü+ÉÍý…¿¤©ÿð/‚ÿe5øË_ƒëþ7°Ñ?Ûÿ*øêÓ^°ùƒÞ_ƒÿõßÿ&'éÅËþ*y³¶ÿuðwÁ?ýàì¿ÿß²ü‹çRøË ²‰%k{þ!'ˆ¹ù?‚ÌŸƒ¿Ï Rü|*íIÿ)ø›Áñf°íØÄÆ? ÂþÿPï?I³ÿç\ðÿ­\‹¶rrvÿuðw–»ðOÝà?þWù÷ßþü]&ø[4û6óïþ3WìÞ_í²òÂ_Ý•þõ_gÿºû§fáêìŸ2ÿòÿëßü—Vðþ1ø»¢þÒ r+VêÛøúwÿYìüþçà_ÿmôã¿ýòXýÓ_äêhÉç^ºaós9ÿ¥-åÿД|ñ_ƒ»¿ÿj%ø÷ÿü?þ>ø3Ñ“ÿä2Á¿ËÿKA¸5ÿ½ðXC¦‚PÉõïÁ_¹_ü¿6ìéï—Þ»ù·¢á‰»F]ÿ×OÁßýÍp?ÚþO’ÿÿü7î»÷l‹…i _bÿ­ügw71#·ƒÓ’üßÿ2ü+îc.øûL¯ô×Ìþ·ÿz—úÛæ_Bÿ—þþï†ß‹'ûð¿üÝào¶ÿTû›¿ý³Ü)dãö~î}ú?ÿ}»ý)ówÿâ/A;øßhÇßÒõE¹R›mÿ÷LuWÿöü=ä_sAóOÝßþ\x²äIj·4ÿ"ÿ÷ÿG—%w¢g3ù®ùóŸéÛþôßä '•úÓŸ|šÿÔèýËÌ_ÿÍ¿z~7÷¤÷¸Äñ˜,áT¯ÉûâJ-£YÀ,0¹þK㟛ÿ=Ø*ÈËžwÖÿ”JÝÞþS*õç?ñÇŸþôWæâýþ'÷î`/ÐáOá_ÿòRÿÿîïÿåêê¾¼ú‡/ÿp÷PøŸÜ¯bŰÿïÿÃoùüóˆë1á +PÈ¥÷ö÷+ßË­f“˜=wD•rK j”=Ï%,ÏñÊ+º×Q÷X‚ ¸Ùi@(6HJP5„%d! ×ôh×€ݨ ƒh„+†\i`bJñ@¬Áº`nB{_Hi0Áè+V²‡[E9@ãi$µ] ¨¥ñ°’CÓSNõZæÊõÒªtP*Š„c’†Áb2+Ž|;ÿ¨%7m‘êBæ…M”z¡Ã«_“·™Ú@„¤„8E‡ LuE؇”Õ'e–'GÊá¿.TqÄ»·{‚ ÙJ­‹ÊfØ+•9‹¢L†azÿ[·Õ¾Ù9¸È®Ÿ£¸PžÙ•†q°'|­ßD?ƒµ¹}ý­þ–/øÁåe?€Jå2ÉïÎ8Y©°\è·Lß5Ҍ۶”Ö숹Ð5쮊ÛHÓFÜ_=yj¯»KÆv ¢Nnˆ4p]Î1ˆÁðÅøÒ¼gÂåÓéP~žÏ8%·Þ¯Ö]xrÔC\‘Éö:ÝóÌF%»å†D_¨ýI—Fü#ƒéoA&ïT¼n´môžª6³Œ¢ñ«z+Ìnõ¥ïÜÖ¿ì¦Ò°gæú>›ßx²äIj·4[;e"4}¿ðÖYYY¹ºªår²ŠO³YíîÂ=hf³ ©äVL|Áöš¼/¨Î²˜ÌϵÀñEóô¦WûÆs§Ñ`h÷ú*të$ÞñkR_½N(Ilxþ³±¾¾wxR9ªô)Iãd.X]-üFWu{{{Õ`h“ SîmMTÊ%¢±4T¯Ë¸¶²üôRÏ‚uPÔÅÝËéØÇ Jîè˜!zÓ‚<¾ä… TM>àiá†sAuëÁ‹4¯nE-o#8XwØäñÖerUWÒ„ p°8ˆŒÇ×-Yfò|YršNRpöbWŽG¥çNo£)Ԉİ\ˆa€•¨P£CìL̉vxÙ ÂN»Ù~l8#X›½âÑ1÷è¹×>4=BÐÕ:iRå4{ áÓ,qÙèLDHó ›K^‚¥TO±zN „ž nêZý0(ÖÓ…fj…ªñv¬×ë‡ûóI 5ŸWÇéoéìÚ¨/½¾óí•«ín&̆­ÛÎ È?Åã}_‰½tç*“O§SAó¼Õ+ˆ_Žÿ–°_'´Àæ–@jìéùi 9¦y¼êŒ—&Çj·«]Æ£¾ï¸Ä…Ý®¯¯ ŠÇjˆ£Ö_œÏ+ãK’gØ|ûùŠ1pðBÈ­‘yXã†m¦2žù4£µê-pxÖ8¿ ÎKtçQ¼ËË+Õ#<Ž£§oedDWÍÖéÜù²Q>8>:úÑlu£~) y9 EXêüÍÍm (+%¨º‡¥>–ºÕ$´¼°Ô‘ ;ª{yøiåæWV^>8æÅŸ|ßã¾õ¸OѧB'Xgæÿ¾¹A°¼°ÂcÞè¤Ae«aìò+B¸Šú³#*dH\ÄMøé‡BèzÔ“¼).9M*»)ƒæìÞúÓó¸´l“9fÀȲ¿é_NÝ÷Æ®€È¡¨ˆJÁ¤’%¤mÉ4|Оà¿-Í$ˆXêøÝ–|¿Ð K]«ézÝþý·ùã#°ÔŸ²Ù[yågÓµi`š|ûùà€{Ö_…0€Ôüñdz²[b³Àk,±Ô¥t4_î^YêCÎ/º¾Wš ja©I0’¥.K#@ýÕU#"úRa>—ÅÅ+{%æ¹ÃRSD‹:¾‚A.A™ÑùöÝ]yoâõõõ“ããôÊŠ¯«vqq|rÂÃÌ#ÍG3 >}ú”4ô¯_¿h?L‹¾òñP­VÓéôà™ñíŸK-¸6nèPžì{×ô  Þ4vˆþÇp„p¥"GæÆ>H¤œÐa2(£ð¸O6ì¥{]&fè¨Ç$¼#÷7fÂÕ|6Õm“ä%hX¿"À¯\æ£Æ­EŠš°Ì§ëý€Yu9ñ˜ fÓLàúá„G•œœôÎ/ÂÝÞövô i;•Ëä¸^ONä\ål—,‡d0ô(à‰:Q} áê$ ’µÑ 0c£!_<ú£cÛzŒÂd{÷Y¶}Ú“Ý£¾r¸MÊš£‘•ׄ´~U]Ý»Þéipã\hc3Øü¢Ó¢Ý¦¯V½‚±>7ªÙMµv‰°Œ|í´[w-V B¢“7}ßÍ3i0+ÅTîr¥ ¾¶r•Õ'úÞ{ö÷ÀX匕ö, ÀRs/£‘ÐÇï‘LF^r°Ô¹l–#Ÿæ°Ô`ʨßs¾Äú‹¼ž «yoóÂrê‘bµzª€›wÖîö6Ë‚þ8:*ɲ‹¼£Òä±Ïλ›25;)ONN*Ö<YÜѯÔx°¿vqAÞøœDë‚ 4ù- ´„—¦?ÉŸµZBúÚ¯ø˜ÚY/û•¬ô¼¸ono¹‰¼¯yM'ór-¬Ç®µ'«¶c³Àì,àYj­‚™———ê–õ4à™ïÇôedCXê{Xê/å½ÊÑÉQ³íf4º_³…¥þ]$DÎÊe‰¾Ú\aýó"ß#.õÑÅBpÌãͱ\Ìg\Õë¬VE/ð}oI–ª‰i·IûËí~Oxøµ§`Ï1iôÌ­?ã² o®çg±éÝŠÌ<ìXTèe×…níU„Ë›v0ºéz‹F—à •j âoðï¦$ÿˆ"×ÜhÞäjçíãÓú·Jmÿàò {´î ŽCâˆuËݶVS°cåÆNâøV«½½rHg}z6›OäÅ£Ú-ÖF)¦º(Œëe¹"š‡:ý"8i€=ÎeÓmŒ}Æ_{ûë×´°W­Â xe$ Ý‘–ÜÜ„W—`»ðè$à*ö¿õ°ÀÅÁÔB0Ÿ·-í1§"µŽ¹¶Eäx£Øƒ%å Â2Ø!q_HÊ@m+øUv¼;xºÑ«'=†;Æ5r:¼ôBhéoû!ˆ|ÿ€Ò¤Ìz½w°Ö. býÛwˆM°*ŸÎܵåOÍn±áhýñF468¤àé±¾7Îhg•`%•ðÆÅõ(•¾R´+‹:žO‹U72ß÷+kíúí·ûël§Ë'"Š  ò÷»•«T¾–Êu2i1D"r‘ÊØ0Õ +† *íï‘Ï­¸í£bIÅÏšÜÈE‰K=aÄ0(½{0%^ 1¤ò¸!^Øü¹»[:>®èÛG ïÖ&o4Pµ Þ´ÿñï&LJÍÎ ‘¢|J¤5pgB–¬e;<<Ì@“#û¼ºâýnæµxvöM‰cnO“ Ü¿½½ÍA_û};)„ÚÙS—žRCŠqrÿàz»//Rà}‚mf·²@„lcì7ŸÔÊRóP5õÝ{©,õÁÞ¼h©ùÞå™vùînsk«Q¯ðWÇÇ'|gÀR ’ŽãgsƒÈ~qqÁè!L»[[?Ý4gr++­»»­­->©Çóë³`©½–:î_ÇùMÂ+·[½ë8²w>»µ(O3H[D! }ëûèÕÕ0½Ò“Ÿ²¸D˜v«[´nÿJ•F, a–/=£K6Tù¼ð¾ŒÕ2>.cêˆZÀ¯·|å!DŠzyÉÌû$M.fs2¸¯ï‰¾k¡¡¬AÒu+áñ#Mä’Ûwa·Í¼1T=dÒò¶qŠg©÷ò\Ô”Cu¢ÑdS‘w„÷¾Ü%´ƒî@Û5kºk‘K(„…5æÌ‡ZU:ü!—ÔK‡éLïÏbTPò=:âÒ®ÔÛéöîZÁ- ëFÈÛTmHcP,¬ŠâÎDp†­]HËÑEh{Hƒ=s¹ÈÎí¶àþV[¾}Ò¬Š=7àÆeW†Ø3®FÊŒ_Îòì'Ï÷˜kq×ë݆XížݽHÑfôÌ)¤ rÎ}ÙGí'Ò³ÜÓÄ8špqµZxU“¸Î$‡”*n¸y Ä×. x| -w~ÑîÞׂ\;ÌR(?›Ë®òªüAšiÝlÞm毮êùR±Öè­=‹+5>û½,ðñ´Ô:CqÔÞï'†Ôê‹fuu•c}å8¦Çp'yOñä™…ëm»^t(KLÉ›NYç¡) …U•$bv+Ǭ-ô¯NßàdK\é¡}ù¾ñÐêdZ$ó&[âóÊ£Þë‘‘w_bûÓ,0# ÌZK}k©åÍZÈgçJKÍÃv{{Ã4ÆÃÝñmßÜðlöƒ\ÅâZ>_ÐG”ç¹Rùž[ÉeÈÖà†Ã&ý£3¿~gòZê‰Þý\ ¸í®¶v *ªÃ2 @§ƒWlt#gÜ ثْô2•”ð`ÈZÊ “3~TG±Á— =RBߺ.;JåJÛÔŒp‡ØWЭ¬!$çC¦ã¸ïîzq×å~u%DyAºTæ´ Ó©%°+Ó~iŒ°„û^÷.B[’<®‹”¤po·Ž»ÒûðÞíµµìSncͰ˜í‚¾`šcv9â½X3OEÓX’†ÝøÜ‘É›2ƒ@>€è¡k˜ºî}¡Rg CÚè&”Æ"½T@-'з».D2vWA:JAü„<º<÷±¬÷ε0– G¯ÒFr!Öð§£kŒP§”æ®Â•guVU½S¹¸&¹ñ(ñ‡¦ÿªåiUˆW赸«ˆöò‰Aª~ÿ”Âc^]W£–ãýÄ_iT2w–ù¼áFàPÈtÖs¬<èÚ#O«´NB ìã»ÒÝ)UœÇˆŸ[Âí1°‡@T×ndMÁ [*uÞno䮉[XDŽÅP{/­–Ú#ѤY×:†KöªQN&•+N )ååùP‰H_ùƒezà¤Fv¢ß°Ôƒçl&£€žm°%I ì+Uµ4à^ëÕ}å÷µÐ7ÆÌ3²ÀLµÔ.âG½¡ ¨§\z®´Ô}˜XÑpÙðB­žVkµ+Îô<€ì¤zR¯ßpFPøÍèðø.¸X=£¯k=£·mW= –º·ûõe&çÇ%©=NžÄ@u l$ˆD‹Häå°2GÎÏ§ÈÆŸ+))aÕÁ™ÉKÆ»{Yè™äVË¥¤^PT„íÜ}cÉPQÒE?¢ÈÆØNxHW‘ö \/wnE5:ñ"H´–”2ç‘ÏÄŒ*£ÊLTÁÛÈpR1V:ncåŸ>‘@ªÿ¾<@`ð¹Ø!ëlH«´)þ]Û¼,ë#*bÕ–Có)ÕK…ÄðCƒO)- @WߥÁò-<=bUy"[õ0â ²^: ;ăMäLúüa¤ L.ýŠý ø•£7_® ÀîXg6W~Oº‡îãò]É. >POˆš¥Ø=ºànnÛÃ=fŸ4íGß.o[†Tº§¶ÃXjÿMòøñzÂß:×rj©y\<>ö÷ö•gönêŽSNnâ=Õ+v¡‚Ï|¶߸ÿ(‚jUt¢@ ‚ÓG˜{w@)!ãH‚÷„g‹¡;úà™G¿+@bÿ@æJƒôÚ•½¥Í‚Ãe¡Hq °I{çµë&Ò‚¸ÇW²!P脽fÐi¦ÀžðÊ©ô=¬P¢ba©ieß÷Òÿ@“ çCmÚèÜ ¶<Ý…¢Mƒ …Üî »ì«ÂW1³®­TWÈ\ÇjKÃÅÌ)îÔCRjJ+.–k饀}ü»'ðIA\\˜épcÓ©® '%ȵv{…1Ú–’‹¢…-Ú›¢rá›3P:vK‰¶JÚÝqW‹\£ÔE, 7(ЖK$ܹQâ9´Pþ¦]Ùâ®ø«sÓpA8…Ãve:ëa0ëŒ ·í.H®0¤D4“íµÓ®Ht! E`[ZØ ²Ö1L‰rˆäÌsr7ÑÍŠÜp4=ÆDÚ”%½6V«H1È’•ûED½ Í „sÇÛÉW _a’K "ÖèR‡£ð¹<ÏÍ‚xq$§ýäz§¶’§ÕbÒåŒK=”oÈBâ€A/>fþ`ŸýýoÙl—CÍ È+‰}höOOžö—ì¼øvvvšÆIµÊ\#Ne©)Šg É5oFTך’­X¤Ø4Ó)uáî^ÁDü`Š¡t¡ñüH_>íá˜vâ´G¿úéÓåÏŸB€bµŸ™=é™{€†þÁ|eÛÌob¾¸Ô¼Ó®Ÿ—ÿ?—ºÎd~÷æ­‡S´Ôq\jøñ&—9¼žRú:ŽêÉ AHô)ÕŸèzè€ø°æÓœ(yM_Û¾‡â!§'â©<óŽWgU›Ìf³ÀP 0j6—Z“}€ˆ\Å ™“|f‚Y³`P^U¼ø8)?vw!¹xý•K%•^0ň÷ aHeúëׯ}Ù‡²ÔúeZÀšŠ@áj[P/`#>0HòÖøˆCÛOããˆP¾*ÂP>²¾ƒ}þ' î)Q´™w7ïh‹øaÿ[`€¥¦—úwuzíÕ˜|޵r"\ÑPÊy!¤bRòíyyêøtfäK'û60¢Dï£S"@ÌœOžÑø$ Ð4ûß¾‘†/þ‡3ûûJ¶¾ýuY½fsóóóñ>àUÅ}ï èÍ5›Wñ„Q;†V>y^Õo°%×IáºP0CåòÂR<Í%#àL.µ_=Qð1Xê—ÝÐäÀ—•0&—.Ú€d ÆRëê‰S©ÝVOœŠ­gY`vZjKÉYUíꢸYö‰¸Ôœ1>Éø$óóóó7ó7õcøxé`©Ÿ|bÅ1‹iKÓãñ´bƒiáiJ£¨™ÞÊYÊÊ\t ôé/d5CíÖ#½†›ûäYj=ïñp*Ez&XÉi™_çâï,uNYê;7iµÔ‹~ó¬ýf³€YÀ,ð |l-õ bYÌf×[`öZj‡¯ôñÔ$+ã®ÃŽÍf³€YÀ,ð62^ÑIN»8›mr=ô`ý¯É;›«±RÍfGx¤¥æ—éi©—[ é{ÂWݳªLµ[XOæGQÏâ°l.Òl¢ÍŽ5ŠÙÁì`>`>`>0#–GÆ[Ýûèq«gf´½f¶ÜkòÎèr¬X³€Y iþþ$ZÐ" ŽëôŠ£½ šE›•x²´šÃÉn4 ©ëú£-µ+?-K°Ç׫Iú´ÔNc3v,†SݹÙÄì`>`>`>0uèS4&ËŸ©÷5Lókòî1 ˜ÞÀ1Ká7ÉÏÓRË:Ç‚ ´Ô¿»Õ5 \µcb–z(7`|ÌŒøã¼m Ä|À|À| Ï’㥬[äíóXjbw(z n4DzÏd>}úDàW"ox`AÄ:‚=ÊfY3¥ï§>âMƒÌ²rJ¡°Êj…¾üÉ1 mÐÄ5S OÞ$Ki˜ºb–Z ÓÊònØEª ]\γԂ›cí*“¹ŒŽƒ–µ\r–Ü”¥a©•“vXÛ­fç¢<ží8uîò­Lã´ÌÌÌÌÆû€×Róö"à1}Lª©¿h}¯aš'Ïë¥+\#1žÙع™°D²ÓƶytüåË/"U»Ôº¼ËàÚfã¼[lå†8Ö/Æxªhvf·’ÍocÇZjÇU?+.µÃÌÇZ‘´K–ZN¨"Dp7š' 1-µññÆÇ›˜˜¼£$µÔ¼½VYÚ×½½<Ÿ4£ðkôГçuäÕkR³`¤þttTÙ-•t)®¥^XÉeðªÁñ,¡ÂúÞðÜ@ó\NHo_>œ7KÛ{w{Ûóß,¾HÔ„´âröúë?þØØÜä`ss«Õn÷ååÏ|>ßh4žªÙ~7 ,žf¯¥3²Ê‹{…¥ŽâRÇg,®…ó™wçl¬ Îv/,ÎŒùÀ²øÀC\ê0D¢ÀŸó¯¥VD>jïÈ(-õÑF£ÁHNU^d9·0#éªûòÁåѤ©0äX“1ëñëׯ˜ï¿O’|9mð‰4Y‹Í£-0K-5µ „v3¥¡Å¥6>l9ù0»ïvßÍæÊ’Zj^Nn _çÃk©“H X,žŸŸ?‰Ž ï=,~ÀÇq¶Lü+?ù˜Ìb\[[ƒJgBd’/÷uå}â'` Ì dYj©1ƒã¤jÍq?3ŽK}×jïïcb„c#®åúúêÛ·}ºÎì÷ºïûøÈ³³³/_¶™`¡j¯äŒùFgF35&˹¾n|ûúõógšQ<8¨Ð§ r¢5æ_Ó1šöÊ6pw4ãÖø’¼^goÎ_$˜‘ ¨ÔW×…Xã r·«+™ÈòJîÖYC0ùrÞ« ŸåŽ8?t¾Á€¦»#_œc¸;ÒÏÏ1”éîHéJîÈCÞ—/´à`ÿ@Ÿ#Ü{Zþù¬gdFmxî3Òíö°vÀ\ìeþà¯]Ÿ‘çúg÷îNûÚïîÈûø'޳Æg ñJßÐþóY}ø ÚÔR'"~ˆ ?@\êQZê$4ùþýûññ±ª5°P˃Øe}}½Ïhô–ñåonm©’„=s5;Ï©z>Òš;« šÍ&˜{°.;cXt ÌRK-S¯+Á©#¥Ú,´Ô<Æ»_¿‹—îËÛsBçç5o°òååy±PÜw¯À$_ru}Å£~zzzL¯àz‡×ðI­»–ìjL–°µ³Ec./¯è\x÷q6 ¼››’W*4æ5m od o ±<çÖ¸, ûëêúºé¬)ªUµF”÷ÇöÛ7nJ!™÷½Úp)wäáZÎÏO™äŽ·*¾#ò8û4ÑÁ1*•ªÜ‘—Û¼‹îŸxl6—»ºªá¢ÌÙŸ–>ë™QâcÒgÀ—Ïå°ZÙlîÏEÒ—ü3ò\ÿÄÜÚß‘÷ñO¬A3bkd§Õ>«Y¼–Z"~¬®ºˆ󮥞p å†ûòr×èçÙ\hŽU^X~~a2%"éóZMÏ$TQœ"”À^?/Ù(‡ïMÔ)TÁOt}œ$œ¤ ÇxË$3#'¼XKf˜ ÌNKý[~mí¦~sÕ"€^ÔO­åÓô}Gß÷˜ ÂôyÒdFáx^¿?8<È­ävJ¥q¥õz<è<üÉ4 `¦-gÓiÚÐl4hökÚÃw9ñ†¾•Jc®ˆÛ£Ã§2 ß?~¡Z£öz£Q\[{Mäu»šÛÙyŸ76~ö[ãk¥r¤Ö` \Âl­Ñë½Üã8âŽüp ËiÜ‘÷ϵbQÃoa ÖÅȸˆc/ö'ýsèyã6ÈÕ óO\ü¡ÎÍyÈ‹­ñâcÊÖx©B®ÿ¼ºbÒ} ù;[k ëÃ_ÐPÑQ3ÈT9õj½ƒ›Õîn!ÜÛŠ&äMýUŒ¹ñÜÂ_“÷¹u%ÓÃ^óŠÄ\¯)ÄçåvüÇS)Í 1 Ì•Ž/š§7½ZI”ŽBÚòŠÐ ¢7‹Öf‘‡¾>ô[U'6l¬¯ïžTŽ*­vœ’“¹`µPpq©K-uŒªÝ;HJéÃòœÏ{Ôî ‡Íüaó°v¯t%Íõ|O½ÞØp_ÀcÊáa滹/ Qð¹(-'çº×‘|$+Ù…BªX¸?<äJë¢×ãC|üµ0ì5Èóñq¡cd䥗ˉöÂîa¶[Hß2½Ã8å#Ž P¾±¡ÖÉ]ÁF¯DÖxHúÜam>ßo«G×EœQ¾VWsÌì9#ÏiCð㺵{ÑÜ(2§ÕÔÉñ`pØÊŠ0Í£ÊÁâ'ÒŒo}i ËÑZð’fƒÃ£GÌtÓA5èuðv«™Þñ`]Lµ‰­!×8ØNBôi9‰”@#5¤u-ÙããØ¢îó*]­¦±F]û{&wº–×·áøî¸Ú®ö¸%½nµ[=¼{vh+¦øÆyÔ~½#î†ô*rGFÞÓÓVµÆ­¡~õ¢}rú>þywzÚF)7¯Û«ÕøsŠþ‰O2\ ŒÕX­õîô¢[c„ÁEûâôîô¹þ9êyF‚a`§‹fû´©wäÑý}ñ3R9¨œžŸ2ΦC^cž‘»n³Ý¹¶e¨®ÝkÝu¯ŸûŒŒñÏGÖ€'íŸÀèV¬i½étšQÊGþüâþ“fT«‘5®"k ïszYÖ)Яšû^º¼~®ŽêÃ'oƒ·Ì ‰=õ´ÔÓ…DßSÙxÉÎÔÂSi¤bx™fª¥~Ð¥)_&ø·ËóãRŸ7ˆÎã(\÷b8½–`=}µ*؆r«€D𢳆²ò²i{hØf¿5†óÜá©Ôë·P¬ñPŽ‹5†q¥Ô5•6œ¶]ô–pC:ÏkYîÈ:wäQûõŽ”åŽì2ì2†—=¯Ó‚¸øg­ííéË|ÿlÇ’GõÎcí¸¶ÿÅþÉ‹pÛ-½ÆXùöîîÎþª#s|Ý}‘kmbNîŸcž‘g´¡ÕÑ|&pHšzGÝß?#¸ím™C\¯_moË4¬‘Ï;ð•¢n#Ñ}§×zÖ32Þ?±†[ œ“‰Çñ º6°Çšn„qZý'_›:£ßÀCíìë‚pÇ÷©€Ð÷ÀëgõŸcúðÉÛ´ÿ¨ñRc©£ç×þc0 <Ó³ÓRÿîV*wë•GjCwìXê(:uÌi›•?}ì~õÓ.ÒƒŒ¯êñ:’åÈ ýr¹¼çÃì÷Õx‚ÓijcÚ •k+ed Íåãuô] rðt¹TÒ0øCóžžž_]þÌåVÇÙÄ}“¨1ªb7„ ~áÙ¾ºZsˆ5†Ú|KHXcÔ}IÞ‹¤×%Ó«5¯eZmxÄv`†ÖNÒÒèa¢ŒBÁv"lâŽà|kóO?­]ýA£°?ö7ñO÷ÄÅîÀ³=EÿÄvJ»=ß„cüS‘Š·ç³üsü3ò¬6 ^“68‘ì³Ú@¦ñþY¿¹ÁØyÓ†>ËzùqR€ÙhÅ+w~*þ‰5p]ÚÀF{Æ÷Ÿ²Ž‰ÃÓQß5=ÿl8kÐŒÌT–¡Ï‘o›âigÝ÷÷]£úÏñ}ø³ÚàëMŽ—&ýy¦êä+ &;U=~MÞÁÒìŒYÀ,0u ô½k„Ø‘®̦xØk© GçùEgYmÊád—^àŒ›£âzÉÞïîoñÔb€5#–ºOK­PHùƒ¡ÇÛke¼ô×ͼTÑ—¾T*£§JA_ÎõÕ€*•c†2XþÞÁ>óðÀÈ7Uø1ª=/¼JßÞl3„ Z1¦H«†Ü·“ÞÙ å3ýqhùP_„˜à2ošM'\i“^¸¥ E/´(|”žD X#ùÍDi†X£’ÔI'óìï7®¯±¡·Æ¨ûÒKÀP©^­ío3ïé)7Eâ4ùr¦Ø†Íð Sùvv\0EòZ°€ÞS0p1ô¹#Ä?á¢0wdœnd#çUÿ,¾†Å¢Üˆø»"»±1Eÿäk}c hˆ]à­‘þ¹‘Ýxhó>CiÕ(ÿ|Ö32y6sYå§•«.fºMç!:Ï8l:…¾|°/òuñâÚp¯\u6¤ ùŒ<×?‰Ò€„L¥bÜ‘1þ™§_vxZ¹ê¬ëܧÕæœ5D[c”o SS~ÚÕ~ötFã£ûâûÏäù'ûðÉÛàÛ&/¥Äxi²ÍÆRǽ™ý×,`xžâÞ#êçÁÓ®ÓàX{ Ͻ+¶ŽF£‘UIé’Fýcþ–ϯnhÄ0 C–(´‹×ÙÖº =¦Øã«;'ÿOg*[+þ*“é)–wL³uûógÓƒdº,–ë=£½Ò· •ÂÅR/¯zrr×n¯ä²D°†&Óžàð ¼t8u{÷~o/æ#µL œåÚ5¦‡nˆP“mà˜œGæÊð—xF:ùòéË´Iý4z®<ÝK/šeŸ´!Ö2RàÏŸ?ýù5høkÈ<÷ím û/Ö¨žÀÒýèÛ¾XcÔ}aP=}P  žnïíųþÝG‚¨ÏÏ瑩)¶šZ•Z¯¦xzoe/ÊB¾¤mÀà^ùJšÁ;Ò¼¹á<‘žNÏÏi'ßEþŽ|ûVB2Î?OïÎÑ{„!xºR–iƒv›µRãÝÉI§^Ç»²››+»»SôÏn÷îèèî2ã#ääÈcôû'óÑO£÷À?ÁÓå\y¨ÝÔ?Oÿøcògä9mNwè=¤ Ù°¼.wdð¹~Ù3|äKŒïpº¯¢—™í7˜•È”NúGðôJ:/;-ÿÓU¯wD§|ê»S7]¯ ž^O,CýúþsУÚ@ãºiöò’HÓ·ÓCûí?é1|9Oöá“·Á—‰Ñ˜m‚hF§¤ûóÛñÃw^v`0 <ÓɈš•…8ÂQBd<( ¢±ÜOó“°Aâ:ñcµð[.¿v{{sÕèzNz-º²Wj·ZÌ졈{–0uP†á›Í&üÕææ¾ýÊäÐ.2"ç¾–yð¹èšN0-aÂÆSú3­FoHÉ4ûPc©T†æÑÔ1áü’y ½…foÂK€†àÂÙ+fþ ^ÓgÃ'‹òµÓH¶™*ˆžlŒ%x ž5Îoƒó]Š`KxFÆÃu­ÐÇÚHµëÇÊ8`ýV¸Ôòá1ìU³Eü Aá”°–ƒ.º¸Ôx%SÍUúÑëås—ºÜ—z(W1ž·ÃÇ<Éy[^³­ù€ù€ùÀ2û@_\jÿúq©‘ë×ÝÝ­Ý]ÞÓ¨•Àš ÁqÕŒsüÀ;šÁR6–eya=—v»=I–Á”ãÏôýŠÄˆæYG}ò–ôµÖÇÞþôéÓíí-Všü;Áåk·ØÛ“8ÇL£,õy)íß/Ž¥ž4.õH–:ŠKí¤ n:bDy?ґijÎU/¢³¿íØì`>`>`>`>0Sˆ´Ôªk|üÞùZj:Üþ8=…âUÞ ôìg:¢BgèÆå„EcäÖãiÀëçÏŸ‰º¶‘/ù6àNfèDg<™T°êNúùèœDOv4–Þ¤ƒ¶õg²ŽÉƒ_ÝxÖýÙ—ž¦úi÷¾yè‚dÒ@^-A ¡ }-I‡«ƒ˜‡ü¦^èvÖ|v`>`>0CG¤nì·óLÇë_³&Ïë¾FnL!(¬FA¥€†£Öç§½½=ègÆ–ýbãÌnÏd³ð¸4F§é;^öö×/<èI^¹òñ¼ÿG±w|^=Ð6ËZcÈ춈Bã{䯄&ù°]¾y¾·'6Få¥%ä¢%ÊÇÚòÐ,%t\í®zoCÊ×ìhý"5,öôݧR9Ø+»Úyù“)vì}°? ¼ZCŸ ™¯á÷ãÞ&êy<NÄýˆ"~¸XUÄýpõX¾ Y=Q¹g}\dö¢€o7Œ˜)'¡mpíéç$ì¼ÙÄ|À|À|`Ù|àaÞýã÷Â<³ÔŠªGí=BI±ìG-\@ä(¶Uv¥œf†(Ä6ù¡®AáZÐùù© ÝÂéz|I™ß¿çWØn°µ¦òjvHî$u­<®òÐÊIkúdôOßhÏû’þÓ§,µÇ©¾yÔÂ|úh<|Mø’ýgƒPï·ˆL »Ö%LèoÏÎ…»`G²ýøã †Ë,¤V,zIzƘÁ0j¼tžYjåhGíHt8nA·.„6ö¡œÀµ^ÌЗ™5 ãb±€æÁÛw‚'-æYX©*ˆv;ÂÙžVZyåËFõ÷•¦œ1»­­]hoÿ«„*ÿöÕ*”ŽJs $:&·Óò¡Ó(2Äw×êÛCZ|vjÇ8cjU×Ç”vu±ÿDc_1Kã^íÄÐnwúɸÔÊ ;šÚíB'‰Q¹qÆÆ—›˜˜˜¼‡èHéÐñÒyf©'„,¸2‘¦šŠZÉcÝ GÍöÛÚÚ\__»¸¨!oxP;µ¹2ª=®>Yû¨º&¼ƒ–lA-ù@ܧM_K±ÔÎ<ž¥6-µ~;›7ov000xSˆ¸¢˜•LöÃóÌROˆ3ÔqÔ ÈˆQM“… &<¼êtÍ o_¿jþÜØØ„mMƃS‰0¸ûÛ·ošr(K\D‹eY€¡)ûòú6CýzÜÜw/XÍmÿ›âc ßÈ»Çç%™ÊEØ'?'¼àìYDbT{6·¶”ίì}öGµÇÕ'[ÎLŸ©'¼w–l¡-0{-µ,)ª=K=”0-ã²iízÍçÍÌÞËQK=!Ú€¨Iê•ÉU*íB©¢öçµ(Vº!š›Dö(HSqk±U*Düà$q<`jÈ¢™†sæ ânM9”fV¥I ŒRÉÏ)ÇRÇ\/!Gà€Ñxô•ÌŸà{"jë|JHëÏEf n ,¡0.'‡æ¥%ˆ°ICr?Q2iI da¯¾kñÚn¾(âìu?»Qjÿ<¬vÂk»–Ãýš÷9áM´d‹hÙi©‰K½NXêÆ.Ç+ûÕl°·¿_Ù³¸Ôâ*?[³ƒÙÁ|À|à}àcÇ¥^D,ò¾möq©§Ò ‹K=3.b!3Ki©ýœGÓR¿cõh>`>`>à}àck©‹¼o›‰‚P{Zm@aâáL«L+g!,ðZj™Ïi©-.µé§Å „Ÿ6¹ÙÁ|À|à=|àck©|ÌU#‘À")™V“—ÌT‘?­vZ9S·À[i©5î‡Sñšgm’qHææææoãXK=u `šÌ“X`vZꇸԪ¥v¬¤ j‹Km­ñÓæææïë‹—z’—º¥1 ˜ÞËo—Ú´ÔJÎ Oo1hÍæææïí¦¥~/Ìaõš>°LKmº^Ó4›˜˜,—˜–úû4³À{YÀ´ÔÆwn>`>`>°t>ðµÔÙl¦R"šxÉÄffùnÆ%W@$âq õ'Ödµ>ùŒ2tI¡éSú3tÖ,É4E;ËNöÚ¼xss£å…ÿþõ‹d>%J~Ã+ûeÀǰÔÚfØhÒ8bº£%'›N“üÚæ¾y¾·'Ôõ¨¼´„\´„½o^²XVf4K W»«ÞÛò5;ëû\jßq*•èÿ¾¼üI;”f…,LKmJãéÍÌÌ–ÎQK­Ä稽Gè j…C­VO<Æ;BÐ…,,'¸¹¹ÉOP×ÍfSÓœŸŸÁÁ pº_bI˜o~…í[kJ ¯f‡äNRב6ÚñÐÊIkzßæ$3íy_ÚýéSŠÚãTß`>°<>°ˆZjåhGí=R¥†Uu´1¼j¤H׎šˆÌš‰ŒÅb îÙbÜAÞ§~Öf Vª ¢ÝŽp¶ç€•‡V^9ƲQó‡k©;­­]hoÿ+ØýÛ·¯¨>P¡$pù«íSRÓr%¿û¶ûûû0LëÉÁöEO²÷Ù©㌩}T]MÚ¥%ü'ÂTñÚá½È…øêSëé>:Ï9žÙT*EúP𘜗oÍ0tgØz¿Ghu^ˆÇü°z"9),úš\r=Ÿ3”Å«ŽÕÄææææoà˦¥öp‡WuR‘ …|~~> mmm®¯¯]\Ô@ᳯ!øPnÜép3[dÇh©=Z-„ÿñãȳàPÎùü„=ÓÇç¥%ÚföCƒ“eÕÞ¾ÌLü+i|vjg®dT{œ4ÙòQuîüØ0-µ|ðÿåáfìzí^›˜,¹,›–JUå"¼òEKíØn6ÁÇÇǪgrðíëWý‰?766a[Ñ{îV%‡­!2ÌÜ£cä"Zl³Ñš²/¯×.CýzÜܧ¥>9©îïGaþh€ KwÏK2•‹°×,}Ûúúz#ž¯ùÀRlj6·¶°±÷ÙÕWŸl9R0÷džvuƒ0-µñÄÆ ›˜˜,,¢–zBò(•c½2¹P9?e³åhµš?â™s¹|þÔmÙ£PvWŽŽ´–ÊA…ˆœ$ L­½h¦áœ9ƒø†[Så•‘kSšÊ(•üœÂq,uÌõrX'Pö¥ßW*¢„Ÿ ­?™1¸°„Z4^õм´6iêõ+/"OZ’ÏJb¬%á‹"Î^÷³¥öÏÃj'¼¶k9Üÿ¨yŸÞDK¶ˆ˜–ú·üÚúÍM½q'ªTJö«Ù`o¿²Wn5›Hûå@Ä›Mô!v _nvpn`vЧÁì`v0˜ºðNb‚3ÏÐûú77vÞ¬vw áÞÖÃÉé¾×A™_>·ä×ä}n]Ë–´KLåÂaÙù€aåTJ³BÈÇÍÓ›Þy)íßÝ××WaȰ¨œ½Y¤¥1µhüD¦/8\?Y098<©U˜pëGÒ6rÁj¡`Zjã½–Ž÷z ¨t.¦¿—ÞÆì`Ï×Ë}àck©‚ÌIS¿µ§Õ&>Ê´Ê´r¦¥Ö/ÓR›ÌÌÌ–Å>¶–z!ÀÇ\5 ,’’i5 Â{0÷´ ·ræÙ¦¥~9Ïa<0^Ö8cóóEô¬¥žgÌam3 |` ÌNKý{„ÖŠ-µc‚e°Ö‡x4nØ9–qä:-Åì`v00x;XĸÔ‹Ø¥™>€âщ¨³¸Ô¦Ñ ,"çdm6®Ô|À|`B0-õ€/v fy³€i©5ý¨ù€ù€ùÀrù€i©ç ‹X{ÌÀ¦¥6-µqÒæææKçXKÍfúÐ ø˜ÌÚÝŸ>eYÇ;¹"߈MØf~cM–Që“;C—$šž”×××}?U«'¾ êü6 q¦ÇW:ùÕMÞlê Ñ=yvK9·0-õrq38¢­gºmóóó¬¥va6mgÕj©\¾½½ýõ«Í)årYfu% ¸ð+1³÷÷÷Ÿ‹W&nAJ]È0¹]ø.//Ÿ[û‹Óóý Ë:ޝtò«›¼%¾êɳXÊ…°€i©—Ž›™Pk(îkñ-þ±ù€ùÀGô­¥Ž"|!œž²˜¶¾ïYIQ'g#|2‹üq†c~eq ÔŸX‡ÕeUÅbѧçüÁþ>|6 (žiJ˜9ŽWÝIrù“J~³†¹gd9`9ëö-#=8Þ'ðü:5R/²×…ÍÙ`¸ÝIi’?I^­hw÷¡¢dÊZ-ÊžÄd”ÏZŒº¶‹ç¡YoÄÏUpzÉ4€òuJ½ÒÁºàõ¿|þLòúö?¤L\>åp½´“z©=iÕ…À‹ÖÈ'-`ZjéIø¿q6fóóó%ñ­¥ ©!-‹Œ{Ü=|ãª"W"è R„.š”å!¶ù êÚóÊçç§Š€áYý”ùýûwRÂvƒ­=X×ì0ÊIê:—ËœðÅÂþR”g©}é¢^¯“Œ%šýÊÞÊìö„®¦…”Fuç§!%%ø5™=y™ÐØkkѲóµ‡ÁÞ÷ïÊÖÓÈg'd4Cë¢"½ÒòÞžo¿O ï uîËÚÚšËÁÕ€¬|±à£µvЦ¥6~¯0žÞ⛘,—,¢–|F=jï_ðƒZjý ˆ †6hêí¨ùˆ ׯ_¿‹4žE†µ„àIµ±?îtÚù|žò†Žp¶þJ%C$ï9U÷K_8¸¿µµ‰ø¤Ñhèi°)üúå '£iŽ$£…lÔ¥¬Õz½†‚ÀöÙ“èþþ> Ózf#çdg?|  ­‹“ƒíO¦T†Û•`_)mH6ÉŽ?€LK½tÜŒi©M#n>`>°ä>°lZjÀ ”0:`(j¥TuƒB>?Âàœ]__»¸¨Á×&´ÎÙAž ^á3™,Œ5h’ìñSŠ&¤ b¦=$È;Ö6ÉkJ€;z‰zýñ ÍÖ“ðÙÐá3ÊeN–ô$HZkÑ}"eÅ¥,—âìÉkL¥RI…·þ”$÷ýqòê†ÖÅIMÃÞ M™,3Ò†¡–·“‹kÓRGÏÑäJæ`H$"7ƒÿßçå!9©V9k¼ïäö4[™­ÌÌÞØ–MKôÉ/b/_ñXÁâ"HkyŸLKýl å/‰CÄvËÌh€ó|_ü‘æì#Α×±ë²øææÂQK=!‚ƒ‚D5N…Æ `ºõ°?¯EåòùS·IB4•£#ý©rPAtÁI"`dR)%§!‰a‚9û÷¦ÊRÃ1Sšâ(•ôE™LéÀtO5ÐÉóžÇ% PžÆÃR‰J„mssƒ?)êÙã#8›“”é›Dö(eé!eÒz$>O'ÑŸ’tÌ‹ô¯±«‡ÖÅ•žž •†ÄÅ·ß§,'Z•,ŸÁ1‚mʳÓRÿ–_[¿¹©7îôB o ZÍ{ûû•½r‹ï³ÍM™ iÝâXÑüG¿o3Vûׯź–E±¹µÓüÊ|À|àõ>¤R¨àn½¶UËܬvw áÞV$xúÛ”éñås MÞçÖµ éQŠƒ†5ŽÞT6à”N¾|²4À˜ÛÇ+|2½%X _4Ooz祴¯¯ÂaH9#z3Õ•ð{÷MªŸ¬ žžTŽ*­vœ¡T.X-~ÐzGÆ:î•Õ}8ŠX,mcL\H4eøCp6‹eãËí~™˜¼Ø>¶–zQðÇû¶“)’?~D”ükZ‚ÈD5*̶D%2IQ®êˆzŸ$½¥Y ÌRK zNgãq§D¿ªöê«EcÎH™ºZÔö›vÓîù€ùÀÒúÀÇÖR/ þxßvE*•)@j†ѨdôðÇ_ìø,e|_{ZíÂC;qïWQŒæ ºó‚~#–:ÆÀ!G¹R)ÒÃh;>[p²J‘äÿ½Þoˆ´_ot=¿Ë"Eª”wÛ­Vqc¢ûžà2gK.EÞ‹p|×%ªüן?m3sÑ[Or-.–>æbZƒwG;cÖPg0O0OX,O88>é7+ÜØÜâàü¢– »™ MÏþöþL;ÜIJ!ï—T&ÃÄ»z±oνϢ4Û§½íÕàûN<ØP‚YÀ,`˜Ü‡góÛà¼D—"˜l}yu¥Ko*n޵ Eµá@µlÎý—òá1“š­®Gçk¹ P(þ–ϯ¦ÞìJo%™‚Õ\Z´Ôß^K}pxË­Ö~¨æ{r-õJ.7ù­²”f³€Y`¨îz »¶³Å•G±Àfd1ºõ›» ÙZ ÉA]_ÀI…¦}ûÜJP.å÷€©>VC__mZê9†kXf Ì\K ð¦{íuc-µÃã¾»3 8w¾Ýí^^^m¹ÐžShÿ2{Ÿ]»YÀ,ðR £¿œŸN‚߃Üq°zlŸŸ«Aö88ˆÁxiÑ£óÁ_ÅjP8‘ºökòçU+huúx›åÓÁZZæÓlfƒÝµ`+dÓ‚¹IIS«Míñßvö®1-õÛÞ«Í,°˜¥–Ú @dÇ2E.ÎŽÓU+×½ÀZj7\XgÔÐ9È-5ö&×h.…—ÙEšÌÓ¶ÀÁUPom'¬K³ÐñJPÌ –¥w=® ês¿~£Œë»à¤|­Ù“ P ¨—8NÔKTŒÒZðc38Ý nËA{/¸Ý ®Jò3§;ÁÑFPÝ nÜŸ¹´èß.‚j°{!ÖZ}|ù•4¤l\;å>‹ÎŒê«?p\jEÜ€ãDVf ºD‚Ç%W@DC@Xý‰à£Ö'ãH*d#¥®Õ’ܪÕ_‚†~›  ’u¯tò«›¼ÙÔ› æ=yFK9ç˜]\êß]Í[Ôc7‘1Öh?žé~u?/] 9÷kžYÀ,0w@»ÌÆ´Ø_7ø­RÁq!ÔËA1'@J>¸;¬ù€rôjUw©& T}|%ée-Z&Â*uEp3l4ô3H½µ&Ýt_½É6 =V,ŸM èªâ0ëÈBØ@áüƒùæŸ(³ïDI¢Ðœ4Y¡ÃiÃŽƒïš‹ÚIC#AÛÐçèÃkAØ\ßPÚõ–¿ð!c‰ú&:^úXêAºæììl¯\=3€ØÉ~ÙB$(Ùˆ¦ÌO¯ð Nîô“[Œ”~‰_þÙÙ…/áòòròz_™’ï]Öq|¥“_ÝäíñUOžÅR.„bo‰úœ8âGÓ#šfë5|¬]šù.â‡c$46”;#}äït’’Îì±ç,óW\ï¹ØZêiñÓQ9 á&ÖH³€Y`ž,èd<=Œæ$t5°艈9{(ìoñLpç.ôð…ü Å tÖrP<—׃½õà`#8X *ì7C_n †®íÊ™í¼ˆ¤« òГŸ‡ÏS œ:Ì:²(vjjó2›W;rž?9O®~´œlʱÏÄ?X—^07í â¨t®L)/…8¸>|ÍW):^:SúðÅë¼p“çUv*¹ÉZ¿››¼§ÙXí¢þJÐ7à5Sí9æ'–3ôëŽÕeUÅb1©99Øß‡ÏfE`zlàÇ«î$¹üI%¿Y{Ü›”–V‰ÖvéHÏuùž_§FêåOöº°9 ·;)Mò'É«íî>T”LY«EÙ“¡|4œºÎ‹ç¡YoÄÏUpzÉ4€òuJçJCê‚×ÿòù3YÈëÛÿ2qù”ÃõÒNê¥vSò øéŸ˜©–:ÂÚ‚ºS‚¸=K½ÐZêéòè ïAvf³À›[àÖ±ÔùÌ#~:É óke3øcSh]6ØßFKˆÛZSäÈü ÚþÜ šeQ<FƒÂ÷6ä<ð ZU6Úá­‘u=ÉOÉKÉLK¡Ð& ò+Ò0ØwþT{L^¦BÒZ@¶ªP€éüÓ+ç‘¿¸m µÔîÃgäÆŠ$^êÂóŒu_~"î!ì5¼µ $ØYâ>8HD/= þû×/’ù”(ù].— ÃìSR ðuC9ã™`ϯÃãC³Ýî°÷ÐSóRfò$…kE»»¥ãã¨"`÷)kµ!úuêëûVévëÞÝq]|i([Oh[»#­‹+-•ËÌ¡êu»¾ý>%?ùË—ò»ÝÓÓsŠ¢ö³juì²ÏÓÒR+oŒK–Zž59 ž–%tSž¥Ê ¼x,÷(HeSæß¤ÌÅsk±YÀ,ðÞ³­¦£Îo(WMÆ„EÈ`'ëó9zžl E%2*ï§©|9ð%À'W¤/ÏÔ~$-µ‚ËQ{ï•£&XBå¶¢]ö ¤]:ÔYÞoÓ¶‚ºö¼òù¹¬qn€gõËšÀÌÁ|“¶ÛG-‘kvå$u­Kv#8á'Š¥=åYjר*d,ÑìTf·ï$t5-¤4ª;?ét()Á¯ÉìÉ˄Ǝ?ö3»€Ùûþ]Ùz¿®xrÔbh]T¤WZÞÛóí÷)áà=¡NùkkQ,I®dåïݯXý¯µÀ´´Ô¢ qÑ?”âZ§.I… š„[7-õ,ç•¿Ö/,¿YÀ,0¯ ‡m9R‰Ø'ùcºføfà5³ž(¡U=IÞ…Nß@¹ÔA®zá´Ô\…j?Fí½ÃŽšúLt´1²ê²&ÑŽšre"c±Xˆ{Öv‚'ÕÆþ¸ÓiçóyÊwÚ}Æ3©¨]Q2D2MñçBÄ×Nîomm">i4zl d¿~ùÂÉhš#Éh¡~-@'G)k5–ÎÀPØ>{ò¾'Y˜N¶JjOÐû(?ñ¦Z'Xö¸ŽdJe¸µ|lâ+¥ É&Ùñ°À,µÔž(pkS¡þˆ‹—zÊ\øp"»³€Yà -@D y7¯8Œ2:ÎÆäúæ×h£ç9otO†7êHéÐñÒ¹ÕROîbž+ÌÂ+šÙk©¡ÏÏE‡0¸g×××..j ð„Ö9;Á“óÇ™LÆ4é`¼Ç”ZAº f(jäk›À¯QCîPéõú â¤Òz>:a_È<ô$HZkÑ}"eÅ¥,—âìÉkd*XRáÁÜD oÃäÕ ­‹“šF<*Î64eò¾`FÚ0ù=µ” ak©™Ã¨ƒJîsðcÄ¥6-õBx¶5Ò,ðQ-ÀT<ìñ$?-]ïl4Тè›{ž[àÃh©'÷çÙe‚oV¹¯|ÑR;¶›íûÞºdU&ò8øöõ«þÄŸ›@[´Âž…EŒ ™Bà°5\†ó±¬èÑNh±ÍFc0%唽Œ{¨–Z 6õÅSŽÆàƒ“öÖ ""íÕ$SZƒÄBñÍAŽY WP(¼šeh]^Ú%=Ï=4e²%|Ql¬¯O~O-åBX`Zj¹ðžFü‰ò¡«‘Ë^ø]]ÓÜ•¾‰^yʼòTÛ¼^b4 ˜æÇ¶t¨kÙ)ÄߘgŽùõmÓ[öa´Ô“{ ¯\¨Æ©Ð¨œ|Ÿ²YÂY´ZÍñĸ\>ê6 £Q(€b+GGZQå ‚è‚“dɤRJNCÃs í)‡²ÔpÌ”&8J%«Ä§t`º§èd ›“(Oãa©ŽD%¶¹¹ÁŸ” õìƒñ‘ œÍIÊôM"{”²ô2i@Ÿ'‚“èOI1¦?& è_'t­‹+==­ñ‰‹o¿OYN´*Y>ƒ;bÛ>”f¡¥V×ü-Ÿ_cØ¥®¡Mª^Í¥÷ö÷+ßË-¾Ï67e‚BBLĈ-ÉñJÎÍÆ·Í,`0 LlB/·î$H‘1”…÷[€hzž_ì{§Óæ¼¶ó“f³ÚÝ-„{¬ 9› ”9y,¼Ù4ÁJ,sÖ8zSÙ€S:ùòÉÒ?`n¯ðÉô–`Q,p|Ñ<½é—ÒÚŸ€i¯¯¯Bbt8¥†°É:ª“ì¸Ý7©~²2xrpxR9ª´àMâñ&À¬ ~õDÎCXKjÓRáKÅS¬f³ÀX€ž<ÍÆÜÄ×ó¸[oÝ®åÓRÏŸ.@˜"ùãGDÉ¿¦¹ˆLT£‚¢•È$E¹ª#ê}’ô–fQ,0c-µêŽ€XmZêAö¢8еÓ,`˜ èâ,²¾Érë¤'шëýZB-õ<8êü·²°R™¤fØáðJ–AXpüåÃŽ'èóo+kᄘ¥–ZçÀÞ3Õ÷¾×~Ö³Ô¦¥vCgMLxã,™YÀ,°Œ`¹6V9±XÚ‰Ž±ƒúÇj©—ñÁx¿kFþóg½ÇÏŸ?“:¢÷k‘Õün˜–š¸ÔÒãIwß %V̽¬ž(å‡>,?ŠKX‚óïvï­b³€Y`á,pã‘ÉÜÄEˆ¹1 —<»4ñÍ•7ÎLjK½pîj 6 ,•f—šÕ»ùT-˜Z >:õb®w8åø!Kånv±f³Àë,p=Ùº‰Æa?˜Ù´Ô¯s9Ëm0 LhYj©Ýˆ\ÄIK8=xiÕ¨5«¦ïy¨~n9ï ï“%3 ˜Ìm'uÈ›–z‚xÛê0¦¥¶Ç,`x ÌRKíF&utR˜êÔKmZj¥ëßæ[-f³ÀǰÀú‰c5Ä;ŽÇäqNô¦/®–š€DD–`ÒŸ>VÂ/[˜Œ9M8äoß¾úŸˆEÝçê—Ú½pz„¦&¯O0xæc<&vf÷²ÀLµÔi®*Ì€ª‰ÊÇcœò,µœwŠêEá’gÙÎ÷ºõV¯YÀ,°`¸uë&®Úº‰“éÈã»;GZêÉŽÐÅÛ¬/òå˯vûæö–°~i@Þž²ú¶¬¿Ýùõë×Ök†D‹† ò4žºYÍåˆõ– 4ÁŠ-}g&ož¥4 ˜-0k-µcªS)åªMKmq©í!4 ˜^lfG·ò[7q¢˜Ü‘çIK=ù­?:ª°î «*EX ¿2HrµB~m×ÖÖî¯ÁÉ-eš_^^&óÇûÎLÞÔÃÇðʹ|^ßAºqÌ™d!ÆR/U§aûƘ¦–:AR­Çï è´DÖ¡®ð¢‘ªå0ŽÎÌsŒê«z}w{;›N¯¬¬qIž“¾½½­þøáná¤×õÆ÷Ûª3 ˜Ôí{ixÞÖMœXG®7zAµÔß¿‡¾¹¸¸·f¯ÇÁׯ_õŠÆ¨ŸAe#úžâiŽ’o,õ‚>þÖì…°À4µÔÉ'½×û]ôÓ1K­ÈÝïZKM?uôãGnuõóçÏFƒëz'=À—,„—X#Ífw·@³+M€¥v}©qÕOÛAoÙœh©Ÿë?Œ…žº-›Í íNÿ&Z AööößȬ eÍñææf2¥±ÔϽ–Þ,0¹¦®¥Žª…¥vZj¿yp/gî£/ìÅÕRÓòÕÕÜÍÍ Žb`RNz_2ùͲ”f³ÀòZ Ý‘kϹ•:LK=IÌ“ØWæBK­Ñ™ˆnkkkh?Úíöíí¯¤`=ö˜Pfÿüùé6{ ħÌ;¾´ ›jÉÌf×3ëtà¨Ïad1•âO¿Ž¸J7¢y†P#>=,I*•"½*ûtb¬ò~Ëå×nooj×ö©×“1˵\æ`ï rPn·[Åâs/îïéäg—Sêšÿc$nÔ}Œ*ŸÐž}mþ$'o'¹–‹‹,–a€Ï»£1k¨3˜'˜'$=aõáüÜVp±¶Ëå2?ó•ÎVÑ*—÷èC+rFV༥a*§H †½_2©L˜Nׯ®²¹œ{ÃEï íjo{5ø¾³føÀ,`0 <ׇgóÛà|×M!z© ¼¼ºbª±@;¬ì×€½a¯Ë|Ã^çþþËÆFùàøèä¨Ùö)Ãb. ÆoùüTîU³ë"è ª^Ïe÷÷ö*{h'bu¨û{Ú€x§”mþùô___?;;ƒB`ëksöÓ'ÁÙ\ C{Ͻa–Þ,`XN Ï‚F+¸*y—ZA íÇX@!õÐw „“a˜c®³÷|šíjw·îm=šÒ7Eƒ¥dާX¾e0 ¼£Ž/š§7½Z)í1íõ5K–f”Ž{ìê XžTŽ*Ͷ\„æH½Z€¥Î3]¯ÆâÊ=3±&¢”¨ì•Û­VqÃ±Ô `-?­<:ÍfÎ=c>Ÿ?ªTòkðøõOŸVÝÞNr]}s±ßѬj³€Y`Î-=&tRpSŽäÔ:®§›µï7QoÈøg*“!ð\½~•Í:–:N³}ºÀ,õÉU·Öì¸HúÎu×eÇfóû}Èf>SÞHG,u‰GOžAF •¥öø8êk"P-q;ÜÍ‘­Óq,õáñÑÑf«iBBÏR¯­ÝÔo®Z@OÕkùô~YXêV³¹áXê@ij·Í??=õv®K=ç(Æšg˜ ¤P‡õ‚Îwc¦'µ@$üö~A·KM T˜‡ymÎ1K=žáOÃ_åriÿ Ÿ ¶7 ˜fmPn«Õ][ zÝ6,õy)í1­c©Ör&ÆÓ‡ŒXƒÐ{–ºÕŽU×Ä­Ï«a©EK}ÕèÆ$u°– ‰èSÙ+-´–zŠšï\nVËs¬!f³À,p× `©YŠü¦dœô¤ÜüRi©¿œ´Šù\›.QÒ«žÞöf³À[X­t6ìÕ›íb¦;#-õïú­Ò«‰ DþÒx‹—zò˜Ó“¬¡8…—­a0 |t °n"Ý犭›M‚Ÿ(æI4®8l !pã÷‘ðI‰4c¢>¿ÞËžñ£¯ºñye9ž–¹ª‚§ÝÚjvlv0xใ‰v*A¹¾?‰#~ı;üjâ~m'ûˆriÄn˜lëŽË_—úAÀæR03]f; Ç­{MÇî5q=ïëûh+Á,`øð`:ËlF»L‹K=‘Ô+æ*.õkæ&>‘—·5Pšëíö Ùàh3¨l„ã,ºnñüÓÔÒš´Ù·ÿ‰©ý¼öŸnEë4ïä¸ÃE¹vkçüûçø{$OŸ¬cøÓ šwâæxU–D\i‘u D]Å4é]<‡³c„¬ø–:â¤ÝŸ¹»X}£¸I8ݘæÃƒ»@³€YàµP–ˆ tªPÏák—3}lôG¼‘¼‰ÆŽ—.(K-ð[îu¼·\õj:iaÕåbXkJ›Ú/¨â™í—ÇCrÕZ½Rá™yŸ[—¥7 x ÈÓ÷gZû™)Æ¥†¥vtJ´É¬Fa t=ÅxUÅE瘧Ðþ×¾j-¿YÀ,ðñ-€–šŽᇭ›89O¿|†Œ…Ž/\_pŠî5C–Zh.¹ÒÓ-ù„ø±žî„ë è¾Ãz½Nn…ì•äçJùHƒÌ&%Ä0{½v-¡ïÀ°_”””æÀC”r#V·Â*%¤µy¿ï楴êfȯ© w²óP£&ê;C®B.èÜ|:&Û¯–çWj¤Þ¾Úióñ–\ÑÉV¸êj×¼ìIÙº#Ðo çcŽ0ºj;c˜ºîå鋞 ¯¿ˆYêØÿc¥†GÂ.eÄIG,µ{ŠéïµrÇl1KíVŽ‘¿9mZêA¶ZÇ6³€YÀ,0Æ,+‘e¤Ïøé‰yzµçбMÇñKA}ÚGfFÞ8;-µ¾yyïžÉÁîYwû¬ë.'¨\¡ÿè}[O×[üÚ­ÝôJëi}gï®…DÀåd^f=ñžFÁéßèŽwÖ›v@™°¿[k:UJRfÃàÛEïôºWÞôR&³îÃ2÷¯zL¨½ï…7­`#¯5JŽ9ãÔ¢7¸ó¹ðº‚èo¿¦¡öfGÚɞ㨜\xÚèíR{£ÃÝ——4¸UÐZÕ•¬×ŽÍ>Sñug«¥vÜ€ß ªYåË=E¦¥Ö¯ŽG,þŒ:p+Ö,`ø ŒF&=‘†xr÷csÞzã?’–Zùð½[GS®ö^t—×­q² ›ç³“ã›;+kšµ\pÚÁõ©¬ 嬿ÊñǰÝ,ÜF9àr¢zùò«n»#8Û}éÈy˜éÓk)³ÕîÚöÔšÝ/,k·ã *M´“ãüJpËXÌ@ûµLj?wídϱæ=¸¼«ßη{ÕêÉÁ㼤!„onØyoŸ¾6ØùAšM&´‰<}o ¥–ú^W9¡—GñƒÜÀGÔIË?u]á}o×`0 ÌÔ€ zpƒi©'çéã;2¼{jžµÔª¾wëJÄÒcᛕ·Ö•9ÆyþØJ_–VNwÒ3›*Z4Y(«+$Žæ8j9:ßQù™ŒÜ=ßqa¼âòµÕœô•§ïu‚iù +‘þÚ|#ºò}š¨¹‘tº¿|ùUÛ©C½.o>îIWK+4É·S~‹%äŽ(NýµøóÉzíØì3§ï ´Ô:Û1ÚÇ,µi©Å&ÊUÛf0 ˜ž²ªPú „›Wž.¿ÕÚÇU D1^º¸Zj'—êuÃþÃŽáx·«w_N½ MwÞ)‰wFyÕnjýASJö¸=ãS&»‰Ò|{.êÝ­BšLìOëÂ7'Ûé[2¦ýÚN¥jÞýÍt«Ý+ŸÝÑ0ßN]Ò~ž—¸ýÉóv·”bc•òè«Éå<¸hqf»Úº‚(vTï]ï~ÿ¼Eú«¦ÀnÉÕcâ=Év]2)ÁïvA«íÏ'­RµUktËOÖurÕ¦LJ¸¸q)]]29’pÑÍ(¯?¯åSW&f32åj°ý®RæÁy›‰š—,´Gj©w¸¨»Žœo¶ïiá·ÓV.#¸£Ñr±Íâ6ر÷³É àþ¢ã‡§OÝPËtãRÿ–˯ÝÞÞÔ%VN´ñ}yÀVÞm·[ÅâF—gôžI Žxñó*–é8—#Úmf³€Y`œÓ«'­ì¦õÖy¨X!í8Â…Î&Z­ÖÐ÷K&• ÓéúÕU6—1DüÞÙ®ö¶Wƒï;k ç‘'×Ý«Û8Rxù(ÌñŒã£­•ý ^ØQàÛgåuæW6þV\¹lv™ªÛ¶?=Îüe-}ÞhO¥ HD¨.V|?ÃO^˨öÛùéúÌbÙ³Õ¾ß(dº­Öùmp¾Mý%êéåÕU†E阤·vñfïö†½®Œ³tîï¿ll”ŽNŽtV±K)¡å …âoùµõ››:‘y”¥f¿š öö*{¥f³¹¹¹ywwGhkÁòî>è¶TǹœL¶Í,`0 Œ±zщ?j»}±öȶc…ÔCß)©TjeeåªVËå…×ði¶«ÝÝB¸·5+²ƒøw/^íåɼÕëîŠéh•ã(þ€[{e¢ãŸåœcš'M¯ñ &IÿcuIptu'œñ Ê·2'¿f«YØ °¼UÈ —:¾hžÞôj¥´Ç´××Wa˜‰£àk=À#Ë(ÌÙ]__?8<©Uˆï@¸@qÔ«…–Úk©XêV«¸áXêŽ[À@EàËÄOëõjon›YÀ,`cT_΂âJp¹kœ´{ÓLÆÍón–zØû%•ɤa©ëWÙ¬c©ã4Û§‹ÊRÛd0 ¼»ÏÂR—èRã¡ýP–Úã㨯‰¯[§EŽeëtK}x|tô£ÙêFÑ0-õ‚ú¶5Û,ð‘,GµŸ¾–úwiq©Ÿˆ[ò‘¼É®Å,`˜T}*D‡ŸeåÎ|ìx¯mÝŠaqŸ„+úhq©gàyV¤YÀ,ð DáÚã>Ge:ï׉V>w·Sàht;§Ë’”÷¤×õ•’½“ëìÖš¿,.õ¨5Ÿs³,­YÀ,°ŒPp™N»žÕøé‰yzõ•¥‰K½Œ†]³Y`®,з.¸ÌŠÕé¾nxQFÆT!­Šê¨C£Y§R¤Ý2ã²¶y¬¥–?{½–ÚâRcQ\õ\yƒ5Æ,`˜C ȼ'YˆH ã§£¥ žàéõV.K\ê„ãf³:¤ âäXö™Ì§OŸ¾}ûÆ&Ÿ¡ùîîî§lvuà§äSaF¤.Î\__ «_¾|ñåOþ°¨œ”¢ /N^€¥4 ,€"u†ÎyAK+5âµBµYêh&±°×ü±Ô~î‡+!ºæ0ÁR›–Úe¸žo|Äšh0 ¼«XœMV#7-õsxúø¦-–:ñ"Ûí»_¿~h«T*©Yˆ¸µ :þòåW»}s{K’ýýý¡ngã'ðw½~syyéËŸü±ˆ5¦ UÑä¥YJ³À|Z`ŠZj·íà¹i©'[r>ýÂZe0 Ì”¥ŽzV/À3®ú)®:ºË§¥vï`÷~ ~Ôõz]::ªì–J;;;îÅnmmýñǃ.Oä>B£pžhžË éíˇóÞÞÞÎf³»ÛÛžÿ®ÕjŸ?æd¡P€Ø¦LòÐåË Ku;7—5Ä,05 ˜–:zSÒ:ÏúüÔî¤d0 |P ¸ù*²Ô‹C?ÆUOju‡%ÔRǣű^3~.’‘­kµ+ÏXynªÕ* ™ðÜ@aå¼}ù•Jz»Ýn÷èèHË©]\|ßÛãd¥r°W–Õ‰’yù“)öƒ>¬vYKmÓROÆ%ð§HÓTéâµz|¦WŽÏ'Óô/µ÷ÙÅ›ÌXàδÔ/âæÕ´IK­äw¥hQdÇR'µÔ€Ý““M2†~~Òû ™×Ö¢õ<çíˇV\ÎþââBKûñÇ››lnnµÚnÉŠÇ|y>Ÿo4OVm Ì gÓRç0¸‘cøi¬¦Ù}iŠÅ"Ÿæãó:IjûÎg¬Áf³À›Z@âb~—i©¥÷œ˜§oÒÇÑRëÊ‹£öÞ)Gi©~üð0 Frªâ(‡fÊT¦õW¯‡öåƒËãeáBŽ5³¿~ýŠêv)½Z„Ø5iŸøM$«Ì,0c ˜–z$‡1&ž4ú[[òÞ—æööö¨R‚º«($±XÔ“Ç«Žî½i©‡=@çççO>¬Üîañ>޳eâ_ùÉÇô`ãÚÚT:"“|¹¯ (ï?ÙK`X ˜–:zUM®™¾kµé,J%‘ˆŽã1*¾GÿùòkªYÀ,ð.Pö/›v=ÎÄ­­³¨7Ë´ÔCöû÷ïÇÇǪÖpµ<˜r}}½Ïh|`©ãt›[[ª$aÏÜG=M,=><<ô|6¤µ[^¶f³ æ~—GÉ*5 ÌÔ¦¥~¶–š°š.HgwÛóÄúˆ£øé"š©/Xáf³À\ZÀ‡¶¸ÔÿMŸ[oæGÒROèžC¹á¾¼DÞ8u› ͱ Oäç&S"’>¯ÕôÌ –šW!!D(½Æ®f£œÏŸ™¯¸AüÂæ$áDˆ BÌ>ŽaÇ'™9áÅZ2³ÀüX`vZêßòkë77õƬǨ«½¬fƒ½ýýÊ^¹Õl2}Až4„Z±1$&ö"ËìÃÄÆØ–þåÛO‚ö¯_“\ s÷ööÈ [à‹´3f uóóÁ1AP öXJk?<¹gí¡©{{ß÷@–B À 9Ÿ±4Þ>éPt½}ý³öÕ¼“ Çì:¦Êù¾—ó›Õîn!ÜÛz89Ý·u2æÆsK~MÞçÖ•L{MdÌõšB|^îÏõ?¦Ršb˜+ _4Ooz祴DŽ××W!+"º ‰D«'J¿­½·~«êÄ…O*G•V;NÉ܉\°Z(ü–˯ÝÞÞÔ£M®:—‘•“Ê»ív«XÜ ˆû{†4Ê]¬ãO««¿~ݶyÔyµ]2}.7«Ž{®œÌc0 ¼Øt´™Ã —n¢5:¢~Y Ô>ÅŽ‡Ú!‚Ôñ{+Ù÷fR™0®_]e]˜dÿnÛ®ö¶Wƒï;&KxpX^ø„ëT¢y/ödͨü^CòÊÒ,»Y`®,pxÖ8¿ ÎwCÅ{© ¼¼ºŠB¹»3ü ³ÌÝRØëvyÊ:÷÷_66ÊÇG'GÍ–¬R®ø»˜ …âĭo®«œÇK—‡®,l3©æxr­ób•9WÞ`1 ˜æÍ¦³¤£4môsuäz+—PK=]æe=-ƒ®“kˆ'‰ñ±ËÔ»¹„Zê9tck’Y`,0;-õK­ZjǨôa,GñKv~ÜÌ®Ñ,`x¡Xv–.ô¹­¥-þqâR¿Ð‡,›YÀ,ð&˜V\ê‡AÉ(|jï÷­»iz: Ñ-«‹êC&l÷aù1\Âkâi,@Þ7¹ÍV‰YÀ,° èЋÅ,µpÇ»0®:ºãË—zA]ÝšmXt L+.5}4o1š-š–z¢uݬýf³ÀL- ,µi©…yfLn½/¦¥ž©Záf³€·€i©_®¥#Ž9= Gn¾h0 ˜ÆX€Á=º™Œi©£îö<½Zu µÔÙl¦Ï£ÀGPh¼~ú”ýöíkr)r¢Ú±Þ!¤ùíÛ·o“¬RÞWøäK!¦ôgZ­Å&Óp̵è^›§Q®Gmɼ{ rQãj___¥›¼ƒ6|²(_;EùÞOæ²É¦¥žˆKže¼‘äNv-f³À”- ,5ÓŸËÑZúøN,–zp±³jµT.ßÞÞþúÕ&‚m¹¬«ÿÊJ‡ÀG¶v»Í¯¬Ï²¿¿ÿ\ž<‚Ç`JæüüRëM¦á¸Ýî°±gÞ?k.ú–md2ïåeTà$—Xg#%_õú y_°›¯…l^`ÆIÚiiæÜ¦¥~ÕÚ‡“ðÐO¤™s±æ™ÌïjB÷óvgz¢i£Ÿ«#îÛòi©5Bmrûãô”EÂõ}¿³³i­¿²Ò!ø3Še·¶¶XæPbœÏŸ?Ã^‹EŸžóûûÆ«««gggšfŽãUw’\þ¤’ßÛÛÛž²änýÈh2 sÆÓòg_zšÊ2}Íc©s¨åÁ¼ž3¦ÚÐ×’¤q¸ºt:ÍZ6Ô |gÍÇ(°Km¯ÙYýÑSøÞ8µg\Ë]ÓY6c&ö®]ˆUþv0-µô$ü–<ô¸òßîV[Mf³ÀZ w/=TÚâR›–:ÆÁOz±_ýghÊf³YX]õÀtÔÚà Fp…½†põ‹"€Ï/ÞÜÜh!ÀGÔ#ຌ‹žä@ÉoxebQû”}MòÌ®¶6Z˜i!¦ù–|ÄXó'M‚G÷-׿QøÞžî£òÒr‘’½o^²ÕjÐ,%t\í®zoCÊ×쬱î×l¯]\|j?Øs”2/R Å>y§,Á³€i©§ ‡~ WýÁüÉ.Ç,`˜®~<¬¶õ|=ñ2sÛz#>’–Z‰ÏQ{ïxÑBjÃM rŒqÁŽ´C=–5Ã!¶ù ê®iÎÏOàà8]/)óû÷ïü ÛíW€þjvHî$ui£­œ´–ìÛœd¦=ï ù‹ ŠÚãTß;µ3W2ª=Nšlù¨º–»3ûøWÿFZjíþýê‰òVˆ¾MßSÇü^úéd½ßÅì ÍfWX€¥^è%Yö¹­¥W«/a\êA-5êdĨ&‚IgD|||¬zP ß¾~Õü¹±± ÛJFÏøªD܇­!2؆²ÔÈE´Øf£14e_^ßf¨_›û"„œœT÷÷£04@¿ wÏK2•‹°O~Nx;ÀÙ7âY}ÚnÒlnmœœpÀÞgT{\}²å|Àô™ú}€e] ¼…–Zñ4¯¿z¢_æõq_£cž‡¼ ã)ÖP³€Yà=,Ðu«g¥]Äã¤ã—ÉD<½Þ®¤¥žÐE)7ë•ÉU*íB©¢ö絨\>ê6‰ìQ(¦rt¤?U*Düà$q<`j•èE3 çÌÄ'0Üšr(¯Ì¬>J“@¥’ŸS8ޥ޹^BŽÀkTé¾ôàûJEB”ð¤õgÂnl ,¡W=4/-A„MšzýÊO”LZ’ÏJô'¯íæ‹"Î^÷³¥öÏÃj'¼¶k9Üÿ¨yŸÞDK¶ˆ˜–ú·üÚ3‚-´öz«¹po¿²Wn5›Hûå@Ä› fÙŽWr¹Etk³YÀ,ðfÈžÀ·å ›v¨Z×|±ý ˜·ç;ûÞ/D%d‚Ü-z_+I³Yíî½­‡“ӽѠL/Ÿ[òkò>·®eK0´KLåÂñ:¸&PN¥4+d,p|Ñ<½é—Ò´Yûœëë«0d¨LÎ'¢£:ÉNÜ}9ê'+&‡'•£JÓMyUÎ{#¬ ¿GhÝ¥“w¥;M›œ—åxxâwnÃ9‹5Õ,`xs ˆ–šid®vý§qÕ“Ú!ºW˧¥~s']ø ¿µ§u(L|8”i•iå,„f©¥àìŒ#é”;î6-µ³ËÃÜä…ðk¤YÀ,ðö ·„m•þ"Þ;ž#:cÇj™¡vЛµ„Zê·÷ÒE¯X‚¤dZWáݧŸVÉVΜ[`æZê^¯#Œ´ êžS€È_¦¥ö_sîÖ<³€Yà-@Pjÿñmüôsyz½qK¨¥~GµªÍËlÙi©w%J~Ã+ûeÀǰÔÚfØhÒ8bZ4O}éi’_{Ò7Â÷ö„º•—–‹–°÷ÍKZ„•YÍRBÇÕîª÷6¤|ÍÎúã~—ÚÅÅwgœJåú¿//R ÅÎwïb­›¾f©¥öß—ÌGtφp´‹—ºvuÅ:Kô&ô:å¹g¾æ›Í¦Ü¥‰×†œþ-µÍfb‹Ký\ýt2½zÁGÒR+ñ9jï½>N<P­p¨Õê‰Ç¸`GÚ¡ ¯¹ÍÍM~‚ºŽÞkAp~~ çm§ëñ%ÌÌ7)a»ý ð@^ÍÎk1I]GÚhÇC+'­µû6'™iÏûÒîOŸ²PÔ§úæQK«å–o~Ìjûrüg¬¹oIò’!¹×Ö¢%èÛã³sášñ‡cý9ØÜÜjµ‡ÔÎB÷†0n¶-•¦¤¥N JÆq©Ýȉ“êúæ~õDwfQuÒÕ?xZœDœQª¬öruaÈ·;Ϲ'Ï=^*³‹5 ˜ža{‘Ì1aËõ2kˆMo­¶Š·£¥VŽvÔÞ_ð(0¬ª£áU#E2¸vÔ|DdÖLd, ¼æ<[ ƒ;ÁûÔÏÚ ôϼ(UÑn·b`qÀÊC+¯¬?%(¸ÝŠçŒI¿µµ ííӃݿ}ûŠêƒaá~”×·DsÑr%¿û¶{–ÎpÅ=eýí!‹ždï³S;ÆSû¨ºžñð[Ò´À”´Ô‰AÉhz:q©'í,U÷ÀR+W¹FñóÙØ·ý}}|gðÂ6/ ÓX“Íf·±À† ó™–ZQ׳ìÝ#ÓR'œ•W~R‘ …|~~>Ô™·¶6×××..j pY׃¼WÖÒà Á7³E@vŒ–Úcá¡¥Q ¤øGž‡rÎç× ì™¶8>/-Ñ6³œ$•J ª½}™™øWÒøìÔÎ\ɨö8i²å£êz›~Ãjy/ ÌRKíBèyTMMž¥þq©ÝÞ¢3«TޏsÏá¤ûù’÷ºñV¯YÀ,0ÿp$u1-µC-Ïåéõþš–#@©ª\„±h©Û͆ øøøXõ  @¾}ýª?ñçÆÆ&l+úcÏݪD˜Bà°5D†»/C¸aä"Zl³Ñš²/¯U€úõ¸¹OK}rRÝßÂüÑ– d*a¯Yú¶õõõF<_ó¥Žmnma1þbï³?ª=®>Ùr¤2`îÁºìÌǶÀì´Ô¿åry™Äи“ùˆ¬L„Ì‚•É åR»ÕBéÏ{Ï(Œ<zGk0.Ì1íe‚3ŸÈ}í‡ÀpOr]nþF§æýÌΘ5ÔÌ̪½­V;Ý 6rÁÑñ1èðn" »§ß¬È‰÷ÏyÊ·4IûðnqàlÈ;%•Éëõ«lV‚3ø4Û§½íÕàûÎbƒ!§QŽé¬Pæí§yššL(L¥ Ÿ!ƒ\h¼:"–ÓÓóÁ)’äE0­ÒðÁöT”ׇÑ”GÆ¡öýr:=Pûö6 qZN«Àß~2åÇÆ‘vuÞ‡góÛà¼D—"˜–žùòêJ7"ùö5à qNw,[§sÏ·nùðWl¶DÙ¡z£b.(Š¿1(þzƒ¡K„J)J)äVöðþïåV³É£+1Û1Åm!&ÈBó¨ð€¡㛕 <==Õ+ðíGÚÕþõk’kYÉåÌÍf³À( |> ê­àªäÓ¾ŽÈ¥ l?ÆLäñ|gßûd H¡÷õÆ'Ífµ»[÷¶NN×9Ao/†Y¯É;Ý«øx¥þÁå¸ÄT. ¯ãF'VÙ¶T8¾hžÞôÎKi ¯¯¯À*Ö¾Ýû:n§ƒVm&‡2{¸©‹|9P ŸBˆ¹ßUSð uG”ÆðrÆ}>/®–š/]†±à§ÁÓÇ•Š\÷ktáKånv±f³Às,kPŸ§!~®æø£¦,½|Zê縘¥ jOËÙ>Ê´Ê´rÂSÔRGhÙ±Ú ëߺÊqÚ¢ú€‚¾÷«'.´–zkkççÏŸðÐðÓJ3÷iõœ¤_3=Jo½^b4 ˜ÞŪÒ\±X¦¥Žc罋~øJ¡ufÔT6XÆAyÉTJ¶BæÜSÔR ŽN„–t?ÀÔVå€è”hþw ûíÇ.rÜWñÓ£͹ËXóÌf·²€.8! e=?ÞÅGåž'¿.½K).õ[ùÕc0 ¼Ä3K­3?d›+Mô!åUñŒøÍ“ò¾‹VæKn›å1 ˜–ÁÊRëZÏwaécþîyóñ ä¸âLùÅ×,Rýš¼Ëð°Ø5šÞݳŒK-o‰õÁ«@ö –z¡µÔ/Œ?=Joýî.` 0 ˜æÒô ṯ›œ—õƒn$P®j™óFwÕ´ÔséÞÖ(³ÀdzÀ,µÔB ôBBª‚©SÄI)<Š˜\¼h<ô8~ýã¹”]‘YÀ,0 š£ËdpÏøæ—­©wa ãROÅý¬³€Y๘¥–:b©ƒÅ‡J»ˆ.Ö‡i©#úè¹·ËÒ›Ìódf7Ø¿ ZÝ™´‰É'ž¥^r¾ùe\»Þ•%ÔRã¹Ï#lùB€×OŸ²¬ã\Qc9=šßf5j}ò1.>tI¡éIy}}Ý÷SµzâK`Éñ™N®ƒÝÚL ¥,µt/ŠwaÜv|W–NK=8õý¬Z-•Ë,¾öëW»XÜÐ…ØX@ÉÆ¢áüJÌl^x®7O®>'¥.d˜ÜÎÎdá=Ã")Ï­ýÅéù~ÐeÇW:ùÕMÞ_õäY,åBX`ºZj¿: Šñ#Z’-õ#–Ú´ÔØ#âNÂM¬‘f³À€®î‚朽n×î`ºÁþÁF™ÔRë¡_ÆO?”˧¥ŽðiÂÿ8=e1?}C³ä¡ŸéHødV.ÓEù•5ÿøãÍÇ:8Ÿ?†½fÕÀäÌȃý}ølVf8;;Ó”pi¯º“äò'•üf sO¶qÀr*¬€è›Fzp¼Oàùuj¤^þd¯ ›Ëƒv}íNJ“üI]‘“»»%SÖjQöäãIù¬ƒ¨k»x:›É€ø¹ .P/Ù­CÙ㼦Z¼þ—ÏŸÉB^ßþ‡”‰Ë§®—vR/µÛ|Óév˜óPÚ,µÔP‹rZV WíYê…ŽK=]Í÷<8µÁ,`x@Ò,B;õ­Û‘ò™žh|óËxz½#K¨¥Ö@1£6mauUᩇ¦ä§½½=ØkxkH°²à6|6pÅÎô$oyÔ#à$ó)9PòFœ8Í>%Õ±ð°¯ÊÏûo!x\ d‰rözj^ÊLž¤p­hw·t|UT*íú”µš€ã¾zÁú¾Uz€Ýºww\_ÊÖÓÚÖf½õŽ,g7´.®”Ö£èu»¾ý>%?ùË—ò»]E§(jgè`Ü}²ßгÔR;–Z]Lý̯žhZj÷øŽï÷Л¬Éfe²ÀM[âòº¼‰k­é_yÄR»ˆÚaXgÙAoÉGÒR+¸µ÷.èˆtJX4È1Æ)B—õ]–„Øæ'¨kÏ+ŸŸŸ*†gõ Ræ÷ïßI ÛíW€‘kvå$uË倿Nø‰ba)ʳԞ_§Šz½N2–hö+{+³ÛwºšRÕŸF„t:””à×döäeBc¯­EËÎ?Ô{ß¿+[O#5}Rô<´.*Ò+-ïíùöû”pðžP羬­­i±\ Èʧ߉X‰okYj©•¥N®í¢+)š–úÑÚŠo{í6³€Y`JhÂ"Ã6å„HnÝé²±ÓÜDKí„Ô/ãhÛŽoÆÇÑRƒÏ¸¨Q{ï|£è &€Ú ©‰A´£æ#‚\¿~ýZ,ÐTxÖv‚'ÕÆþ¸ÓiçóyÊw:úâÔ_i€¢dˆä=§ê~`©ãk§÷·¶6Ÿ4 = 6²€_¿|ád4Í‘d´º “£”µZ¯×ÁPØ>{òἿ¿ôžäÈ9™ÀÙ:š¡uqr°ýÉ”Êp»2lâ+¥ Óì/¬¬9°À,µÔî±îŠ3Ýw{âRꣲwq?8žrŒçE,sœÀš`0 ¼ÀÙTK¹lw¯æú´‰j–N¤—4-õ‹¹ùèžš–ÚJÍ1µRªºA!ŸŸ‹apή¯¯]\ÔàkZçì Oò¸þ8“ÉÂXƒ&É×òõW.ˆ™ö ïXÛ$O¬)îè%êõÄ'4[OÂgC‡#Ì(—9éU@ÒZ‹î)+.e¹gO^cŠ@dñ—Ç GNJ£“W7´.Nzlãdž¦LjÜ1#mjy;¹¸˜¡–Ú¯ðÒs_b=> —Z\ꤶoq]ÇZnXf @!ƒ¡tjÅœX¢Ñ²=º÷¦¥~•Ž\ï‡i©1Ò $¿ˆ½ð@ó}o]²*y|ûúUâÏM -= ‹˜BÀ pØ.CßéÞõý1Ú -¶Ùh ¦¤€²—qÕR«¾lê‹§Á'ík¤""íÕ$S>‚§oÔë}íO¦ôÇ…BÁ«Y†Öå¥XÒ M™,Ÿ/Šx¬`ʇ÷~˜¡–Ú)§Ãûï…Þ=|‹ zD,µªA䪑Wžj›ßïÖ[Íf³ÀË-@Dj¶Çä]ੳÔÒcñôÔ/Õ‘ëÝýHZê ý Õ8ƒ‚éz TÔÃþ¼•ËçOÝ&a4 ÒTŽŽô§ÊAÑ'‰€‘I¥”œ†$† æ 8†[Se©á˜)Mq”JªTI¦t`º§èäyÍÉ”§ñ°ÔGG¢aÛÜÜàOÊ„zöÁøHÎæ$eú&‘=JYzH™´‰ÏÁIô§¤Ýô¯±«‡ÖÅ•žžV‰øÄÅ·ß§,'Z•,ŸÁC“í´ãŲÀì´Ô¿ñ¬2ìr~Åhhž€e 6 ¹ý½ý£Ê^‹ï³ÍM™ 1â…EKr¼’sì–mf³À¢Yà¢ìž›ù º)K½«An%¸;á…×öå"¸j§[R‹‹’dûçY@4î­HŽÓ÷Na1_Tp·^ÛÊM"Ífµ»[÷¶"Áë ïÜèl L/Ÿ[økò>·®eHR4¬qô¦²§tòå“¥~ÀÜ>^á“é-Á¢Xàø¢yzÓ;/¥µ?Ó^__±~¸ ÷…MÖQd‡î¾Iõ“•Á“ƒÃÜ’‰:’Þ}„mä‚ÕBáw04g3 žvúiwÊ´Ô¸ùEñk§YÀ,°óÙ²Ž•BQM×GŒê¡CÌ/6›Žë -ÖÇËbžD–_>-õ‹]n©22EòLj’Í…#2Q ³-Q‰LR”«:¢Þ'IoiÅ3ÔR§äeÓT©Ù@ꊻ@×ÙÕº-ÉùEqk§YÀ,´@Û©åœäƒÎŒŠìgOkcNý#Ó-vÇËbžèXB-õ´<ðc—©T¦©v8¸¬Sëf­°OɹèXϰ.—c=‚xÖ8l¸¤T…I²„Lš³!ëYèŒõ{MC q9š+1Ÿ½G­ÔK­iVÁpuu»õ‰E $2 ­ £r$oÜNŽH/“ãÓîZXÿ®Ê ¤v”ŠI´MkL»¼öäJE×K+K›VöJív«XÜàkøþAâî3è¤çÆçã˜f³ÖKþúõ«¯m½nï„/î““_¿n'¹®\nV ,„óY#Í jØÞϬó’“uXt£Ÿ‚WÞ>uõåÎ.ë¢ì^HUW…öƒ²§Y÷¸sè 6wYšùyËŸÄCË+_“¥^†½G˜1¼¤Îò~¬xçmô¶«½íÕàûÎÚnáûÁº‰íŽûØà¤uE4Ç,48˜p¼æ‚ôͯPs°¿v! ˜·ùÎŽø:<.o½ ŒoEU''¡ëî°ã¬hÈS¡)Ëå2¤¸ŒA-,$N®Žk0üz›ñ‡ #å·ZMÞ’4R×Y„ᦩÜSšäOúŠXϱZ*J¦<8ØßÜŒÔø:)ßǼ£UQí™ ‹GºuÅ{¬ÃÂ%ÓžX¹fh]@‚Ýííf«ÅÒ’Ô¥íHY,VãËç¾pÌR‹§§ç´C½xÝŸ÷s.«yœÏç·Áù®N*ï•ãòê ‹d^çöö†èâDævÿec£|p|trÔl)úZ„…"ÂÕí‰êÎyؽKàT}‹—š-xlÔGüG¡XŒWNš”ƒ7÷4 ˜ÎÚÝI=Õ~8®Òšžíª4ºS¸ D”éË—ºP$Ÿ÷îïC¨ß/OÇ$y’0·c½1n[¸ÔÑ××7þz©°Ò3Ö}Éù üÊ ð§ ¶ÃÃC–8gI™+¥'y§ ÿýëÉ|J”üCƒÈ}Jª£¾.HkÎx&Øóëð¸@[à){êDóRfò$…kE»»¥ãã¨"`÷)k5Y‡¥o£Þíímß*=ÀnÝ»;® ˆ¯l=  m|Ÿ(æZWZ*—¡Ø0Šêñ)ùÉ_¾”/”Ü9‰¨ý¬Zq‹ìô¢Z`¦q©Å¹:ðТ‚“½ªL=.õíÍÍúGç’ÓѹGzØó¡çÕq¬Ýf%¶kÇŠ–ÚahÇDwÞQÆ:³ä•Û]W &Ë«GÂO3÷®X ãê«þQÒ¡ì% míîÁGÒR+¸µ÷.—ø xä†ÒfŒ¤zRLãÇÃ6^sÊ7Ã{éüüT0“;ý²&¼Ó!hI iíW€‘kvH(޵¿d7‚þ¤XÚCQ^Xì¿„¨¢^¯“Œ [þ…KS¹ö¾“ÊaSÝùé…V”%%ø5™=y•ÐØkkÑ(ñCía°÷ý;US _W<)zZé•–÷ö|û}Jxh޵jîËÚZ4ÂÁÕ€¬ü•†ew ÌPK­«'JÖö›~MÞÌÅþ Zj¹wÓЂ¿»XÌfçY %ý˜h<úè`]Cq* ¾è„ñGZêæuï¶%ÝêZ.X+?”1<}tW‡.œ–š«QÀ¨½wâA-µþLt´1²ê²žÑŽšre"c±Xˆ{Öv‚'ÕÆþ¸Ói£î |DH=µ:ý•Ú%C$ÓžϯƒSû[[›Ÿ?F¢ÙÁ¦@ðë—/œŒ¦9’Œêׂ—»\Ôj ¤c(lŸ=ùmŒ1ÉVIí z?³F<†ÖÅÉ–=®#™Rn-ß+^È¢ÏlûH˜–úwšÉE¡TÝŠ£59ù´;¯OÐ";/˜Bû?’7Ùµ˜–ÃÇR«ÒC;0%ˆu Å©,KNø 6}çG埞Ê1óü6¶Ÿî7}ò6øcõÆaïw¿¢T}ifŠám´³pàyä•Í ¬¿@!ŸŸ‹apή¯¯]\Ô@áÞ& ×Až´˜?Îd²0Ö Iã=¦”#€˜¡¨Iw¬m’'Ö–Ü¡ÒëõÄ'h¯õ$|6t8 ¾ yèIUBKnŸHYq)Ë¥8{ò‰¢è+äÈIém˜¼º¡uqòa>Î64eò¾`FÚ0Ôòvrq-yKÜçHOùnŠ"åÅQJãµY¢qEù•C>±H/89oŒüJÌÊôDúœ‰<.Ö»Bnoÿ¨²÷âRoÈMO”­/î)±ˆô§'ãmóÍ­óBP†y²3f uó„¹õ„“^©¤™†x}qâЭD=ø¾G$U'­„ü§÷š;¸z|×ê­ì†¹^kïûcät¨¨oV³Íû@AÆùé©P´ŽÛ“3½€qùGg–;NOü0q©'Ç~¾Ïߌ*,ËKq£^ÿÃ}¡µšÍ/ÛÛ€W¤€<.³ö~üñ?­®~º¼ü †æW²(ZEK (§’˜óJ6ûÉŽÉcÆ«««TÚl4˜µß—’G›iz¤Q͆/Á·-5?Ñ` â›füœÄÏÑr@Áué*¶™Ì‹eps³Ü–\½Äh5ÇüDèz9XK¿êš Õ y\•¤ú¦tõ KDg(êíQK¿(saû D?†õí©L´^¿Êf]Ä8Íö釈ø‘ˆSÁÅAà•A±M&¦RÐϕʑ×*€D\ϤR›üTQiÇÅÙÓÓaÈžhõ›=ϼ=È ¾ˆ^ÛDàxâÓX=tö¿òö'@‡å$~õ±J¨xª?|¼‘Zí‚–»ˆÙãã°5yùH@÷Ugoå²Ùã“(eòñ#Ž7âpv{DãvÓrªZ—DüØÝ†s§èXT|âSÒ 9Deª-âÇ«ºÈ¹ÌEü(Ñ¥HoŒ6X#~ÐØHó¬}MÔU‹ŽÃËÖ鸈‡Çx>ü½FZ#~D«'Öx PÂêû`}5«,5_Æ<º2A!!&z’Ó¥Mó“F!õÐöðSûׯÁµÛ¿b«'ÎåSa2 Œ·€.ØÚ“Õ^ú@l¹ÿîÇVPzÝ÷ò' 9¨½~I&)îïöÚA ÓÅ(L‚Àh³c5Äc;¤×\=·õõÕ KTL›óÚVM³9Ç«'ÚS9] €øÁ¸Á´Š9áøÓÓ“?|f@~O«j+gN, ,õy)íû·z¢, +¨:ÂÓ_N÷¤zªhõÄJ…5¬< §×_-°z¢®v+ËC¤K‚­]îÅ×R =BK Ô~†ÆzNÁša0 LfY„Å©Ýè#û´Ô k(^½z Å«É0úìúЪ\¶' »lL¤!lÖrÆÑº|ZêÉyÙS¡EùñCT"¯ÜàÈ5 ³- …Â$¥¹ªE3cÛ³Àì´Ô¿Gk%JŒkºÈðÍ®;âÀ•f™FÜŒE-çƒù“]ŽYàÃ[ÀGŒÖñ;׳=„߀Q`»~š¨g§;'^ȸø!R~£Þ­·CåNgúƒŒ m„Å¥þˆq©?ü“õÆAFòúJ| oñkÇÏz!ã~¼:VÉ¢8еÓ,`P hÄh$lƒäo.-?Á1óïÅ’JÎéð µÜJ±žäY¬gðôî|˜¸Ô/v'Ë8S  “þù³ŽÞãçÏŸIÑL+µÂçÓ}±ìâˆQL›ø}á•.&ž@cÙG?\<C&ºÌ0üÝk«‘9þ¸Ë“mY¹êùt k•YÀ,0ÜÅl9‡oÃà®×½r$õ} QM‡W‹D¼/1#B:JXñ”´hM¨2g±¨Ñƒãã×ÑÐ÷Ë"Æ¥~‰3Y³€Yà­,0­¸ÔÉõ[”½ù]q÷#¬íùéÅ×ROS«;mõ˜ÌS±@ëNŠ‘YƒA·×þÜ×zíã¸K“ŽHÍþ5†[Xjb}€¥Ûwºbb]{¼”â0)÷rj¦GiÇõ~›–z*~o…˜ÌOY`–ZjYçÅñ8‰þNYjÓR»ûâ§ë?u—ìw³€Y`n,i©¡{µ˜/íW명¥~Ý Å[XjG„K™ Xð”Ì…DH-ÝÆÄí Ð{Ùò:Ÿ:þ9f¼t¦"WÇf›YÀ,ð!-0C-5k8‚ #óã;,ÉH#¢ŠêQüÁRÿ^eeøÀh9LéÁÜ:W™méÍîëñ,!°×V$ Ñô^¶5ˆ—Gˆk·cغíõîÝÒäqŸé:ШŸ´c÷šÎß;ë/®–Z#"ÊfW?}"¬„_¶0·²ú6ûl†˜­ß¾}õ?q¾ÏåH£gðOBSkˆ\ÝÀ÷ÛÛÛÌ«£"ÊõÜ"Ƕ™Ì/¶À µÔ7€îð¾£AAe¤ôþ!~5^Vý´ÐYñµ¿øÞYF³€Yà, ›Ãn·ÓPa.̃ºöšŽû”&}YuåErjTLOÜÊyñ£×¾“2YecÙ8æ×_oäÉþöáø]´Ô“û+¡‹·¿Èö«Ý¾¹½%¬‹ƒhvZ.«oËúÛb¶nmí°v þä¾´múñŶšË¡“üÙÙyu,CȲ#~‰ïÉi)Íf¤f¨¥V–ú¾×u Z¨jY»ü£Ä¥6-µ=Hfå´@ËÈË-!ˆS†ºäå¸S—cG˜n8–úüE,õYS K”2ÁïmúM„Õ‹õñln>‚™îM¤h3ÖUëHéÐñÒhu6Îí9ã'‹?:ªì–J,sMJÞÓ„•ð+ƒôµ´ÍÚ‡Z`"x`TƒûÄ“ h®ëÿùª)PWõÓò}!O¶Í˜ÌC-0C-µ[àů ë†HÝЦ¥Nróæ—f³ÀbY@µÔ¹°Ò CY#1L„ŸÀÚ]¥V×r2¹°K€ê»g_ܥ㶷Ö\x–CåYØðì Ø¯çq?¶ÞÚÙ~®´Ô @û„cÔjWž{îKÚ§ö&2¨ZÓ –ßÇ[RŠ7›ÍÕU7¶b›YÀ,ðR ¼–Ú½k4ö^ÌR妯û›ßí;›g¬kø&å¼ôÆY>³€Yà},@\jY!<õS{.>µ0©¼ ìÞ9oæ¥K»”TÏØ`¥¯[R~AƒP߸Ùl,•%èÙôÓ#4Ó‹ ¥†ŸfÃ[T­>¡³“{ï+è:Ò¨}†mø[RK ¤fÝ>Mè9iŸ¯·ÅÁ£*aý‘gxª%5 ˜,0s-uŒÙ¥kð,µ»Ý´Ôæ“f³ÀYjZÖ5 E3ô²áÕuïø$¼¹ ‚Ó~\?ŠûÏ”S#¿¦„bV´ÓÒOÞ‰Ê$„ñv¦Å¥~ž"Çš -5ºg6¾ŠT­MSv9¹÷ÏÈÛO:ì{@ú´Ô,m= |KÆÇ4õ K `’b¹\^___ 'Ñšj˜C ÌTK-/ÅÍpÃÐ,¢4-µôq mß:…5É,`a(jßœí¥›°Ò72SäºÜ´þ™hz׎·XÌÁs—QRK^ÙZÊqÂíªí7´h;žÐz‡7¾—–zPë<êQ+‹çççC£öÎåóH8|.Ž9“,¤//AE˜yppÀôD{èÍfWZ`¦ZjÁÓ™T­û‡õ…éѪeçª_yÿ,»YÀ,ð– ‚ž¬k˜ê&Cé‘ÍÂ1J/v]Së ¨vkiÔfN]¹éŒn,CVf7F±>è7 —”Saµ+Ô¸êÉíàŒ>WZjÏO?éß¿'æÝÅÅ…û(èq@œ;Í5&r6ú 6€2ÉÀÓ—“u%ó"8!6ßéùùÚÚÚ“í±f³À“˜¡–šnÀ½uxþS²‚ZâèšgmZê'ï–%0 ˜Þׂ—{LìÒõˆÅ¼[Í÷VÀÎAïªf²½v= šž#”!ªÙjÇýÐðy¨³ó."uЬ÷îïÁïÀBã§Å”Ïåé¯ÌU\êɽ7—˺¸Ñ…Â*ð÷èèH³a© ܱ··øF® \æ¸~NæEïqS¯S‘ê¼cZOÞZKi0 øgÓ÷9>š˜wñ3w`~eqÁÃ2ÏÐãûû{ÒËñCúX¶†¿+ZOË>”½üíÇWͯ¦¥6/4 ˜Èˆ`éɲaKú·v/\ÍK£¢DÜoµz¬tx½¥’™¡ˆ ºÙ ޝƒf7¸ëÉ¿¸rÑ׎ÏFõÁ&å4o¤%ÜÇ伬qØI[E6>ú.q©Ÿµz"ä1Ú7úWR0{Ì#ƒ2ûçÏŸÐáì}$Ÿ>™WTÝq|k=^ 'Ñšj˜C ÌPK­¸ÛñÓú‘ëª`s‡rnÞâo¼C{æÐ)¬If³À @$Óse3½è8öV²nø- "õ®;2 wßìuï8G|‡r1hÞWÁêI; ²ÇAæ0H_k.“Ü.Ÿ­Ü¶t“­;é'³NX2-õs¸j5îäùåMv1fn´Î.âG§ü]ÍI¬}:6‰¸öŠëa˜éÕ»r¦SÌŽ\Þï. ïµWΚ½Ã›öÁÏæ~1¼kÜÛ2÷,Ú(Cº‚¶Eè!Êl‰X܃±N;aµë4£>ÐŽõÝñ¤œiçJKm,õGï!ìú–Ú3ŒK­œ´ëúîAƒäÐÍ‘wü´Å¥Ž¹“¥ö?»x³ÀY€ Š€éƒVIìÂRg£XûÊUçX21ÞÝwNPÝ8 /ÎÂãÃðà wT O/‚«Ëðö¶×le{Ý?nòA·Þ ®#œÛ:” !ÑÈáùØrài‹õñÒ8'‘iUK½@†5Õ,`ðÈ6?:H1åÐãÞ(b‡@aÁÀ°Ì±^Š •J1•Q•XÇ¡e‡£GXÈe2Ä}й—½âwÓR?Òö™šÌ bTÑlÙ@þÓƒ<†™Öac®º·¶&}Üe·wÜ~Ô{——=04’êÜj¯°|Ùîí–v¹œÉ­ÝJ‡Ã«hÑò‹¦t ºæbX¿îµÛç:êÃøéÉ8éAÞZýÊ´Ô ò|Y3Í‹nj©qã"ƒÄ=š o¨jØkÓR‹ã¨šÜ6³€Y`1,ÐváòÙNÐ…“¦7óZjw6šÌ) ZØ Ì4ÜßYçù[9ÜÝ•ÖÅB͆Ì8,}ÛXM•Ûô[§²È"‘Y3½ð´\JMk© Wp]…­›èzËçÚÁ¹•i©ãé²Všß3ÔR+’~`¦#Å¡œ1-µxŽ*#m3 ˜Ä¨ŸÙ²AžºÇ"/ ~Zžçj5¨Õä©Î¤z™TPÊEd"Äq{m4>†áÞ)„7k&b’N¥\7¨TPQ‡H¨·Së#ê'_ÌÓ;¿Z\-5+¶ìîî~ÊfW?}"È´_Ÿ\cHË>›ù$?}õ? Æ–&>^¼yY;†\þi«Õj_¾|!î5…ÊzÔúç òtZ3Íïoj©•p܂ӋÀLÃè8–Zλý"Æ¥fÎÝÜêjaw{;šÇ-¡K:9©Ò==çºÞß ¬f³À$ ÜÛÚJW‚EgÒÒiŸj>©†·-Y™ek3(•8l§…º,ûxüÉcò–J§Áéæ}£~0a±|q´[¢ÔÎÁ;¬Ùgó²Ïåq?vúè¦Î‘–z7Ó4www,Îö«Ý¾¹½e^#K!z_òñ¤ýúµµµS*•ô§AžÆS7«¹ñF’«'²"c¹\&îõÍÍM>Ÿ÷…LÞHKi0 $-0øÔN^¨3£@z‘îpÁãRU*Åb‘>¨P,rìº1¹J¸γ¬'Ï;64 ˜ÄÌ&dË­`%ÿëã®Õ;> ÚíóP¾•Ðu¸…׃nZÖ&ºAÇÅîÊUÖJÕLýòvÿüf/è´ƒÕT°‹d$ÝË~qxz‚¸–fT,”fFýs²Ö‘Ò¡ã¥cÖ&|½ŸzÎøÉ¢ŽŽ*»¥ÒÎÎŽ¼AÃeÿøãø‚!gÐv½.A]Êþ‚ý  ù%âþÄè(êÚŠétšu}!O¶Í˜ÌC-0JKÝëvù×j·»Ýö]«u×íFû»¿ôzÌvWO:Çãr‘ÊÃ=Ù¿«–:¦ùÖ–·D¤;\p-õu½Î:®™mäîu£ÁEzNúöö¶úã‡ölÉócŽÍ/Íf…°(¦åL!è@BKovÛ kWÁj9 ’.—‚t&â•9ØØZiëÕƒ6˜ûö]9”Ý_žkIT&ÑÆ%›…õPc²Ì(ºKÔ ºWæªjÉnÀ\óêUÇ-y®Ë]‘äJݳ®´¾r¥œûTÆ5=£mdשׁuÅå?º.-'¸ïDf‹ž)º<”ãj‘¼ÅüuE÷KíóT7Œ´³´S­ËU»!mÜ;µª2èJÅ'}cð~9FwJÛåRÈç¬åJÞßœFÞöø*â–t±jäQÉöˆo¨ï»½Ôƒîû£«ð~8¤=±/‰Í|/²ƒ‹|×ë²üTìo\V†åÕ=ã'Ì?Sú\àÄðÈÝ·N»'0‡íôÊÆÁ~¦ ¡àŸIÔ¨?ûLi+è´×W­N b‚^XÎËjÿй0¦@êÁñ=‰)4‰é:¯14<~Ì'èmWgÅR«ŸôqÆÞyÁ±¾¾>Tû1—ózªV«þ;¹=”IÉKhôÖcšd?™Ìã-±Ô»yòîàKõn‰I©ˆC~PÄoüqžjŒ}ÙØ, c©óùµ:,u³ëÇÔÖò+ûû•ïežsƪ`©µ‚yæ¤ÛÆGA.—£ŸâÝÉß}iˆHäÔ v]ê(‹u/¬Ív¿ÌÌ^àÈa©¯j5¨Ÿd¿·=c–zrDÂÇ£À¼_ n`± ÔÙt†â–ºHýãÇ^R¼g&¦‡ÎAŒ1ýƒžÔÎôG”'”O.÷é© ˆn›YÀ,ð" (K]+ ·¨›°ÔwwL5‚µtDS;~MešŽ“ª]\¤WVx…¥>ª4Ý·­²Ô"üX-ü ®º<ÉY‘ÞQËrûgÄǘƒôÄ¥#ÖQ9Ž®ý‹hskób=#v¿ì~Í¿¸yÑ»)y¿â÷܋ިÓË&ಠ …U^Ì(,µø1-3ÊV†çõÄqO÷å%1Ä”¿º þUO¯ùV’Y`- Ï¦ïOà;´Ÿ‘sOëL'7óçA€(ÀjËÌÿœ><éai©k²:‚ ²’jýCh©ÒÅì`v00XPx/-µÎD\F¬a×lX ÕR£È(—KQä"{Ä Ãä¡¥V–:Æà.·[âÙ´ÔÆ!MÂ!Yóóóù€i©ß^Xfe³Àx-u¨ü´*ª=Vå´ÓtŒ×Rkg¨"l· °®H7bžµi©MSn>`>`>`>ð6>0ÿZjV{ÙÝÝeQÃÕOŸ2Ízà PX‘…ÇeŸÍ|’Ÿ¾úŸ8ßbH£g¸^¢P“kå*– ýØõš¦nÑq©% µcª]Üxerå§ý,û8.u´ L2.5ZjÇI§Ù‡¬ý*˜:FåÊU Z·¸ »Ã|À|À|À|àÍ}à]´Ô“¿Â‰e»Íò‰_¾°HøÍí-óY Q³Óò6+÷v:ív‡•z·¶vüºåúÎNnÑmA°šËod¨Rœ ¹hQ·Éh)Íf~ ŒÒR‹Æ#"™…¥v ¿DϲòÓµÔkùMö4.µàip³£§)1ÒŽ ]³Ê¸™·áfÌÎfgóó%÷÷ÒR{ÎøI0rtTac W ÍJ‡º¹¼]=Lv‚¶YÊ7FÛïøøÐüòòrpåÅ“““ÕÕœ¬ù¤Y,Y`ž-0JK­OqôÅ)ª£§YãM?­¥vé‚ Pœÿ9@žœÛ؇å]ÙÆ[»¯³Ã›óUfs/2X*x/-õc0<ÔjWž{îK×Ç4×j5›o°ü>Þº//ëC]ÿþ}¦qNæYÛÌS´À(-µ~ GOY"î‡ñ]õ“ZjÕô ¸ÁÔ‚ÊTÔÊèç6»%çKÌææææoìo¬¥†Ÿfãݧh}‹ëÊ/ɽ»£ëH§ÓC_ö´<©¥RÃ4kJee’[DPħ’l4ÇH´5¯±ÔSÄUVÔÒZ`´–Z#s8Ü›ˆû¡A`>`>`>ðæ>ðÆZjtÏl¼U­˜CÙåäÞc·Ÿt8€’i©üø1 |KÆÇ4u’>8ØßÛ+¯¬¬ïƒÞK‹‰ìÂͯ°ÀØ¸Ô –ÚÇýО_çd\êÇZj‡¶Ðz§ }ïö~õDÓRˇÊè5Ò˜è yðùs±ø¹xxpÀ<M_,ØØå …ÏŸ?3[…>WËá|_™¤qýiïââbãË—ííí³³³ñõjzŸæúêŠùækÅ"y)d0ïÅÙÙ—ÏŸw··Ïkµ¾¼ZN»Û­V«ƒm{÷q ,\ ‹ZδYm«væÇ=ga½oOo]½^¦‡LϽ®ñöñ÷:ºïÅ!åÓ<,<ض÷ÿ¹mµpÎ"þ‹ ’6ô>Œocù¤o÷µÙÛ¿ÅñaqÂçŒk¿/É{­Çƒ>Ìâ¯GÜÙbáYõ}žëãÓ·šM,̳‰…1c·{çû]êÛí„o÷•é¯ëÅÏéxû$û+=ìC^üì¼Òžï¥¥ž¸òøœŸŸ}¹a”sùÿQ{£ñÀjõÓñç>?cóñ÷EŸ#mOåèhsSøÂ¾6llº;K×õæê˜ëº»k}s_, ãÀQ?à¯ëç•XøÀYX}»¯L]/~NÇÛÇßkì\©`áÍÁ{÷âgç¹ÏZÿµ'æöôÝ÷™òµžŸ~w o&ær¼=9øúõ«æÓBú+6(’§9¦÷NÖ•Ì«|¹ì;‰"sçO¶Í˜ÌC-0JK-±¤½–Úi¡u†a¬¥†¦–ãñq©%I·'$ŸÒTž¥V†@J{ç´$éw[;;[[›¡ 7·¶*×'ºxÞž›áέ­­ÝÞÞª í©g"år9MY++ÐÉ6ßÕžúñy2~û¶Ÿ_[ãNSÑ÷¸ $Ô6œT«åý}ee%·ÿíTâ oT¿ºúãÓy»×GjáÍÍ0a‚ÍÍ­Š{ëÐ~7õ6âé…‹XX¯«O#•´9ß*üñ#™·Ïžú.|}ð ] Þ»Ÿu,üÇ`Û^Éá½æYƒ5':aiþ1,5ޱ··Çý‚ræË–ãøK&ªzT^ÓR?ãÞXR³À ŒÒRÓ}zÜ+ü´€ja©µ’c¡D|\ê-§–ZWODô”–µÈ{÷îÄ#È\q?´|ÖíÁ.‡µ»Âa«pØ<®«ñûyGX1b‘å`’<™:c©ã9T(Ã|.§iòù|ëövð56SßyšAbZB 'xÐÁö¨Cèy0w«u;Š7zSBñð0³QL ©ãcsðzÕÂCïu’tn0^=~À—3†Ë<¢_ ç°Ÿ´L?¯Õ\.?Ê?ß0~N/gï Ùn1Ó=tæû}Ry{âÛÉvòóÕUªU}o‡ ߖϯi¹j_Ý ÖËPœÌÖ,ëõµ€ÂËË+Ø©ñ*%\lÄá Y5i|óM³yʈûÉ ¯‡ªåÕ©âŒ?tïî\€=†oz¯+Ái•a‘€Õjêäx¿äµ²Ûäq›q]¯¥v¾Êÿ µù‹¹Ì'í+Y* Ã7Š{~³q€Lï8ìVÃA§nØ«†ÝãÁg‡y Š }¦h§××~þò…O•áú($M:­/þgqðOÞÀJ©TSæ˜ñ‡©÷Ùãã°Zïív9ù,¸^}¨‡ÚA,Ï _Eîãjó'ýpT?ù¤}ªbá!c,cnϼ§ÏõÁôÊyÞ(ygÊ×bÜ%‡ vùfd¾w±¬†÷3ŽSŽUÔÂRÇ£ßÂRO ¥Öµ]"Ëaë0Œ¢SåfÁCÌ[™µ†cb^ä¼Ñä~°HÅ¡z}- Äq¥â&z‹¸–h4€©'‚/ãØJN¾3’Ï“ï§gsDù`Ïtšw¯çöiÖ××y=ð4¶ål#ô¦oÈ¡™K™(©^'{™ßÓ½0¾Ý»È&ƒ¼××bað4Ø%1à¼tQQùOru/³šu XpLí»„Ýs§ ‹\8 dúTßuáÚ­ ^/ò ¥þùóû÷J6 &ª.¢gÄùu¤7s§ðäx9åI}x(ÿlߊøƒ#P_ÎsOþL=9>ž‘–Žþ|Ô6oáÁzűF¼Ž…£.bÄøÛcÞúÅÏéxŽ +EýžûI» õ·q÷4î3¹ùùa©?Ô°k1 ,ƒFi©¹vÇRË„B:p'ýŒMzé?ÝBäüi©ãѳ‡¾¨K †–ÑF‡ª#œî´#¸©ó=rÿÞ„{žœ3 ŒkÖ˜¶ñîZæ¾)Û2%%¾^Ž9C-¹••f|^fçrÚduuµ¡3ÇÝýФôí ªfÞ#T.Å2Fùîªk¬ûò{°òP eÀ\«Õö×(Ö òÚëúÛxŽY4ëåqª¿êÉýpB äþ2žÖ‹ÅMâL#’w˜Ý— ÅÂÛ_EêB5¹Ü*ò=Íñçç>×ã9T”ßC5¾Éö?És¿ÜžÃú¨G/oîĽX__S Ö;Æ—Pi,µ¹X8—KÞßuŒ··ðœŒx»½‹–ÚXêGîm˜>–Fi©õ*u–D¬!÷Ç¥vsý›=ÖR;1u„§ Kí¨(=~ÏäóÒï5¿\ &Ø\ãÏþë?¢®jWØ”#çìSŒò7ä"Ü^«ÝVäA–òÞç·vwEžáfˆÊ¼±ÍÍ ù¦¹)6·Œƒ2ã¶yG" ¨„w3¶Ã%ÃïÑ›ÞëmH8ûºý—!×YøÊY¸+Sé5*‚ð £c;ÀõððT-n·ÉR.‹…'ÑR·ùû×Çs¨þizƒg¤0kVWà\/í&œ=¾ï¥Ý]ÎA¼ŽNÝñðÔm'€ÃããV«IÑxÔñq…3Ø ÅoñC|?ÄŸûüðIsOùhÔi¾ãËtoÔÿô¢°ήt Ã®·´[R “@,\ 'ÆR†sÒØ“.„ZøØ=Ô×õâçtŒ} ±pÑYxNÆü½V~hžµÔ‘ØÌf±À(-uÜ|ÁÓÉXÒ ‰K=TKí”"AÏIXjAåtã/ÖíiiìÞ‚“›v]{éRQÐJ§‚B¦²9D3ËeA¨g“ÞØ š4o}TËz½cø6—¾•x³ ›uP! áÈE¼€wy¡2Q‰ø!ƒvûúuˆN—yâDÙ+—™›¥°ƒ¼~î?¥ÑNö¨#(\Ì–úž‰Ý±ºÛÝû½ƒÁëÍe#  oJ¨)gaõÒQ~%.y ˆ…ÝįIô Ccªø¼}ÏB2~ZvD1Þç_Ì/>÷9ꤺÁN¦1U/ÜéòQÑ×6âàâºH2q™K'¡}sy~ñ·\±5ЦÒN‰3Ø/tú"€ú.®Œ?¶yT<Š¡÷4y/¢oŧú“'µÂSì‹Ú™ÞÙÁ{éS{¥RÛ}÷•[ø‚‡Ý[xÔœ Ÿ7¶ðbkyfwv°gòÞé“GÅTÚ/%ûµð“öyãqéKMK½ ÅšiX ŒÒRG— x:Šõá†z Kï7–ú·ÜJî¦q™"eñR Ã¯Ì£Ê †þæI p®:ÚìX av0;˜˜˜ÌÈ Š˜NÐ~] ÅÛy»ÚÝ-„{[–G™âÉ™i?¦hO+Ê,0W8¾hžÞôj%âD<³ãJårÄY€ª=âPu€¬úŒ©k²’Àá ”G3šv'£ˆÅ\PX-ñÕÉätö‘¢šÙF*3^mã‘V=ÅKY³›ù€ù€ùÀöù×R£ÒapæS6»úéÃb~}r]ïPöÙÌ'ùé«ÿI§Ð$7¿&"׋Ú'žà+I’åèñ\¡kŒY`á,0JKýO«‚#¡×è‹K=\K­ñ=$'AµTIÉáRk©ŸÔ}b%KófºU÷¸¾‘FÖê2ß6˜+x-õäQ\Dwl¿Úí›Û[è+·V®ë³XéP=”µýúµµµ#1 ÝæÞÕ6àʶšËA˜%ã™$ËÑ5'ož¥4 ˜-0JKÍT!Ö‡Œ‰E’ü\މâR |ŽçøƒÇ]|¾ˆ¥^Z-õ“šB,fil Á|À|À|`¦>ð^ZjÏ? GŽŽ*Ì€a‚bhæ‹ëš\oÕ¸Ð63b´Ý_°ÎÕfš)VΓM²f³Àx ŒÖR»DNꜜ÷³×Ä¥V‚Zæâ óÊò‰.ê–c©‡Î³6¾Ðøióóóó7ðÞo£bOÍ4.µçŒŸ„&rñÜs_â¾êTàm÷ÜÇ['ó‘}u] ž|ôd“,YÀ,0Þ£ãR‡ƒ±>\Q"ò˜(.u¹š¨d²¹ jéMLKm<´|j-jÌãíÞ™| xc-5ü4´’j õŬë“'÷þ…íä5d£åI-5šÈ*šÎsÒ>[´Wüw’¥¾• òºM „eÔ ¨¶™Ì/¶À-µõázûN±PÚŽDÒnÉ—uœF€ßEä„v4õCÌTÓRÇýžqQoÀEyß³ºÌßÌÌúâp¿e\jtÏl¼EU­ïle—“{ÿ.yûI‡}/ø>-5ÁLGo÷Î~”{(O𠵋o›YÀ,ðr ŒÑR»þsË:Ä‹MG,µ¬Ÿ0bõD—²÷»j©úvå¤*w,µi©Å2c×Pü<]£ÝGóó9ô÷ÒR÷qÆc^Ý„ü?¼:½OœdšûJ & ®‘©›¬˜ë¢>™WâãNÞ¸—CËiøÈ£¥–Ú]zÌRG(KÍÃG?±Ôñ,D-Í}?b©%(u„ÍB7-µ³¥ÅšÐQJ³ƒÙÁ|À|àM}ུԞŸ~V|ÿþ˜w*qæÍÊ,i¤=æhìKT6·F¦àiŽû¸çd^ TÍ t8)+c©Ÿ¼-–À,0ÎcµÔ,µû‚fŽûµÔ1ò~xZ{ž¥–®K[Àjäò‡®cq©»šCîJ?!ÙYÛÌæÛÞXKý\$’Ëåc°¹éƒ«`ߣ£#-d KM`½½=°2rmBYs̪jɪ“yI j§üímV)ÝZ[[n#-½YÀ,0ø|ùw‡‹tׯˈ¸çxEa Û1 ¥&ŸcŸUoí´Ô¡j©5:5“Z –5ÏÚt~Æ×š˜˜˜¼¼K\je…'ÜÖÖÖÐ~0}I„IÁ4jì1% Ìþùó't8ûÁ•“yAÛ„Õ£üŸ?ë}È{ÂZ2³€Y iñq©5¾G„c<,a;DýHK­ìsDñ©–º°Â‹« ¬íµg©MK=ŒûؘÝ_»¿æsë}Zêä<ý1ðëу­FþzZ f¹µÀ(-u"¾G4Êä¹je©i©rŽÙg½V´Ô)”A(a€ø)ÁU›–:þÔp,¾i(ßTCi6×çÓì`vXfHj©y·ÁÔ¦DÏèFSg9KïY,õÜâk˜YÀ,0Ô£´ÔQ¯¢è7ÉB3KðÇRGQ§UÍ¡rH1âR‹–Z&7 ?:l-\µ›îhZjã®æ–»Reg÷Èì`>ð±}ÀkyÛ­®®jd«èM63¼`,õÌLk›ÞßÉñ.ú“>-µÆÎs€Y#Rk—3TK­±ö\hHK­úéLF~ Ó‚©õL"ú‡q´Æš˜˜˜¼½$µÔ÷3ºó°~ÂÌ^ÍÆRÏÌ´V°Yàý-ð„–ú1KíbßI›µÔ‹:úµÔ=ó/-L¶ô_‰}XÞxAãGÍÌÌÌÞÆi©{=¢Î!üÐq ÓR¿?0±˜ÓãµÔ‰8Ó^¯±Ôq©…Öù‡ª !â‡S}d„ŸfE™ÍóÓ—Úy‹éYMGn>`>`>ð>ÔRóf* îõdZêÅ2Öj³À|X`œ–úÑjJ.F^ÔfâRòC0¡ ¦§ý¡bn'ûp?\üi&à¡ýÍåq‘ö”!0>æmø³³ÙÙ|À|À| Ï’ñbÝbáyÓRÃïîî~ÊfW?}"È´_ŸœµÊ³üŸ}6óI~úêâ|´ C…š\É××ן?v¡¯ º¬Œmf³À‹-0NK-Êi?æcN ,¾—‚¢£ÕݱjBTQ­q©ÓúwĨÄâR;+ɆÅú0;˜˜˜¼“x-µøñZêÉ_ÕwwwÛ_dûÕnßÜÞ2¯‘5;-×o‚Oÿúõkkk§T*E¸y %¯ØVs9”ÜÉx&,¯ÈZ0D¼&àIµZ5÷äwÇRš†Z`œ–:VN»4bVTܧ¥vϬÄd…¥ÖùŒ?Fµ$qØ`>`>ðÆ>긪Ûܲ‚rœäq§ŽƒáqÅ×jWž{îK××ÂZ­æcó –“ÔQɼFã¦^GòQ,Q€Lýb­@³À²Y`Œ–ÚÇ÷p8ÁRkoˆ–Úã䈬~`©o'”Ó¦¥žCÎÆ}ɳ¶™ÌÌ–ÁF—Î(âü4˜]5Ð 5Tk‘Ü{ÄOËBiC6Z˜ÔR©ONN4ç¤}¶¾…k’W‡Þ£Ñl’œžžª^6üg×;u ŒÑR»11…ZÊ@GÇ<´ƒZê¤êÚk©Ý¦Ú=馥6ݤqóæææsà£ÆKgÄR£{fã5ªh}—+»œÜûw<ÈÛO:ì{ñ÷i©lŒßî¥ý(wòê¨1ÉÊÊ 'ŽŽ¦Ž0¬@³ÀRY`œ–Z!p<–<†¥aO#~x‡ª®uæ¢c©O?b©MKm|°qáæææïéI-µ’EìÞ@KÝÇAˆ1ÎÏχ&ãçòy&ú\s&YH2/*êdâq¸|©`‘]¬Y॥¥V(ë²Zj‡‘a©“q©=fŽK“̱–:©N³¸ÔsÀͼ±f_0¨ÙÜ|À|`®| OKlÛŒXj}G{~úÉWö÷ïß™;¨íx³rðõëWÍ5¦…Da#úÉ€Ë÷qÏɼåry_bóQ>SùóÉVY³€Y`ŒFi©õ»=Áa?:îÓR÷Ïôp;ÒRë°“i©—A›h×h\óóEñ7ÖR?ˆär9ôÍl.hô*rg´ZÈ–šÀ ÄÅ|#×&”5Ç›››Éª“yù©T*³/ Ôâç8>·©–Þ,`H>›¾”8ÓQ–Ú…ýP–:Þ?"lÞ‡åI8W†µGÙ³ƒÙÁ|À|àCúÀk©õeø¬ØÏ¶CûÁ$ÂÛÛ_IÁ´ N2rÿüù:œý JîË 'q¯¢6Phx½žÒRÇë´¼Jƾ{¬¥~Ð[»‰}ZjÁäŠÇ-.µN±Øfóóó÷ó÷ÒRüzÔb%˜æÖ£´Ô>‚G„ôq?7Ãmëê‰i©e>£i©÷µXæææïí便~K=·¸Áf0 µÀ˜¸Ô7{ÖyàXèÖ(âG"¥†¡¥¾× ñšUÆ×bEÑZ;mlÁ|À|à#ùÀ»h©¥6(føÀ—:B¼qÜØhÀrߪãÊR+NŽ÷º¹i©£ ÓˆëG–ÙÁì`>`>0G>0ÿZêŒ<ìÒÌÒãµÔ"ïˆã~Ú­:®¼µ`뇸ԴÔq©#fÚ´ÔÆI;W0nÞÆgÌÌÞ×LKý!]”Yà}-0FK íÖmqq?„zV–ZÕ²7-µqNsÄ9n1sÌÌ&ôÓR¿/ò°ÚÍÒc´Ô¬Œí9é$?-Ǧ¥6žé}y&³¿Ùß|À|àÅ>0ÿZjVlÙÝÝý”Í®~úDi¿>9 ‰gù?ûlæ“üôÕÿÄù>¤B=Ãõ²v ¹|‚d9ZÚ‡D9vQf7³À-5,5¨ZŸDëóԺêø0-µ†Ê3-µ˜Î4Ä›Üì`v00˜/x-õä¯ö»»»í/²ýj·‰ͼF–BÔì´¼M|éN‡àÓ¿~ýÚÚÚ)•Jn¨ ^9øÿ·÷þ0q,]´/ç<iΑ 3Á † ¤@f‚`é8ƒÌ„á gâ 2ÈLˆ3®d2béAp%Ç™ ®äy’ƒ÷Ûµ«kjþ5ƒ™Vßi=ÕÕÕ»wW­Z½j×tµJ¼‘|½+¡(‡ØïÞ-õ^=å”dv ”k©Gƒø£SÜ-µâRK3bìžS|Ý‹ž©|àÙøÀ ´Ô½3ÁÛÛ[Ë++ïß¿÷>˜5Y>þqs‚ÉáoÐ6˵h»µ—÷‘ÐüëׯÝV^üôéÓÆÆº@’, ÜÇÝ´ÔVæè诂¦N±>|%Ei©¥!¶vÞœdÐñeU‡ðþëYˆ–ÜÁ¥¥nÃe}÷ññIâž[òåL3?§Ø|íå·‘m-çú/°×¬^­NÝLè\Y@覥6Ëüú5ZÐÔ)Ö‡––Zü«xhù€|@>ð„} ÏZjøi6úÏ\µì+¿äûJPdŒuÄ(Ô<×R©÷öö  ±ôYKî™ÎÑ5ÐÞu;»œïS—òN“[úù-õîîn7ð»ììüv–úû÷ïœ>5%Šú¾pJçË·i©MLÝXG<éªnnÄ¥¶P{16ˆ“ÃÌÅ¿#¾6v¥HÂæ4³-Xž<Ò)>b1Ó3}žÞG½ÏØ¥¥náŒK ÈÜÜS;f覇&sujêòò2Eš#y!íçnmmmnn ɲÀý-P—ÚP.Ú"îGXóÅ;_Qq©ÍÒ/ÞA¿˜Æg²›4èòùÀ}`PZêÄOßÚyoll Ç8::rNŠÄêꪟÕQí?„è{¤ÁÓ¤[àr˹¢¨o}Ê ôn’¸Ô†Ð~Xtêø‡…_Š7Ú°u…_êŠM,u[;Køjo ¡êg8ÇC¥eù€|@> èôYKÝ{¯ì9™-x¶‰‰‰ÙÙiôÖÛÛÛþS KM`TÑ€o¤Û„²&½°°_ºå\ÊL±ùîZCå—d ”Ä¥¶œa†¢‡ý(VR,Þ耖c\ji©1†8§rN²xEõÍDßLä½ú@ŸµÔÞ‹úLÄ·™™´µZíââ*L£Æ.)eö·oß ÃÙ§H )˹”ÏUz¬²É²@¹ʵÔÐÔ)îGXI1Äúœ´´Ôâ×õmA>  x‚>0-µXê­t›/ÓåZêðE¯ˆûCTœ´´Ôâ“úÃ'Éβ³|@>ðà>0üZê— JtײÀÓµ@¹–ÚçyøŠÅBŠ´Ô!H ¼HKýtQ‹j. ­J´Ô6†÷zGEuŽmn¢Å¨NZjÃÌÙꉮ ‰Zjا¥¥VLÅE‘Èä÷i©‡”¨b²ÀÓµ@‰–:àæÀSgq?¬/ðãIKÝÄO‡ü@q©3¬­¸ÔÒ^+æ®|@> ~-5+¶,//ONLLONd:­OÎZåüÇ~b|Ò~ZM?q¼Ž'²aõ:kÇpVÊðãÇÕåeâ^Sê¶þùÓÅ7ª¹,Ðg ”h©ƒÒ,ðÔYÜNq³Gü+ºd,µÅ×#O`©‹Œ1ĹFdà\… ²z*-þL> ¼ˆ–º÷¼»ôζ«Zíìâ‚yiMj^#¾ôÏŸŸ¾ººZ\|¿²²R|En½‚wâlÓÕ*ñFòÕ×ÖÖ¦f§î³³³©Þ+©œ²€,[ TK±Ô•'GZ=±IKmš~,uÈéÿfÛþíÆ o¡:ˆ?“ÈäÏÞ¥¥Nœñ­@d{{kyeåýû÷ä¤÷dYÄÏŸ?½o“Ãß íÓÓSÿ)DYé±Çšýú5_=‘³Ö×7(œuÏ‹Bn­›2Ȳ@G tÓRûÛY¼}ìƒèÔ¶÷XIáÍ͵ÔÎ>wˆKi©ëÒR¿îG÷(ŽS> r”–:qÆ·"’ãã“n´qÎ4SÎññqŠÍ×^~úvn«Ø À÷÷÷óª×?}ú4ÿnþÖZ)ƒ, ”X ›–Úß²âík¬¡Xh©|®€²ÏÝÈßQKm¼ÐU;C`ÕR<×'ÏUžüV> xê>Ðg-5ü4}Ÿk ½Wö•_ò}ê­ÑuŒucÂr-5zoo/r]m'´ðÖ9K½½½Í‰Ô§Z`erþZ’dûX LK]à^ã§ TK]´BNA´i©¾î¨¥v…µ´Ôái™ñÄcÉòù€|`P>Ðg-5ºg6ºA×@{·íìr¾OÝ9H·Û|Á-õîîn7ðx«&„³Ôˆ³!ÂkµÚõu ]5Û}À„Εd-µAà` ‹Km “,W ÛѶ´ÔâàW>  £ JKÝ®uî†Bæææ`Ž;þš3Í-ªSS———é iŽäyòs‘V£]K ¶þ(sm²€,ðÇ讥«$ÒºŠÚXê0ÏÐã{´k©ƒJ$”ãëµÇ¥––ÚÆ$6FQŒÙA>  Î¥¥Nüô­}öÆÆ1ïŽŽŽ¼ß%±ººêgµh©ó¢ žÙâÁAð4éÍÍÍ,xÞ>Ðg-õ]HµZ=q£gg§Ñ[#}öBJXjƒ»ð\›PÖ¤òKçç£ß\hzÚÊßß?¸k%•_Ú߯Ôw´Æ™N,µqÕ‘¥Žs ¥¥ß쌉ì ;ÈäOÎú¬¥ö~×g"ö¸ÍÌÌ ý@ë|qq• ¦Qc—”€2ûÛ·oÐáìS$”??6ù(ŸàÖ`÷2Av5V6Yàe[ DK ?ýË´ÔŽ\ÝáS;Æ¥&—´ÔAüÖóæ·ô|õ|åOÚ¥¥nǸ/èîegeÒ¸ÔFM‡ÅÈMQíXÙ"åy²%.u8$-µtÒaì%½¸AeÙA>0”>0(-õXêg…5t3²À °@I\ê@RµÛÁc}ʼnq¢Å¥ö¸ ¸¥IKíãqÕ²ƒ|@> Vˆ–Z,õ €UºÅ—k’¸Ô† a©ó!°Ô¡Œ‘òL{0t<^ ovˆøQð®ZS\jÅúw+ȆÁ†_Kýr‰î\xš(×R[¬¨ê‚‡âŽFÜŽG–:w>Bq©¥³O/Ȇ֤¥~šˆEµ–†ÚåZjWQ{¿Ðà§ãZåv<²Ô’––ZZjûPÁâádù€|`h}@Zê¡&ªœ,ð4-P¦¥Îc}Œ¸NÚñRH§¸Ô -µsصԾ6L¡‰qø†Uc'nih¹%ó@éÔ}‰aÙAvÜÆ_KMÐèåååɉ‰éÉI‚L§õÉY«|‚ÿØOŒOÚO«é'Ž·@òøî—µc8+Ï@hj‚R¿›ó5e´É²À},ÐMKme±>œ{ÎøìNq© ],›'0ZjWŠ8ˆ;iGÄecqºâ¶åòù@_}` ZêÞ{ë?~,½³íªV;»¸`^#K!úéÔ¼F|éŸ? >MHéÅÅ÷,'qsÛ°˶éj•x#ùê‰ß¿????;;;8:ÚÛÛ;>>î½zÊ) Èí覥&gŠõ8ip|™ctêæ¸Ô1YZµÔÅ í¥Dl^Ìg,®Q> ÈúìƒÒR'ÎøV8²½½µ¼²òþý{ÇÐ,‹È²,7'˜þmŸžžh»µ`ï¸Ù€æ_¿~ÍWOÜÚÚbEF ýú5éýý½[k¥ ²€,Pb-µE¤gN:D숈ÙXêÖ¸Ôq­rÿ"mm@`©‹y‹^ŠëêºqҭºÉ?åògãƒÒR7ƒá2pr||’¸ç–|9ÓÌO°Ë)6_{ùIËÈÏÍáõ›7oÎÏ/…–dYà>(ÑRç,µchÛÂz.´ÔqÎbÈ€¿3¥ZDÒÒR‹‹ê3GŠ ì£aÙAv>Ðg-5ü4}¡k ½Ûö•_ò}êÎÑut[!œšçZj 5² ?1qÒ©œØsç0zcccss“#H±WWWã¢n÷:WxÙ(ÓRw‹õÑ).u¡÷ˆ+Â4âR3}Þ¢´Ôiާxߨþ?Þ‹ûѽˆÇ•<è³–Ý3ãZ×@;ðpv9ß'@òN“[PJ‹–zww·øœBÓÙ9KÍu—–lÏÏÍͽy3Ó2sñeC#ݽ,ð'(ÑRÇ1o\ÑWI4~Ú¿˜Ayåq©ã²Šöþ6Ç¥Îg5JK-ŽV\©|@> ¸ JKÝ—tÚÀÜÃÃÃŽr¦¹%Cujêò²¡ß Í‘Oõ”¯>3”–:ñÓ·‚TļóØvôÁ$ÐføY-Zê¼(¢‚°}ƒàiÒH;ò ù¹æ#è‡çÜÜ\_[[»µVÊ È%(ÑRû«IçvéFl‹Âĸzbȱ¸ŸÐAKm¹í$Ï×2/ÒJÖó1VÅ{Éäòù@ï>Ðg-õ]HµZ=ÛÄÄÄìì4zk¢sx!%,5AÖ××ßȵA̤òKçç"üIS>9‘UÏÌÌܵ’Ê/ ÈíïWj‡¥nLq³£+2Öñ•î—ÚQ¸ƒî—:ÓRÛŠKìgßÄ{Éòù€|`P>Ðg-µ÷»>±Ç Œ‹ö£V«]\\å‚iÔØ%%”¿}ûÎ>EIùósÁߨ>(?älBÞ=ÖPÙdY ·À-Zêf–:ÓsHø'Zê¸ÜK7n@üJïüŠl%[Éäò?öAi©Û1®‰, < ´è/Z´ÔQE‰i×kØâRÇ™‹ÒR+¦‡8xù€|@>0Ô>0(-õXêgƒ3t#²À ±@™–ºˆõáˆÚc}¤´¯ç"-µ™A\ÑsE>@“ eù€| Ï>0-µXê­t›/Óeq©›b}ØìâÍü‹íêÆjÛ¤Å;OZjé¤ÓC>  ½ ¿–úe‚ݵ,ðt-P¦¥.”Ó>Ã0©¨=tüÄ¥––Z|¶8iù€|@>0x–úé¢Õ\Z ”i©¥vbÚtgžöÕZŒ¤®X¿YêðKT†Ä!M?‰ŠK]ûšã?¨9þº®|O> °þ¬ˆîÜ¢}ôžìñ:li©϶*Y¸J´Ô)¾Gø>–±Ôh[Û“i©‹5bšâRx<E—zðÜL1*O¦g!¼h~-5+¶,//ONLLON::­OÎÊáüÇ~b|Ò~ZM?q¼U‡#@yV'óä$Q¨ù¿|ù2¶ýýýÃU@xê(ÑR·Ä÷h0Ö©ì=k©3}‰ÍsLL€øqfòù€|@>Ј–ºwÄðãÇ@0ÛU­vvqÁ¼F–BôÓ©yøÒ?|úêêjqñýÊÊŠÿTÄh\Ç{î/ûû+kk,<~uU›››÷…Y‘‘¥d@ÛlÇÇÇÀëÞ«§œ²€,Ðnò¸Ôþ¬ÀÀ´½¹õ‘ÑÄR‡hñeŽ+ÅÔÿÎÖ1ošÛ¨¸ÔŠq¡)òù€|`€>0(-µsƽlÛÛ[Ë++ïß¿÷>˜eY>þqs”aÆb@Û§§§Ún-Û)°Ï,£è}9eºþdoookkk,l¬[¼î¥bÊ# ÈÝ,Pª¥.ôÓa9Äâ%þ-µMt”–:<ƒ¡ŽÕJýúÏÉ&²¹|@>Ðg”–º —!“ãã“Ä=·äkQ{C0§Ø|íå·óÖ———³ÓÓ”‰°djjÊ g¥FŽ *ɲÀ},Ð[\jø‘>iÑða'-uhë9Km 6ëÄHK-íæ‹Ön“ o¬½²ªƒì pè³–~šÎÑ5ÐÞm;[œïSwŽ®ò¸cïNÍs-5¾Ùs¶Ï¬l™lɹH6·¶ÈÌ%rtþëº6Y@øs ”Æ¥³#|¨Øþˆ,u{\êønž¶U74ÓÅš1(E6oŸg-ž¦Ï< S6—Íåò—é}ÖR£{f£st ´wÚÎ.çûÔ™ƒ¼Ó¤Ã–¾EK½»»Û |Çî»8Ÿa¾R¿yó†càòø:d%„—6Y@øs ܦ¥±†|äkq?ñ…ÚµÔ I@Þ´Ô¿b4>[¦Ës†¸ñgòù€|@>Д–º÷}sss‡‡‡ûö·d¨NMåú Òñ<¤‰E¨ÚT§ªçççž&‘D (t¦,ð²-P¦¥_Æœt¶-°ÔI©u§¸ÔqÑçBºqâŠ^&W¤ç®ç.ôÙ¥¥Nüô­Àccccgg‡ ¡ó­“X]]õ³J"gƒ˜ÙI;†6Çæ&iÄ!$ˆ”‡f:]šé‰P×l$Ü·ÖJdY ÄeZêN±>cì»j©©„s§7Ö7?-«|@> ôÕú¬¥¾+©V«„à`›˜˜˜Fo½½½]Ð[íscñY__|#×&”5iç¤WV–)aff*Å´æ ™Ù°‘Í£‹h“d?¶@™–:À_×{X²)î‡S Y\ê4QZj #ΩϜ“lîì•ì ;Èzô>k©½“¾Óê‰0Êh?jµá¤sÁ4jì’.|üíÛ7èpö)ˆ+¹ã>Ä´öVŸ…­[t‘?Æ:Qx(×R‡hÓÅÚ,ÙÜBÛMq©-ÊžCoSJKK]ØBq÷ ÙAvȆƥ¥N÷¢ ݲ,ðì-P¢¥“¥*êÈRÇ•É拉ŽÚ/i©Å#Š¿—Èä÷Ai©ïÄR?{ü¡”ž™J´Ô„¨ Qïšæú·2۽ƥTwƒ§”–ºóÙã÷‚¡á„TñôòùÀPùÀ@´Ôb©Ÿ„ÒíȹJ´Ô°Ôø=²ÔQÊYê;h©ÃùA婸Ôõr¤·¶1™y…l";Èä}÷á×R ¬È²ÀÓ²@‰–:°Ô¶Yž/¦ÎnÒR»º»–:hGŒ¡Q\jH(m±ì ÈçÒR?-¤¢ÚÊOÂeZêÑѸ –úIURxZ(‹KýëW\ Ô0tÔn„oõ1¨ž´Ôâ›ûGVt}ßÈʆ_KÍŠ-,¿2911=9Ié´>yŠ-MðéIûi5ýÄã-„<aZäÒ»wdžœœÈóc”ñ<ÚdYàž(‹K Ký+ðÔ…^ÃØéÀGÛ¿½Ç¥––:<$醃ßÈ}×ŒÊæÒëËÚ}` ZêÞ;ì?~‚Ù®jµ³‹ æ5²¢ŸNÍC˜i‹3}uuµ¸ø>E•öàùæñ¾ìﯬ­]àúª677¿¶¶æyXP´VEî½zÊ) Èí(Kí,u#ö]¶êi©‹8|Àª,ÏeŠ{P9â±äòù€| Ä¥¥îÞÞÞZ^Yñ é§YæðóçÏÞ…G½fÑŸƒ¶OOO ´ÝÚË;“ñùà€õ½¿§ÌËud¾~ýêy´É²À=-P¢¥6”‹ö£ˆûÖ|ñFÚ¶?ÒR·Åäw"þL> ÈúìƒÒR÷Οt[Ѱ`Âbg|||œbóµ—ßÎ[_^^ÎNOçС=Ï=…N—^¦Ê´ÔðÓa†¢Çýðy‡ñ£5†9ˆüJ¬ÿÝãMGaH½þ·£õ"_ˆøá3C¾,ï¤k©á§ÙèûL ]h—-Î÷ ˆ ëëˆK¨y(ÌÊA ¤ÞÛÛ‹ÝtÛ >»è§lnmå[ò¼L0¤»–îo2-µ\›¡ã~Ä•‹kJK%Ä-õ™[’Í}x+;Èò{ú@ŸµÔèžÙà\í©³Ëù>uê æ4é°¥§oÑRïîîvßvbFAS Ì7Bê7oÞäeöÎßs¨Yà[ \K Mí¨ÚZïl{Mÿ$.µ´Ôƒ‹ÃÚV}ðh5²ƒì xÑ>0(-uï|ðÜÜÜááaGðÑ¢¥ÎóT§¦Ðu¤#¤9â’&~5¢êVŒþŒ1ŽnMè£ʵÔàiPu¡¨6õYTwHK-~Z\©|@> x¢>0(-uâ§oíå766owttH€:‰ÕÕU?«EKbf#úžchÓxln’FBbff¦ýÒÒRßú8”AèÅåZêŠóÓ®¨µ¯Íq={Éí/ÓRÃni©Å{½hÞKü7öWß@žŠôYKÝKÜÄ7W«a›˜˜˜Fo½½½íJXjƒ¬¯¯¾‘YÊš´sÒ++Ë”033•bZç×ê;¿ë](¿,ð¢,P®¥ö±kd©cˆê0Ñà´½Ù¶z¢­­h鈭탚íÆê‰ŠK &Në‰rZ6²T0(-u¸/sèÆegl-uCãÕn†H¯).µ…×+4!!Ö5XêpJˆQ ¸Ôψãß,ÞZ> x¢>0(-õXêgŒù€|@>pˆ–Z,õ³Rº)Y rÎÍßÞ3mt`=gq?K--uàÝ¥­ï.Èž  ¿–Z0Ež–JµÔÅŒÃ<îGc­Di©Å Ý…z*qTOiÜåÏÞ¤¥~ZHEµ•ž„JãR,5‘±BÜÛK15i©cJQKKmO^¶Ïm•dù€|`è|@Zê'PTIYàiY '-5|t±†bg-u¸gÅ¥Vü Å¥–ÈäOÆ_KÍŠ-¬w8911=9Ié´>yŠ-MðéIûi5ý41>ÞAÈæE.½{GæÉɉ”ß.-÷𣄲®×ož|Qmea³@I\jÿ2ùiÕÆRQ¨—Zü«ô£òù€|àÉúÀ@´Ô½#€?~‚Ù®jµ³‹ æ5²¢ŸNÍC˜i‹3}uuµ¸ø~eeÅ £™¦Í'D}Ùß_Y[» ÀõUmnn~mmÍ~ùdq¯/Xfy9Ò{%•Sr ”h©íËXÈjq©maòø.ûKË{šÇ¥öµÊãvZ]q©wYkéÉäòaôAi©3îeÛÞÞZ^Yyÿþ½ch–EüüùsÄÍÞ×hûôôÔÿj_Ñ{ñÏ€fïï)ÓcùQ ŦòS!½TOydY Ýeq©mÄ[¨¨¥¶·8}+kÑRû[ÙMKç9*.µ´ÅŠ‘"ÈîƒÒR7ƒá2Lr||’¸ç–|iÅ5?~||œbóµ—ßÎ[_^^ÎNO·”ÉÁ鶃ÂL²€,p' ”i©­Ý7j–:„ѳñmã[Y€ÏqõÄpÉîZê¤ ·çkÁò^;͵—äòù€|à±} ÏZjøi6ú>SB\µ³Åù>õßè:ÆÆÆ:vçÔ<få ƒRïííyNç¤ó­…·æ\$›[[-Ù8¸ÕvðN`B™eY DKm 7õQ°Ôu‡Å±¶Å-GÎö³AïbõDÿ3X:°Ô1_ –·_Ÿ¬&Oœ“ž|@> xZ>Ðg-5ºg6zC×@;øpv9ß'PbN“ÛPr“–zww·ø¶3šša¾R¿yó&• `’bËAa#Y@ø ”Æ¥6œË¬DÓRÛfë&ººÃÛ¢žµÔÎI×뿌‡šk+Jœt¡=7oí¾üAþ ôÉœêø½4~Aýƒî´‡SÚµÎÝNš››;<<ìøkI «SSH8ÒY¤9â’f2"l4¢ê” "L‚ÜÜÜÌöpÊ" È,P—Ú‚QÛÔÄëÃ0O@±-ª˜F#²Ôޏ[µÔa|ì ÷ˆð¢ž£3CÞoµø^‹RùaCâ§o-vcccggçèè(t¡uĹó³Jjbf(;†6Çæ&iÄ!$ö÷÷gffÒ¥œ›ïàð0?xkÅ”AºY DKm§¸Šºx !‡…\ì,ƒÉ=i©Z{,µ´ÔÒ‹?1Nï=;;;=9ùzx uô+ð@¢õ¤ËICGQ>ÞëjÕd‘}çìÛç6è;Òƒ<߇ò•cÝЃ¾}ÖRß…T«Õƒ°7zvvø»½½í…”°ÔDðX__|#³.“vúyee™ff¦RLkÒòœžr!×y·Ç´¾k•_xá覥ŽÇa©säPè5¼-jÒRMH@7k©Mýë—ôߺq/Š#±áÊáhUÏ4Ñcññtÿà 4Øë“ÓSº.ÒèAÛ†¹?|$ívƒë¥'#¸,Üͽ-ö¬Õ®Sù"QUÒ zÞ2J~;?ÏÞ'òsœ+¿}ûöÝÒR˜Õd9ÏÏÏÏ}rjžÊês|\¸mðsݹ:»/˜.îëcË}åt¬§×A~.; ­ôYKíPÃg"ö¸A3Øq£¯rÁ4jì’hC¾}ûÎ>Eq%w܇˜Ö”`GŠøÖžî±bÊ& È-ÐMK{XXê8«Ð¨é¤×è ¥¶_ `€).õ©ý B‘ ‘–úA¹qiË]µÛ³eܹ0?ÿýûw{‚ ŠèíHÃ.ÓB6³'íåp.x÷ðë!}›“L-寯o0aèèË—ë?¼Ì”‡Ï¾œõõädyqqooǶk××_¿r– ùíãîæ&½5Xùä$D¨í^ òön »²kµsÒé}ÞßÛOŸžž,,.£§ûJåt¬gKùýŒT¾Ú;ù€Ñ;ƒÐR'Œ+D" ÈÏÏÝ´Ô‰¥Ný»IûâR§6 @úWݹ7i©Åû>-þ²eÜ —ìÓ x޼>wž4`zyy ÿfõ{ä\°o¥2ÆçW°uhAš¸[‡Ú×µÙ`²á¼SV^àWJ˜1Dó~\_§ÀÅÅ…ëëšûUânýæá-[ód0IDAT†Êx$¬’úÌX…;èG]/nû O¼5´7£N™Ÿ›K¼¸Õª(§c=½í÷«ã²É0øoî@´Ôwb©ŸàÐÉÏÛÝ´Ôv<õ‘bä¥8xAÈÑ).uЄÔa©£–3!⇴Ô£ÁMc;q3âÿÀn-ãΛúÁq΢§ÎŽ òçÏÆ7‹oU¯³ç³kGû#eDìÁ^âX1 ÆÚŸ)ù Ý‚ek×ñ\.Pð‘v1OïmoßÔ묵¶º¼|~n zI}N¶ûLÒ‹›jüô„}â­¹@ö\@Üö½¸x‹S9ëùvϪw¹Ÿ>0-µXêç¨tw/ÜeZê"ÖGÈ“é:Š8x-ZêMÄ ŒW,@ˆácÚy»´õ´øÚ—ù¼ZƬjVô‹¦…J61œ]ÀÍqþHßd‚®™ 3”ðsãð¾330Ö{û»~.ùQq\‚qÃÿýÜ ®ŠsJƒnå_¿~}¿²²¹é‹8TºÕ§cJ´ÔÜ (› ´}šî+•Ó±ž^‡’û}™¾$› Ïs~-õ G'º}YàÉY DKb}„øÆÇ…NÒÒükX"±Ôá#ZŒ(_Ì\4–Úç0Fp“çæÇåjÖÏ%ÿñÑÇ/ÏÏ=–óâé~Sz}sy7õdÚ¾ÿʾ[}:úL‰–z~aá ‘¼‚(üÓ§Ít_©œŽõìÃsyìç®òûÉ÷ùZá5Ša[|5~•zœÎ\,õãØU¥ÊCa-µE¤u ßÇŒ‹-O˜‰Ø¦¥¶ˆ”Ž|ôßI‡¿ÃQK·¬i~à¿áá-T=‹ñ»ùhZ&#êÎý6猑m0ÍžtÒROU«Dü8ùþ}ݶúüÚÊÚuíšWôµ(7xÛ¶Œf¶÷ânøÝü&Þzmmåââ‚û¢ì™ªÑøÊTRO½×í¬÷kHÚØÐ5uþ^Z¼ ÒCKKý(fU¡²ÀpX DK³ÔŽ¡ llè¹-.µë;ƒ€¿ªÕ©‹‹³£ã˨©ÌLMl}ÚÚ\_!d=4ÌÜ/¤™j³v6 w¥e‡çä(ÑKè™>§gª{yþ<:>Îô¦ LLTC/û ¥ƒúÒôÈÆûÆz(ì©Y±…áô÷“T\|,bLë ó1*Ö¸ÂÚã̸`$í?[º%1§‰”ßÛÙ9·€ú¿Þy~Ö!6 "1Ÿ']¶°ù ¡KËOÄŸ¾œ^Œ®ð‚¦…C9<>æ ôÚÚ:Ñ0ª »¨à ëDµº¿¿ÇÛm·öi‡7ñüòÆý4M•ÊüÄ™þNòé ÷|À(¬^ÀãC ÎfH8qŠÄ)¶k”åóòyùÀ0øÀ@´Ô½¢s²T8ÛU­vvqä-ÂÉ[W›âI_]]-.2‡bÅKöÉùæß—¿ìﯬ­ñ¹éêªF`Ÿðm zzPõÔÔT*¤÷J*§, ä(ÑRGŒQ¬áb3 ö/f´Ôñ•Ž ê¿’A ê{‹€ -µ·x¬ýUù}Öe–<ÓvòðÔM~¢gñb} |J€–θG²½½…ì}˜¶AË"SŸ?.:Ù&ä Ú.‚lF¡eSþø|p@Oïï)Óõ'èk+B™Ì,Fê챂Ê& È­(ÑRGœ¯˜˜µBµÔÎG‡ýß&œ)›§è02,ÅØÞBuö°>@¼Ž86Õœ3„õèò1Ùaà>àÌ£ÕŸ|T-uœ“Ô!²P7Ú¸¥†è7Ò¬ÇöòÛyk&{ÌNO·Tˆ¦NöðX”E(³@‰–:t€ÆF[‹Òa5ñ ÿ覥¶¬E\j‹BÝ‚Ç –Ú‚ˆÙû"8¯Zü½|@> è“ô9.5ü´k Ù'®ÚÙâ|ŸºkbÀwS6SóP˜•399é’èØM·u÷qTqœsl†¡|›%¤BšdYàÏ,P—: ½Ïç@8ª; c¬¼48.@7,u„ßaÚaPQc­¸Ô6<á¿ó4ªƒeÙA>ð} ÏZjæ²Ñ²'í¶Ãù>uä ffðwì×[´ÔÄ *›V˜ÑÔó~Ú×|õ -5¼õæúº´Ô†¢t–,,Ð]KíMlKCr8aÝ—Ú ´v=h©m¥D<í {--ã‹Õ2F_Ñ7 gdÙA>ÐG”–º…3.Á"„{gEÕŽJ"gW§¦ÀÇé,Òñ?I///CQ»~:ß@äÌp<—–ZØP¸Ÿºk©ò ¡²Ïå(`3 –Ô#KŽ…1€Ñ¹–º2õjö‹âR‹m‹,žXß+äò>ûÀ ´Ô‰Ÿ¾µãÞØØØÙÙ!(‡÷Ä$VWWý¬µ7ˆ™è{Ž¡MãV‰BB"¬$ÕˆH®9¡|Toe®MþØÝ´Ôœ³77ÆúptÃx¦ÕÃ[bWêvn:²ÔÎlÇý¯8ÃZZê80/üŒ¾WÈòù@?} ÏZê»vÒÕj•uXÙ:ÏÎNƒ}‰Vë…”°Ô!vX™õ‡H;'ÍbR”033e ì Ãæ ™AíVþô4øÀ}×J*¿, ä(ÓR7Í-tÚñ¯Šh”k©Òví{Å¥G+ž^>  ôYKíýîVO„QFûÖ™pÒ¹`5v ŽA™ýíÛ7èpö)ˆ‡+¹ãÞ’VhûëׯVþ••ÿ¨¡N¼d—`²¸ÔŽ„ãW¦ÀL'ˆ õhb©ÃG´h®&-uO#¥&‹Å¥ŽütÖûÉIèZâÀäòù€|”–Zê^®Ò=¾X ”Å¥Îã{83Ý`© aߦ¥¶ÜpdûŇ}u ª—Z±>lhÆÒÊòùÀ@|`PZê;±Ô/—èÆe'j’¸ÔY|×I,µk¦ÃD°rXUÑÒmZêÆJütˆiP5غàª#B—ŽVzbù€|@> è« DK-–ú‰B%U[èÅ%Zê,¾GˆN¥ÔE¼ÛµÔaš¢ýÿxÚxêbž¢ýÕ¾fÕ@¸ q¥>’dù€|àEùÀðk©{é•G†ÇeZêB íy2]uÔnܦ¥k%Žü )l|‡Ÿ¶}ZUQºFéåòù€| Ï> -õð ÕDx6(ÕRúé €.–Gôt/ZjÂ8ÿ9b*jÛGlneµ`yñC/Š/®o2òùÀ}@Zêgbt#²ÀðX DKÅ¥¶}†{ÔRÿBiÍ”ÓaïLŒ´Ô}æcÂC0Ë뺲ƒ|@> H=QÇï¥%QŸïßs÷®¥fñpg™››%n4+)@ú®îñ§{Ùòœ¨ž Û—/_z9WydYÀ-P—:°Ñ!WXÑg(F–º‡¸Ô£FS£áœq[71žŸ¸jñ²áHÇç·Êäòù@ß|` ZêÞ‘Çòòkžžž7šµ‰Qíë Þië=ÔtÊ ŒærD&a;<8ðåµÉ²@/¸MK] h:`[o<ê5¼-ºEKíz´÷ëW€ð{X3Fq©Å‡;qf²ƒ|@>Ðg”–º—^Ùó\_×Ræ±±1èí­­-?½´´{Ížtʶ¹ùqrrrzz:±Ë‰q'Af?Nz¹8Ý3°ž" \dc½F 0”–ºwÀÁòã¬nˆ#Í~:Ø„ {Í~»ÀÙŸ>}b­ñ‹‹‹Ó““³³3Ï霋/-½ã¬÷ïßó'l÷Òò§¯­­9LgiErúÚŠd~óæŸ>55uy}Ý{•Sxá(ÓRïcs¬â=µ±mi\jg©mÅDÃÓa"ZKKÝg>ÆÇCìt]ÙA>  DKípØ“väá+¿äû„HO___#¤~÷îbŒôÓññÑÊÊ ²?*޳t9ÒÃŽ½~ ÷Ìô¶(7>¬¬‚ý EÍÏ/ äTlâ³ÅIP±§/_üü…ã'ݾ,Ðb2-µ#±À)[2‹ûáÚ ŽLK$FúÚ`vX¦Rù»Xꊫ¨Ã>üç9*.µæû‹§—Èäƒò>k©»Ÿ¤½'öÙŠù>ï¡Á°ÈÏðÊè1@ØþëÏŸðY òÖj×H5Z:xîñôôôõÄÐ<ýÄ)” ²¯V«pÕ~c@äA@ío©´ÔØA\‘øBù€|@>0~-uÞ…£ÄO#íðƒãã£N†±,qŠÔ11Qm—ˆ ÆzoPž~åv@ö¶%ôì Ðö÷ïß=ØÝ#˜P6Y@(ÑR‡¡ª³ÔAE]ÄúT³ñ™‡á¸åtMG@Îüé,u6ŸÑ8mÅ¥–~ºXØ?7(~Nוï½p~-5sQe8f¿··—$Î ‹üÉñ½½}ô!Žc˜}ì¦þñãLJü s`°×°Ý®a[\Xð8ççç)'!óœÉ^^^&3W¤¨pÖšp’, ôh-5Q¥C¼¸VbÎO[úV-µ³Ô“ö(•_EÚ‚Ä¥ñ•äòù€| ß>Ðí{iR÷؉>R6à,MÒ‹ùù9Hå$ü€rFÎAÄö)²ÞÆÆ”3á>8‘´×*Ý 1øÀÜ›?r8¨·''&Ù)NöçÏŸ ‚h›)Œ‹‹‹^§~¤T±²Àó³@‰–:éˆoeK¬^´ÔMMUÏNÏNNsm|ffž(?[[»×——ó ¦‰®ç×`K×SZ6‘ÈäòÇó¢P½~ý8MÚók-ìß,ÏVÖãL¾ïòÁ²½¯öòàWW²€,ð¨Ø9º<8«®Œ¥¶‹W¬»¾¾Îˆ·r£þ9Bì‰j•ïNÕׯßÌÏo~Ú³át-°Öá+ÓüT…H×ë„Ís uÜû¿ÒR‹›—~T>  Ð¥¥ž~T@£ÂeÁZ LK=:Y’,îG®®¾MKMؼ ¥vÕûQ‹ûѤ#yáz>ÇH[,}¹|@> è¯ JKíñò´É²À³´@Y\ê_¿b8b…–`Ý÷¦¥v–:Å¢FK ºâR›ñÀNuäò—ìÑR‹¥~–@J7% ¸ÊâRÃR ØõÆ1gÚÛâRÛâ. –ÚÙD$ÍsG[ŒQÄUûXMväò>ù@ŸãR{{–ú>ç ñȲ@,P—ÚYêFì» CuÞ—Ú§°ZjÊI,µâR‹'öì ;Èäý÷§¨¥ÃÝH¤KÈ÷±@‰–ÚZ9´Eܰîa\$±·¸ÔÔ®W (·r¤¥+í¸|@> ¸ðŒµÔtжÞ8ÿýüyzz655›–Nìt¼_¿ëÆIaUs»ÊÅÅ‹;‚DïZHž¿Ð¡ÚP+ÕßË¿O±ù¹Ôùàà ¥4Ö’L@³Þ wÄÆ0&-¦ÓrË[ºU——ÏÎÎSý{¯mº.Ø=­dÙûéÊ9l(×RÛ„Â"îGXI±‡{ÔR;/Ä¢à:±ÔÒRÛpÅWr—žXvÈäýõç­¥“ãÆjä,3qyvXdÁˆÂ¸Uò;ßz¤:Ș¥¡]oŠæ„Å9ÈÞ3·=¿ÌÈødé?¥B–—…°°ÎÛ·o)„‹&ªØ!ìää„áÚÚ7ܨ2œ.…P¦_‚¶Ô“»`ÉéÉIhcr’†iötª3¹¼¼L…ò¸9Ý zmm%$ÁFl,¹»»ÛŽÛ0¿V«U®°¡Ô-Õ¿cÝÒícC×ð˜å+Áòãã,:ÚkÈmmm®¯­yÉ[Â^]Õ€¹ –º ¡ž@ðƒƒC¯'·Ci9‘L H\Î..¨ufEw˜fÒ‰i¦ÎGé‘®ì¦é^NOO@ê·)Îòlf½‚SOõïX7ìãKèAo{}òsùsia!:·ÖA†ÐåZêŠGüpEuZ§¥LKmƒÇ@g×Y=<ùéÈRÛ ç£ŠK-~Zܼ|@> ¬Õ“(ÇÙe+7èiäѱ„‘ø`~rq98žK0ØH¼xíºF†[±`=-eŸÎMõïX7*ㆥ>‰&Ïùø™¹Y±Ô·Z~È3”k©}¬Yê–uZÌê nSv ï4ÌbÄõ¥6¹´QÆO“ˆ‘ªÃ»aˆé#;{k,;Èòù@ò§¨¥öŠÝö mäZäëkã›]õ‹P-!rs¾u4`ѰxµZ«E$Ê)ÀP° Ê Ð¤_(qÛüX„–N4-GL ¦¢…öüàZ¦0¢ú'ô¿p¿, }7-5ÇEÄ æ,öÔÙ/‘«™;¦§€Â}h]qü^|CþÒË$K®èWÏÏmÜ~§ºâ¹ý¹¹YÌÒ‘¯TÆrk5Ò¿OÉåZjAÅèÔÍë´ô¤¥6¨ýk”0,¤X%ü/ÌmlÁòg3XÎFö—ýåòâŠ*Fï–Rô¨zÚÑŽ#z?7ç>éãÁÓç§§^ i—=$4I:ϲìæ²E4 |D> œæ æÜ6"<‘Á i/Á÷~iN„c_]]¥sS” Ì~«–º©žöSçˆn³ÁAþsþ›œ ’©-›ÓÞ©>póL4¼ÄqÅ„¼Ó¹é* —çu[\\xófæè胴Ô'U>.X}ëå•aX-ÐMKí3ãÈ-*ªãcÇùzÒRGQõèX`©ÇF~™ØßÂð…¸!-X>”-Ñ[ ÙAvÈäèáSª•ßþ½4g7¼ã¾Ï—ýÞÏMœk,Ôögæ¢Ê9è¯}.`QLOM¥({PÑ>ñòü<Å  áWóå\rnäÅdsHA!üIJé°gã®|@Û /Gür¨´{ÑR§+r¢_‚½›žfI:]weyZÝ)ê`¥X0c†O®$á^7w Äé’x#ûD1Ǻ¯ççœ ¤NwZ­6,ϰ§÷QÓƒ{¦ | tÓRÇ™ˆ>üÊâ~„Ù‡†¶}"bôÒîËŽ³ZjÏäÓ|ΰ} §»râ‡^?”0ݯ|^> ”?¿Ì#Ç­¬˜$¡IÊü|p ôùv°Ñ gÄ Ë++ áù@dL°KsûrΘ‹ÕË¡2ÓƒB Åá}½ÚüúöíE!)á'ñä q9&Pttänó[ÎyñÀš[=áÎÓ4ÊŽÌ´ƒãˆ³‹â¸;pL‹Ç©Ûáá×ý½=ª„0æì¬Qx^î+‘ÙÉé*ë¶µ¹…lfŽç1==>:ê$7Ï%Yþðø˜H#=>beN ”h©›ü0‹ûs8Ðs‰–ú¯êÔ !uŽÏíµñíÍÔS]·7Ö®//™¦`oT&j¬Ó4×E4l¥£õd7„ì ;Èä÷ôˆ"dÄ‘I›ëmËÂþÍòle}qê‘:lÐê3‘÷97¿_Ê$úH÷øBŠýðau}}þ ÷ btñùóç)M… Ê;G—gõÕ±„Uxmy¸ŒEëõçžSÝÒúäÈŸ©V_¿~ÃØõÓC²óZà¤Ã r~ªÂ „¿M;‘±±Ù¡,û§70(®B×O&È^””|/Íû¼ï˜ÿO[Ïæ&ÞsƒE‰‘hÝ{–ö’OßÞÞuåɃlˆÑ;À~ÂUHß,P¢¥Îð´!b瓽ßéMKí±¨+­‡}Ò®IK°´’¨• ^§òåcòù@ ozÆZêná2$‚ÎûS†óBøÏŽL(êQEüÃiÃçW«ž´Ô|+ÖP,´Ôt¦TKýËE#>ûÞXjŸÛ˜´MÙ<ëÅ‘˜itïý]/M6Ocbùž8iùÀ3ÖRwC*ß¾}Cï!<ýüœîhH,P¦¥.p¯ñÓª¥.Z!§XKãR‡¥aSH½8·Ú¥ØöCûÐ-öÔ£’…½Gíh‡÷9wH‡ª! ¬æ {_?œuþ¦©´/_ö£ù_ö÷}-@JjûÒŒÎÑÚ+áŠGOéxõ¥¥–«“‡kA!C{ç¨= Ô š×ë\¥å\n¡Z}'7%PÕtkÛÛ[¬¤èU±”zÇõV°–dy ê ˆgýG·›oØÖ/Í⑉äæBD-ä Fv¦œs¸®sê¬Ãå4åtañ}ªT¢¥¶b O+†y‡®Úgß®¥öˆ•Ñ€§Ã>àw×\§±še,W!®Hö—Èä/Êž·–º‰¾¹ùôéÓü»w B d3þB˜êœë¥;Fe[«Õ_â.ÃÎîììpýéÉ Ù(ŠÕ‘SÈÓÓsÈàóóï(9~ÆÉamz|Nôå»Ã³+fÀ~Ÿ:ÓkXì¼,.u ¬ ?Iq©3-u䪊ji©¥#/rqöâïåò>û€i»¬ãû¨qú³ra\©€§ÏOO¦vD—Ö}g…3X0Ìùc‹Àð‡¶¢¶ØÑ3[PCHó-Ì‘™™G8ž`tnÏv~7¤¸ÙÕ;0»-O'ØÔÜÁ1ûXÃócö“Ó(j §’¡ó‰p2ÞE)å[˜@OiçÚÇ‹_ù)]Cq-F#WWW@jŒ&òzÞv}ýþ4,P®¥ö½­K¢)?’k©Ãûèi#¨]Km‡3]DýÈâðµ`ù`-qu޲ɲƒ|@> xD°^©Ë:¾-<èÃöäý™ŽÖÄ×ëû3ss~#Aò{8ÞÚÁ%ÛôÔTвíÒ…Ëós—…°‘ðšƒ,sûÐQ-çZäö#9æjµqÅ<½¸°àW‡ OW/c©³[E¯âÒdö®]±Þ´³KK@Ü¥TÕtœAq¿(6!µÜþÐQ¼¤aI:7]|aq1 TÒ¥1©§Ñ²§ÏøÖI»ÄOàaÝL¥ ÊÝ´Ôv<õQè5:îYKÍ™ÅWž°*Œ´ÔŠs"]µ|@> °<9-uï(!×RAùüü2ű²bJ×?øöùàðçÓï`£Aϼbe%qê>áHɧúq­Ÿèi`w‚•ŽSÓ¯)M}Òóôf¸:!¢Á¾í ì–Ò 1gFqšŸžž¤‰•銦ë# üŒt6 °…ÐÓ\=Í/Ì Îé‡EˆÀv®ã—>M3#)çí[æ+Îs ÷A áDP™{|nØñ^fFöþÜ•sà(ÓR±>œ{Îøl“m4Å¥N¶¹xÔRÿUšaFðÁéM`©ME½0=öqýãîÖÚ5sÌÜj¥eù€|@> xl€nEF̬2W,¤>haÿfy¶²¾Ø8ø°4ˆ­?Ú’jûÊ&@À‡½µg_ì5HÝÕç÷ßx °ãùlÑû—©n£Ëƒ³úáÊXjÃyåÁºŒ`S5Pnœ›ê‹mlŒP<Õׯß0vý´ÇÀò²ÔáËüT…qõÄBõ¸j‹¦F‡ÒRKK<‰]Ÿ5”îÁº®ì xÉ>`ýÑóÕR— ô»¬0’HÜC'Tàïîn wÿj£NéH‡ß¿d•0@ ”h©-"u„Ðð¼µÔÝ ñ:[@˜ œ) èùãKCnm=¤fTó¨ªý?¾Mx t×R£Éˆ©}ÞaÑ<{:ÎAs£éHüt(-hBêuXꀲYá%áni©Å*.¯|@> xÆZên˜àÛ·oè=„§ïƒ™t®,PbîZj;©=ÖGã[™éÈRütÆR§¸Ô¬žhˆÛ÷lw›g-^M±>äòù€| ?>¸¢ëø>*wØŸˆ=²€,0 tKíÕ1ÑsÄÀ_ÌZãRg89Î\,´Ô! xá>ð2µÔ/Öè6eAY ›–:ÖÇ¿ŒEÂÚôÓQD]¬çÒIKí¡« -õˆóÓÎUÛLFÅ¥VŒ ñÓòù€|`À>0äZjBsáxnn–ƬðGÀµ?@ i%¿[ÏÍsb.×û¹·® ²À ±@7-uŒf%n0ÖñÎq©Óê‰IKMèÓOZjCÕ]Ö¬oôÂy# ÈúãÑR÷,Xêhw§§g, Nül–ÙKkˆô^Hïºð<çô´­xÒû¹½×G9eçmnZjÖ?²of©ƒ^#RÖ¤GG*ÎR7CkÖR–ápb©—zÀÜŒôš}ÓkÆ‘¨tó>"—d‡¡ñAi©{Ç××µ”yllŒ0)˜46‹Â^³'²mn~œœœ³lDA„%F¬Nf?‘Æ~~º“j,‹Hb‚ÆÇùóêêêëׯiÀÞ묜²À ·@7-µ³ÔQ?lTÄú(ÒIKíQòq©›µÔ¥K#fè[Zê`R³Lø]Kv–ÈäÉ¥¥îp°1¤Y$Í~:Ø„ {Í~{kË~úôi||ââââôä„u‹ý sf———¬ÍYïß¿çOØî¥å%N'@µÃôÚÏŸä¬ñÏÏŸ©†b©{XÊ) äo\jgŒuï ±ÔE¬Óôq¼ëÚ0Ñòë·¤÷·]K——–Z\8rù€|@>0 >Ðg-µsÀa7NÚ;`©—ï4q<ͲÕ©ß½{‡ö#ýt||´²²ÂŸìŠã‡‡‡üIß<öúuZ–{<::ú°²rpp˜–^§¨ùùN§äTl;'-–Z0Q¸«Ê´ÔE¤;_1ã³CÚ€wq©ãr1 –ZZj{بEk›Éòù€|`@>Ðg-µsÀa÷“´wÕ¾êJ¾Ï»p@0,2Ä3¼òÁÁÛýùóWš’”xåZí}H  k»¼ž˜š§Ÿ8…’AöÕj®Ú·sÒb©ï §”_(ÓRÊéÇæ)ú‹çé^´Ô®¢§±‡öNk—+.õÐh íéHç:k¹ o§:è]x!>0üZê¼yó<´ÃŽRË›—cb¢Ú.!ŒõÞþ> <ýÊ) ì€ìmó2ÅR Ê÷·@™–:àæâ]³hzú qðzÐR/VO4™ÖÒRûð„ÿÄUËòù€| Ï>0üZjÂ} ÊpÌ~oo`í]þÂÂ"’ØÛÛGâ™kì¦oþñãLJü ÷¹°×°Ý®a[\X@ Bâüü<åœÍ™ìtîýA†J^ŽJâR§øáûXÆR“ÎãR—k©‰H]ŸO£ý($i>£8¡ £1ÅâÕ7ù€|`(| ÏZ껢 @0MÂëùù9Hå$ü€rFÎAÈö)²ÞÆÆ”3á>8‘´_.qÏÄàso~üÈÁÍ­-ÔÛ“€lל°}þü™0 ˆ¶S=¥¥¾ë#S~Y ›–ÚÔ±á±>rÆÚ¦&Þ®¥þ«:5üãÃsè€:̯z}~jŒ‘÷öÆÚõååüƒé‘ÿÚÛE"JËòù€|@>ðx>À üׯ_C§I{~­…ý›åÙÊúâ”À, ÈwµÀÎÑåÁYýpe,µ] _Áºl'®FG»aÄ›ÒÚ¢êë×oæç7?í1转… á+ÓüT…HU±/làiPucž£´ÔÒR%ž^ßjäò>ûÀÓÒRßµ_W~Y@ˆºi©­2a—°yÄFÚg(Òš˜£[\jÏ’FbJA›íÔÕ¾~Œæ¹KK-Èäƒðá×R袲€,p ”h©³¸Ôå#†üp„Ç¥niR\êŽÛ≦‰Ë•›»Ðx)Ö„bMÈäòù@ß}`ȵÔ÷é×u®, ÄݵÔÎRú-0´ÏP xºYKrhÚ®TþöeK=øépbÁU;‡2)Ýç9ﲿ|O> x±>Ðç¸Ô÷ïà}Em²€,0´hÕ_ŒlM—Áâ‰ÛЉß#ü‘ë5<.5ùø5c©æÈÛ´Ô¦q½H¶^y7n ÏZºT+]W:Nù€|@>ð¢|à)j©StŽ¡Åª˜,ðÂ-Т¥à7è¤Ãdsg© u#îGÒR˜ìˆ™ G»&Ä µá[KK-Zß"äòùÀÀ}à)j©ÅR¿p¸¦Û~ ´h©™Žj"‚‡‹3 <œî)fK¶³Ò‘¥®×ÿv~ºØÛiåi©Å‘›oH?Úwý¨lþ¢¸Xµ3%íÌ“ÓR÷ÎRÊš-ìÆ§§'WW—-dm÷üV¼|¿ñ°WWWÛ—o¼S±ÙJú{ùw*§ìNÇÇ¿ÿÞ’us¸H:È’7ËËË“ac)œn7…ÿxPp äö ûýõL×¥(¨§<ÀrZ´Ô¾öøØXåû÷û)H="nÄý¨Ÿ]Œ¥èQéøi‡Õ-ZjCÕ–®––ZÕÀ9*QuÊ^²88h)µ$ã·ø°P%ëݰqGl cÒb:-g±È†[uyyÑ–à8:âÓÓÉÉ©’ñqVh"¿•5Á;BlÓR;?í˜=Ó—„èÔŠK-ŽÖ}Eväò¾ûÀóÖR»ÜÒ7V#___¿<;,²`D áÜ*ùoöÎdÌҌЮ‰7¥Û§¿ç {_Ìœ-(J_‚,ý¯TÈòr£pÄÛ·o)„‹&ªØ!ìää„áÚÚçõÏ+ §K!”é— †-õä.XcrzrÚ˜œ¤aš=êŒA.//S±ünN÷€^[[ca Ûâââîîn‹ÑøƒðkµZ加%ªAÝRý;Ö-Ý>6t Y¾,?>ÎÂCTLÚžvS?­#-Zê››úDµ:óæMµ:…·LŒñçÄÄØÄDØ[z‚Õ¦¦É33Cò7"ã…¹Œ -µ«¨ãìE›ÃhÀÛYjÅ¥ö‰ÿÄ“Éòù€| Ï>ð¼µÔ‰û4èysóéÓ§ùwï@l`Ç/_¾$ŒBzaa8ñÍü¦ã²¶9È’EÑ=3êÎÎÙŸGZ¸í€MãBèœè…,/¯ììÄBàä6Ö×9¸µµ¹¾¶æ%o{uUæ6Xê.0ŠzÁ½žÜ¥åD2% q9»¸ Ô™ÝašI'¦™:s¥Gº°› ¤{9==©ß ã8˳™õ N=Õ¿cݰc* ½íõÉÏåÏ¥……üéÜZeB ´h©Yõ±(Žº°ÄCfË÷ ü437ðô ù“Ò:φ™S\êÀO²:ì?öÒR›+ˆ£•–Z>  Èž¢–ÚYÌnû„0÷  ;†Ôa{{›_ÁŽ9çJÚÅ9ß ò£Ïç =}FîVNOOA«¨;¶ Ö6qÛ\2˜ ûûQSå ¿K!uxYíÝÏŸçCÉ ‹×µš×–ïß~¹K]p·I«í™©'dž§]m:ö©ž”@9Î.X¹AO#Ž%ŒŒÀó“‹ËÁñ\‚ÁFâÅk×52ÜŠÕëi)ûtnªǺQ¿Sê“hòœŸ™›K}«å‡à_J;~/ÚÀ ½GüȹOzhðôù©Mb#í²‡„&cŸŸ0åøØ7Pil ‘OÀCç9ˆ9· B…Od0HÚKð½—͉pÌ ó«««tnŠ’ÙoÕRç÷•°/5rD·Ùà Ž9ÿMNÉÔ–ÍiïT¸y&ÞŠá¸bBÞéÜt•Žu[\\xófæè胴Ô'U~ÔWÑöd-Т¥vLÌ®÷¶.GÈa¤WÄ¥ŽZêðOä§Z—–º÷ñÊ]Ç7Ê/ÛÊäò[}€¾Êù¤íc:2„}z婢ņ½~p°XÓï(è¯}.`QLOM¥({PÑ>ñòü<Å  áWóå\rn%äÅdsHA!üIJé°gã®|@Û /Gür¨´{ÑR§+r¢_‚½Ûòì:³Ô…iV–—¡Õ¢VŠ3fØÙùäJîqsÇ@˜.‰7Ò¹éŠëGLËà!uÊɬµdy†=½š†Ð?U¥ä·¶?ö*uÑ)äíRHwÔR‡õ`l†¢´Ôw¯Üu|£ü½e+ÙJ>ð’}à)j©{D-¹–zvvúüü2·²bJ„&)óóÁÍ™ óí`£Aψ–WVÂó)€È8˜`—æöåœ1'!ª—C!d&¦…@ŠÃûzµùõí[›¤e¡&&J’3Äå°¸¹Ûü–s^<°æVO¸ó4²#3íà8bî¢8î¼ÒÀâÅqêvxøuo*!Œ9;kž×„ûJdv²CºJǺmmn!›™ãyLOŽ:ÉÍsI–?<>&ÒHXÙ†Ó´ÔmsEø6ÂòÌÃδqØ¡ÓRÇ_=Žu¥òWuj†:§üY¯ŒV˜¡¸0=öqýãîÖÚõå%ÓìʤC|ðH¢*¥Ý]dÙA> <¸@·"#†ŒLÚ\ooöo–g+ë‹SCØaƒt„Åô¥L@¢CxO¨J>¬®¯oÁ¤ÎÀ!FŸ?~ÒTÈ ,°stypV?\ë†ßn~ü`8Ë;ȈŽèÔc¯[pïæÎ£¯Ë,ûüT……–Úùébn"–´ÔâÆ^27¦{—ÿËîÏ[K]‚' GQb$ZwPÈã\w{{ו'²!FïûA W!}³@¹–šˆ–›[[V Õææ–…`læªS\j;l¿uÔRÃR¶––úns?¥‰¼&IçÊäòŽ>ð¼µÔÝÂdH>„íîLÎ Á2>àÈ„¢rñ÷pÞ²ju«Úçf4Ú›Õ@”Iæ0Lñ¸Ù“fžüBÞF5âR‡YˆµÔ¦ú–ºe,2pžFõÁâ å‡ò—éÏXKÝ­ãÿöízáé[‘2Èf-5­77LH…œ“¶ÂäÔ›“Ó“¼*hkÃÌâR£¢¶‹¸¾z¢qÙa/I’|@> ôßžb\ê?ëæu–, ôÇÝâRsu¢ßð+ÁpÐß\’=iŽœ?w…‡·1ÈGšÑ×|ÉãRÇÕ^BÄi©ï§ðerHâõÜåòGò«¥î°ÐUd—inZj2Af–—™hè+°4¨šãA¿™÷¢¥.VO,øi[»\kÞqMñXýç±dsÙ\>ð,}àej©_&ÊÑ]Ë}³@wýÅÈ:)ð4±<¢Fƒ4¨šã£Ž©‹Ù†®ã°6ª]KV#/"R[¼=g‚PÄöÄC¨|ÙV> È:úÀ3ÖR‡…ÀÇÃn|zzruu9­'Ò[ÿN˜Ó\¹å”:'O½~“gcuÿ“º¥ãyºÇjØÝe÷èéŽçrœÙ-?©#ÏïQ''&¦''YØ¥Û‚äÜ5qî(Љ.ͽÜÕnœ›®KQyío\Ù†Ü%Zꟿ~9?ã^¸jŽ7k©c\êÎZjä! ê¨¨&~µ´ÔfQéÈc²gÉñŒu_òsùÀ0ûÀ3ÖRCUÙJàü÷óçééÜK“”`‘lïž ¦@ç«9’˜ú¢ƒ¾q„<•ÊX^âׯ_ýÏÀ¨Å-O÷ty>šÛ:çvwœ›ÒÏ¥ªí¡îX1…×`°±:~÷îªV;»¸`ÞXZî±¥@ж¯¤¢zŸq/wµ›õý…F–'ÒíB=AÙ†Ð÷×_-uœs^ÒöOÔRKâR1B=i©ÅÍ뻄|@> ¤0xÞZê&øææÓ§OóïÞ’Í8ø aªs®—n A”m­V#|uŠ» ;ËÊdzrB6ŠbudìOOÏ!ƒÏÏ¿; äë-{´>z|Nôå»Ã³+f ¬0(xrZjz ǦÝö yäðõµñÍø—l ‘££\œ×-¨”z®L¨Õ"~å '¸%CBÜ£#ÐìA±$ÙGRsd1H)¬·«T(*¥#²ÌPRΩs•ìê†&FÙKËVó¹5ÿˆâ*Ä«ó% ºšt^wÚm>b~!¦%]xªC²—Käbº4èuuÕÇäädc,‘Õ?¯çBªj‰J´ÔG_¾1Ù _+±~Ä· P50aI\êàí¿®¥v–ZZji({«õ2žSžÁò|²¿ìÿD}À{¢Ø ÒïåÄahâ€+ðôùé©ãÒ®‚HàÒðe†P8ƒÜ?¶Lh ªa‹=³5ð8„4 ÀÁ™™™¡ÃçÇŒÎí™!ËÆ%s ÀÍ®ÞÙmy:ÍçN88fŸkx~!¬QEMáT2Êñ.J)ßFGGÓ¥Û¹öñâW~J—ÆP\‹ÑÈÕÕU¶pGc×ó¶ëë÷§anZêÚÍÍöî6/¸:µ™ûû[[Ûw¿m´?•Ø _áÛÿW‚ñuMKÇ—ÒR‹—/¥ëÊ÷äò‹èZðíÚÇôiôä͵lÒR×ëû3ssž%H~¯'Ð÷;¸d›žbadÓE°AE»táòüÜe!l$œÛYæö £ZεÈíG:rÌa)æxÅ<½¸°àW‡ OW/c©³[E¯âÒdö®]1ìQ€Ù¥% îRªj:Î ¸_›€Znè(^Ò°¤ÁRùÓ@%]šô4ZöÄgCZ'í ?=‚§èfªs»ºi©'ÆÆ\ÁëäÄÆ·Œñðެ¬¬ñ«{kôØ0ÛÐævˆKíÌ´ÅÑsvVZjq´âéåòùÀ€}àÉi©{G0¹–šÊçç—y,ºp” ®ðíóÁàϧßA¤ž-xÅÊJÒ@û„?"U<$ŸêÇY´~¢o¤Ý V:NM¿¦4õIWÌÓ›áꄈ´+°[J‹¤(Ýytj~zz’&V¦+˜®Û€Á³§ã°éT€-„æ˜æêi~anpN?,B¶sí§¸ôišI9oß2_qžKP¸!'‚ÊÜãsÃŽ÷23²÷箜·@7-õ/‚šÜäÁã ;;Ÿ˜%Ì@ΣÓl~üØ—:ÓEì<ÀuΕ¿ªS3Ì><ç/ÓŽPâüÔ·7Ö®™ ¼°`f1®ãf“•6™Œì`~ ;øÛ ;Èò÷èVdÄÌ*sÅB*aÿfy¶²¾Ø88ðNúÁ+à+›¼äç] ì5HÝÕç÷ßx €ª|¶èýËT ·ÀÎÑåÁYýpe¬cß †‡Ï“Y7Ýúª¼}ÛÜÙÛÚܺŒ’+ûe~ªÂÇ¿‹{³3ÁÓ ê€ßþPо,>úÔGÞLU>~ÜÜÞX¹¾¾œ˜°!>ûAL>FQZþ ÈÕ˜C¦ßÕ|]ºÖÒ³f©‰×Á‡a¢<@Ø¡KËÏÕÎR¯ŒuÓ\pã•ñqôE¼ƒ)8LÞÖmîìÈüÒ™k$\¦ï¨ÌNÏþõº:syqvŒðÖy±Å^f&*›0ÚkË ”¡¾)1H°ÁÜ!,Q/YiÙA> ÈżߡWOË"Ì(²/¨ô^›ÇÖ!®Çy{ϵã×}ɲÀcX`içœvdkqÌÛ“º<¶-tX@4âé¶<?íñùâüú&΂­Tæ&,H| Km復K½·±ò³~S»æ5"ˆK¼Ó^dY@xl @N3ÏǦŒ±6ôXŸtñ£¾ùµ^™­†¨ ±?ô^Q{Y@ºZàìº~]Ùš¯T_[ìŽ?ûÎf,õæf»–Ú õÅÙÙÁ9*ꌥÞDøATË DµwËç{›å›Ò²ƒ|@> <¸ÙÁÓã„®‚Ljå®Ü|99«µqKÝ8'oçádÙäåùãð÷³OßãÞK½u~m,³cáùêÈlÎR»êƒ=Š×R{'3ÍûMÌW} óRZvÈäòÇó´ŒôVi1¼?擼#û3.JçÊnòù@»$-µÁò±6-µÑ “«ÁÚsÕ !·×—;ëKîëï3&й´>zâäòù€|@> ôÑ6?íY¯«–:Ä¥Ž2޹© A­÷6ÖZ܆|@> Èäòù€| ù@SÄ@bǸÔ(=LÝÔÑY¬¾ŠbQ+–³|@> Èäòù€| ÷GËÙš£h?Œ–n¬žªF·ÆžÕa©Y=Qc2Éäòù€|@> ÈäÉš´Ôa‚¢Ç¥þ› k²:î‹ea8¢q‰Æ¦òù€|@> Èäò÷ŸyâJûÜEÒ¶æKd© ¢CèýªÏO}Üü¸/-u°“溺¿È²ƒ|@> ÈäòÛ´Ô9K$!‹h<*Èäòù€|@> 覥Žë´äZêƒSHk´Ô†¦K½Û¦¥þþýûþþþÅÅHß¼yóñDZׯüøÁªŒ§§§œ™™!úaùon~lmmŸ_Áz~nîãǯ_K‡$®wØ|àâúzooïÇ{qéñ^ÆßS³³ie¸äíîì8jÞncθù‘ÞâË—/¬§¼²¶2?¿À¹×Þ޳öòÖÎß’¶·¶¨eòÒòЉ/ÒÍðÀù7oò÷kÿË—ÝÝÝ_” qãƒýýËë náÍ›yÑZÚã£#úŽ////-,tl+êõ›½½ýÓÓ“Zí'K*ÎÏÏ­¬¬ñªr_Ë««Ÿ?¦L¬‘VùŸš™ár¯_¿îtïõwï–*¡2þºµ×“·òèèËÙ™u|©kyÖ9îîÒžP«꿸Ø^ÖM£ðã“y`¡ã……rú¥?|XÝݵʳPqKËóáÃ*ÿrúŽV:8899¾©ý›˜ÀJ˜s¼Ò´væƒ÷#±Í÷““ƒ£#Öï ]ÀÌæÇÍä'îK¹§ùS»<;k©Û­ïæãùmûûup°4ayiiùýû¾¼_ÏùûömZêOcƒý#¬+ÓÌUÓµ¿ÿþäø˜MÕæÖyÀÐÕjÕ’ ñâ,VŠ©V§8Âqš˜Í­Mo4Æ6¸¾¾¦»š›¥Ë<8<¤ícè>:êô䄯”Îûëׯóóóø¹×ßgøæ÷’ÅÉéðm‡v™ ÑIìÞ÷ö¹ç²iû[Ãñí½=ê€æ?¿¨hm%,ºÔvÝa³§ê3¨g„Ÿà¥¹ý†#í¾:¨gtxx°¸¸xxxüõë ¨‘·©ÅV`^7²ílmñ²|=>n÷y éòò ˜ÛPõÉ k—Oð*ùûhxº˜ƒoonx¦¦¦Ö××[ÞY·Ãññ Ù–i(Þ¯Žõ¤[\XäÍ|§>.¯ÿùù9ïìÚÇ\tsóã©áª¤¶Œm•çE“ª)ŠÁëØØkêÌkîu *´9^ëgé=ÛlÕñ^å{~] ÄÒÒÒáá×o§f% Îã~l¿íˆmô<¬¯‡‡³³sƒ§å÷›úžíöÂü|{=o}7Ïos?9>::>>Þß? ²þîè¨ï׃ûÆPõƒÝ´ÔqµóÓDüi[Dч_XUÑö´`‹qš´±±µµ5½U*ÖkkÖR°hìÊÊÊõ…ñìI“ã!'åxzn~žGûöí»ÕÕÕ—{ThmONO:¾ú‡:>|$]ÔgdgçÓÛwloq¯¿âÉÉÛX¿¹ñòiæx3çææÙŸœžxù4ÜË++ä„ÁüL.ÎßÎϳ÷ŠpÜr./[Εr¶×¿Ý>Êó´lG7Ïx‚_¥õ¤“v?Á Ò½ð+nfŽÞ޶÷ÂOn÷ƒ£/¼”ÕDïcMJã ]ÕßšÀÞq¼V»¦Ÿ™šªÕjüÍ 2==]šJï |ìiùXžëÉÈ-ù ä4cÅä«ÖBn†2kKiñÆwKKïÞ¾ýþýÜÓoß¾=9±O.Þ®Òöz»JºÑn¯®®Ð§5¿õ8€à-èÔwìÎ/,ÐGP%^:ï;Ò¹¤O„™âEtÌztl]~{ž¥¥Þ—*¬m¥òºZ…âÙ)ÞYúŽØ—ï#%ðÚÑŒÿøa–iP¡ôÍ}\‡z|©<5ç;W§Ëk©P›:óâR«¹™7adÞÚßqƒbÝâë׬ Ikðþý 9½Î< /3ô?ñ\ëgÃå^”Ï×®¯“ßbwóóŒˆnnøžð.¬=7BßN˜ã¤i*ñI|uiy™¾»ctñÏØÀC§`/רØâÂN[ÒóúÚŸû­ïæãùmîóGÇ‡Þ ñ­Hóèï×3÷[óÆ´9æôyŠu"~˜åž6~Úö¶4y™–švsÿà÷eLÆþË—#Žð½†VcfnŽÒfæfÑ ³1*BûÑ>^áP/ã?ðJü®Æè(IlÈ^¯s"Aö¤}ü··³G³Ë˜’ñY8Âñý½½¹¹Yr.,.îíïûµèBØxÓh켓pNÜLNº z/Ó¾™ÎÏ=9Yæô½/sk‹œÆ:ÿœÏúâÛx—§n|ƒæ¸#ÿ¦ã8*9ñóĹ/¹Ÿ„dL'¾¡‰/üù3ŽÛГLU ÷Ôý­ù‰¼ öÖÌØ[Ãñ‰‰*/Åùå%씽eûû´ƒOÝΪÿcsl¸ m×þ_u£Oâ]`‹ôý$´È“¬…Ä©’ßþüYƒ²°ÁƒxšöÐËI-0Ú JHþ‚ù¼»O_é]ÓŠBÇð”ßïåå%¸¹%A§U8¯X˜Ö>ˆ×p~Þp|ù;›só7?~äïW~.ÍõuÍpp—¶½½žpÿGÖñͶÔáûù9À—‘ ŸÔO Ïuø¦Då—…]®•êºéØžÐ8ði qËcûÏP•¿¾±ÁÀ(Ù™¡M"M#G°$Øš®™4G8ÎHƒï—Ùs®Á‰â{E;Æ(÷ÏÛäÏñòú§íæ{€xê€ßvÄ9åïfêkÏoy¾××?Rý©§ [žûþ_ÝlõlŽçq©½íò¸Ô°Ô–© ®Úµ³ÓxšÆÄ4¶t9—#ö‰äøØ¾RŒlnn¹9B# ë ºm¯0zÃûÿ‘Í_££Òö4ï "*^öt^ž:¬ƒs~þ™žã\˜_0†`nŽ´_‹<|¶³P çä8q¦77è—¶¶Î RaZàekAŠE»jíäÌφ™݈û_ê·úýy8-vƒ¹Áƈï¡Zò0pUqâ í mãw©¡gÏBŽ·©ýÝùúõûêöNµ×“ÎãðŜޭ¥n´$Øñ„†¢¹h·VµyÅûëm û–6(èÇÃÕæéø`v†ÁÇúV§Ø `èa”Ó7“` £Æ¬I|÷u iﻩ½ù»À(ÓfÂs§¶ºc”´ŸØ<Ç6É7x(æ´Á‹:Úay^XÇ£Ó7Ìòw3 1{ƒÉo½Î¿‚ûyÚ»Ç~¿JìÜ7_zÔ:xë4ôÅwº0.âRZj;ØEKícÚYp//$­Ãænáļù´\á µ†èÛ\Ya¼ÈÂy:jŒxÆgISÉêÄ„§þB@ÄÏŸ°«“Ó<£y¬ONÚÈÞZ®Ègooæð-fuyùüܘç]˜: pY´ƒç^ÎÏZ ðO+Fµk«IàÈ÷Mý¶¶Æs¨Æñ~/^O¥ÿØL:±¯‡l˜k©¿}û¶±nÈ£…“N×-×RCƒËñ"\ž€ôáHxkøðÌ[³´”ô öµäð+’“ŸLâ±®®ö/9zîòùŽm)¾A#vpp„}Œ=Cãû ¢µ©b.8<÷aŸYe]mJ‡ã´«±÷­TH'ÿgž iwfFz¤‘…@‡rÿ¤í—¼_\´©·ÍïÌΆŸŸâÝl¯QN;séçºæ¸ßåHÒRÛK´¸è*ئºÕë©Qbt|§:Öôv||Dý 7ÕŸ.ok+èMèÈWÖv;ŒÒ¡¤ã>O#qÒ© ÁàIKmTÑ¢÷³/êÝðµªcBW}ù‚ÝŒž³¯"õóïß9Ô#1H]‡…½FˤžöÅ/ùj;Æ(é/Z°—c¢©@2’ìx.C(t¯Ý8l²%ïfîá·^çÑññ ÝòF¡î¯W‹_=äûõðI°žµœ-q©›µÔŸŠêZê4¶ÀMß¿z8š••eø_¾¾ ’;?µ±ãùÙiÓRWL*—­\ µãAŸÇ“vê×çY«Z4ýdðñ w¦«ŽeÒZq‰ÓÓ3¤ éZ¸8¯%stÞ¯¬$^Áô¬ë¼«Ô 6ÐËŸ˜`(lç†ÿs„ãäô·úƒå¤EîÀez=¿ÒOÎôâIOßòs?l¹/ü*ˆVãs¿_±Ëï áH¸"fjjÆø?k‚–ÚÞšKóº¼Ð]tÞÌsg ™««åoz×:¶¥xÁd@c×µ¸Óô Aq¿¢ý´–‘;´íß[ŸÎ"g%¨GÛ§’“a¢Ç`Êþ\Þò¦ðõêÑlûsä5988<ùÊxrÚëß’Ç@UøÜŸO÷˜ÞÙü^ìýZ^NsÒ¹°$|þ¦Ïj¯sI='Æ^C{_¶i©g§§9+Ù'ŒZûž±>ºµ3©Î‰³ôoªŒÄÛuç/¥¯±ï!3»ÛÛ&æ±™oÚL bn –áñ¶9b±\*†j´®!|ÓIÎû¶cŒò¶:Ç6äDôñ#ßrÖ ïx.Ê"Úêní’?Ù’w³~KÝŠnËÞËKD,Óú~½ˆ~ªà ÂûÈÖQKíÑ©»Ä¥Æk¿?AO†›"ev-5~¼³cì~ÞÜàâÕi{Tìñ3û†W¯£ê³Ï[mãl-Qhî$TÉ´Ôi“ÁI(?>oL̺mtpþ}0Œ_¸4žžŸúôɯµ¾¹É@–ƒP)~„½««mÐZ=/Ó¿õp_—çç©LÄj°;ñ%~]/Sé'jº+¼”iÝÁ£àëÿ”ûaËóe0†ñ—gvyùik‹#%>@™|$áœáDã‹ÏÌÎN3+ÀkB—| ÈŽèˆÎs¡««Ÿ¨õŽÚŸKÇzB¾pœ ÑïXi¦ÖhjÿyU=£º¡!A!` B[¿ÀAÕHÒðx çÚ™v®UÉDgGïùØþ3Tåï|úD¼Ø ÒÙRÒöî®)íšâRß„¸Ôç%q©-fÙæ&í,ã'F¢ É=pœW‹Æãæ†v–?iSÂgµ1¯6^ÎåxmP/Ñ–ÙÜÜðiœƒteÅcšòIˆ·’/Ä!b:öÊ?A«Ð"sQ4j®ÛÆ4\ˆ9ˆcããÜ”“(4Ö4g–sb‚À#-1_<.æ âžðëž]ž£ðÁÇ äææß¬­5âR'?lÖxÚ´__OW«+>$ß@ùw`s³âæ¶EÜÁ¼±µÚëê„»Çk7Ío#ù[“žºRÜØ{®E'‹ò ø¹ò=Ì";¸“¥ø¸šþ’Øçp½Ä”Èc¨ÿ¬Ó 7ZHïPóxêÓôëy §Û—Æ-yäž³øÐù3jàë﯌“n´·¼S++Äyè7—ùîû—ºW2UZ^Yö¶=õ-q©y¯××>zÿâeÚ§ükŸ?#oØÐÓëiïû—/¾ /;dSùûîõ§dÆÌ‹ïßwôO~Mq©ýÅG¨à}P²K\êÔϾŸ‡›Ã’Ì|õèÝf0çŠgGwâëY¸^O»¹‘ùÏÑ£ž Ú!P5yl’@'Œ‘â@çÏ®°ÛäÏ"|ß·¯‹-ÏX__ ß[}©Çwóñü6¿H{Üwî¢,.õC¼_/ÄW³¸ÔøÆ@r~ª2;=û×ëê_²öOoh$ÀÚìç¦+[›[Ûë61%s¥]ÒX?D ­j¸æ#”¯2e[ù€|@>ð¨>€Î¥)ª&ÙùQíüàýïKx^ÂòÉÇóóÍO{0¹ç×7I{6Wµ±qW–zcíQù°œçÔmŒ«ãò ù€|`8} н!•õýäQûJùÿŸù¿0ÆŸÙMþÖ‹ÝKí™ÙŒ¥ž-âR{TÐBK]¢%}(ÝU®É{¨2UÎKÐ0éåçòû@ˆacVë:4·Dój†Ì„1ÔN>r»d?hýâŒXû§’Xê›$ÒœŸ#.n»–Zc—^Æ.Ê#?‘Èäòù€|@>ð\}àÓÎþÆÖæeˆºÓQKãR;žöy‹a Å o÷`æÛD> Èäòù€| ù@d©]³U'–úô&)Bæ¦ÇŽÎÎÏŠõÉG*£•¥ÃêèA‘buâ”úh\mÈ¥ç6ùºBž"«Š±m§æ{?Ç f‹X0­ÄNùÅú‘aíƒøkLû³Z¾úPq­Ñ_õ_£•¸gIw*äGú™Îêàž0ðuHÆC~Øî%=q³^ašâYû×^xé°V¡Ñ”Ø{Ôö^Xû^úJø5²7Èš¡[Ñó›§Öø«L¡Þ"y#eiK†§ãåÛª^·¼M go7Í_¨g~$ΰsC{ÛéKZƒØG.†Ëå5iic[Ëo¿Ç.þÖ°aQÿv;ÇöªQæ--X|^®hFKI²jÑ%<Þœ§;Ì'+žZêµÒs|ÚGî« èÔã ¼ˆ‚Ð{ùž³÷üíOä®ç>§üÏÕ?Y³Åâ/3qv~añºæÍ³9™k©Sľ ¿MŒTÇ*4(ÞÂZo‘ÚîøÖ>l/Û©üÆu­ˆ]J¤ŒS÷Ñ­õ(%x:GØ)Yxëžnô(ÖÓééÂZüô•Æõ~¨ÑðFðŸÐ|Ûu#¾ïýx¯ÓbM +źY=C¯c£WÉç«£‹¥{I}O£/ñ[nê}›úæØO»oØózX¯x&¥Åñ«ùñg1ÐtÏxn8ìï/UáceéÑÞ̱f/é&¬Yôn‹X¶­=iãEÓÓ@É~N‡ g±¡pìí7šæˆ­â ï`žN(¹åîš[õægXŒqšßFÔžZ€FEþë¼¼€ü±áLÙ}lŸ±$-¨·zûÓ~õâxó;}Ûmë>ã-^Wv ÍVYÎØúžϹ›üñ4Ø™¢™Ì{„þ§ÃË1YŽ{N;»äõŽé;ÃÑöêß^ÍXµb•`póq¯ªP@EŽò}|¦Ž™œ}ë’~Œfæ>×êvn~ü®uîZfcÌÖåe~ò>ðG¾×“¯¶9M󸫛óENº~ó«BŒù„ ç§FXŸÖ õæÆæÄìbí§·t‰qi4æMLâc V£¥r\Õ{ÚÑsâx ÅtjyC½Y"—ýÏŸ–ßZ1ç§mc¹Mþ°”q-9\Ünþodmc™n»Q´ãB¸9áo]ì!âš½h=GãUÍP­_±CûNoë½-MÏÕpÓºÁ-¬Iò€P¦cè`1”ÜÔoì>Çÿ«@*˜-1ÖÑzŽùü~}˺ì`%¤}jZzÜÆ©£õ–5Ò½?w¯aÒ%÷€x½ÀÀ¶<÷¿ß[ž) Y³ðžYÕßë²tñòõl˜¢Ÿsó>¯[ºS¯ÙÞöp$ŽÀÃ5¼þŽdMn¬Cñu+¾ÙaÕJ†±®ã¼fô™ãNÿ5Ð Mmˆ·«Î4[ûcÿ„7—FÐÛC–<Ìè9Ö±÷=don´XÀ©V5žN¢-´¼ãà7Z˜bÍÂPZÑ0sdÂØ&‡å±b;KH-’yÅÏX‚]¹a‡¼¡éÒ²…ú§n!˜ÆŸEñ ³ÙX 2¿ ì~öÎ ÊnÅ{Ö`(î¥èWšŸ{or1^Jh¶;Ïý­=íGâ¾h};ü!Pø%ÿV™ÎÍ˹WºðmïƒÒKÐ{ºä.îôS³+Å÷Ôý3¿ë¦y§ ¼¤ÌÑg‚uð·æãí>é-Þ->à%gþÓ”nU 4yþ½<¶·’³§ÝF†A‰›þýh{{ëïù7s{;ÛSñkZnÛñtøØÀ»ážÃC±]Ó‘çyº¤ó23<¿N«¨‡Þ<ç_Wíhh¢0¤`dÓCëäë¥xšºèQ¼_I£ØP¦÷4®?á ´·3õµþMÖÛ©ÔïÆ´iÚGtÔ›|¨SºhæcN¿LÂÖ~É–}q¡Îà«›]=ÞQA§óèÒâ÷Ò`Œü*E—¯XXÀíѹ£êð0"R÷¯ ñKwÀÓôëVÚ-éà¡1ÏäoCó OsÝþ¤ý*ƒÝûçOï·ó3*ž/m‚a¸øÄü)Ow4<Êý*í‹Q\p±ˆ†»¥Í3›ÞâünBÒ‚çÇ’=ÝÀÓ†¡ý½p4½Íéàÿé j4¼[;6 ïší[Ñ^ö«ç0Þ+ð¦óÐáË!ÿ„}¨dOÇfÉž”-þj$^¥a±øe/ÇÓ¡CŒ™Vvn¯§C‘VÿŒeˆ<Ž#(¿`„ê>ö‡Bk`õ R>»L2O„´nŽ©ûíùslÜÞåš¡S«._÷ò=O˾¤³§à« „‡À"»öü‹çkÓ8ÊGDEeKÒ¹9#VÈ*•W³kÚëæ×jN'$5~á1tÙÂ}¹cžX·xï'åˆ3O׺»dgu¸b~õ.é’»èå§ÆºÅÂ>?±eá½pðIm%þ­Ô4IÙ»£Ò;ø@bSiæ½øj7?ï~Üßôù›„5ª#,ÇÆÂ‚ÿ×ÿü.ÿ÷ÿ®þñé©ÿûçÿ÷jdä÷«ðª®ûÿþ«üþõth´~çéWÿUFâñº§ ý=âéW£#¯éÊ+~m/3ÕÁ®õêU}ä÷¿•êõßÿ¼"ÿÿ ¿:É2ò/ïÀ«þ㇑W•¹nñRÐR5ß²4¥y™i?2òóÓ¿F¨§5¢ÿPæïßÿxÚŽ„bìøÈïÀ<Ù»Gñ¯Hÿ.8æWÁºT„ãMiêoÇó½Y#á©wMû¯¾Çž¡Aíœö_‹gQÜ~ÿóÏ?ÿ‡ûùÍ>XodôŸW¿ñ™‡{üý»þÊïÅöV>?`L»‹ çùãÍÒ¡ý­^µ};˸j¸IòD~úŸžÒ<å”ÿaÒÝÙqzÊW?݇´_qxö‰«¾Ó½w{.ųþ]ñgÝ´ù¸êp¼=mGÚ?Iþ3j¾×´yeˆ㸧¤ê˜nðӯ̫ãÞætÎa›?ûûnyšÒ¸þHý7ïBýwÝ^ Þ—“ùû^¾€HB«š {a¬­¨.¼ã¿+¡dÚ(^GËùê÷¿ÿþË‹öê_ûãéð/­±.¼‰ÿb^èÑ߯þú÷Õaÿí/i;ÎÊuÿãemð©vJý7WÿǪóOxÝÿ §«þ7ú&äWk?­V¡>–æWËÄYÿ¼âgV;ë¿ÿþûýÏïÊ£¯hBþûïÕï¿,Ï?¿)ç/rZžßÿü÷o8ÇÚÏ+”OìéT¬…·°’½Õý‡ìäg½ú—¶é¿`€QÒÖªüKûdÇC+z«E9xªµ9ÖòàG\-å åûm${†´Ý—Ù¼HÓGØ!þ¡ú^aGû1¤ídêù_…«cÏÿB“Ï>¢±¢­¶üŠÃÖNþQ:ôGt„wÛw»Vìqì ¸b³v£¿hNó³õaÿÊúM;Ïúýƒúµ°GbÚûµ¦}ÖßÅÞÍó´Dzþ®çtª¿ßÅö ËÿÊÞ;Ûû·TN:ï‹W #†çÂhʼNû`Ü&} =è?üä>ÐÀQ·øU7k?Þî½v¤ƒŸç>ß%íÈÞoÿÁÓ•“ÿõåûÿ{Vý³ ÷üÜÜÇõõ‹ïG¬þ21Ö>¨o:â¬cób¦`käz»Ð“¹N ‘ PT¼iÈÓ(³H‘Xð0€ô«»28¤ãX¿[ÙñP#]¹á¬‚a ïxÚØ\ð4ûTr¨³Õ6 õ«GÅ­ÐÄ1Û‘˜1°/Mi?Ò´¿•Ÿn°_NPxÍ;¥¬XvwVsXꂵŠOÆR›… ®(Þ]bךËÉ™·|ÜçZ´†œ5Ñ¡\1VÜ©ê`Oc¬ÙÇšXÕ ¾³hÓ­E U±«çûÊxS}졜˜Çb”P~lþ­n°ƒ—ogYM¼æéÞc:O=HȘk[Í­çFÝ-i•dÃX!¯Uü5jdI êþ7jwáú™Æø$˜Øl’tz¡K)¾Â5Ò~¸½ßé|ÜŸHáç·§7¿þµÖ!8ZrUï"bßû‹L7Xxuäþ½åO~+lÿOîòwvN£sðsвŠþ.Ö-¼©ì=ÝÛùÖ¹¸–_1xE±5rßšÏ(¾)öÍ' ‚½-pÑz–mu³?¯‹O6¸L›Ï´|«ï幞½%\±)ûXoéÆÓoñéV?Ïëß’N^|<¼5|/œ­VN¿l®oNU'ªÕê_Û»µZíæ¦vrrÂ{¸¾öqyå}‚m±]Î>Ä êHö>ÄöÂûß ¼Ù^éÝîlÆ?½¯¦/‰Š‚CÅC/àwlëÚÛøÙ2Þ’×ÜÚ ¢eì–îðA5uXIyãCî ü§ãu)V½Éþ(O¹«­(ÀO«/ýÙñ¬§/iÓKì@«q­Îùý]È^­ ABé:Å›’—ßžîÖµßo^·ærºùjþŒºV9¾úEUÔ§§[Ìo:/§­Ì¼´»yN·ç޻ݺۼüoj»÷/y_3¨ô`:úÝžûp¿ËsìÚbôÒª (OÄEÙ¨è±}²³ï ›o<ôrôåhk{‹Àñôøøø_ÛŸë7?þ¼©ý¸æßËË˸h7;Þ£è hLt,(2O9¶Ž-o›vÝ6¼<ë ràÐŒ/³£@ðM½š_«P¹õ¢àéCžæ{Ls ÛŸ@Š3ù›»ut:(ÑUd§gøRµFçŒAöü‹\SìΦwª ÃïhƒÕ3v •loL»bú¶^³+¾/ïq}\ííÞCõÄY™ÍÃý[Ù±AwÛvm£zLtjçÛï±;0”íü}\+ È÷¶`¨ žf¤þÿ¢úÚ~2IEND®B`‚PK!–µ­âñPword/theme/theme1.xmlìYKoG¿Wêwíüˆ’ÅŽ -¢ÄPqïŽwÏî¬fÆ ¾Up¬T©*­z(Ro=Tm‘@ê…~š´T-•ø ýÏìz½cÁT¥¼óøýß±/^º3tD„¤A}ÍÒÛž2ï2øJ”Ô >‡š5±( 6ÕôCNd‡ t„YË9?î“»ÊC K-¯j>^eûb¥ bj m‰®g>9]NŒê†N„ƒ‚°Ökl^Ø-øS‹¸n·ÛéÖ ~€},Ít)c½Z{ʳʆ‹¼;ÕfµaãKü×ð›ív»¹iá (6ðÕõÆNÝÂP6l.êßÞétÖ-¼eÃõ|ïÂæzÃÆPÄh2Z@ëx‘) Cή8áߘ&À U)eWFŸ¨e¹ã;\ô`‚‹Mš¤dˆ}Àup<kx‹àÒN¶äË…%- I_ÐTµ¼S 1ƒ¼xúã‹§Ñɽ''÷~9¹ÿäÞϪ+8 ËTÏ¿ÿâ¢¿÷üÁWn¼,ãÿé³ß~ýÒ Te೯ýñäѳo>ÿó‡øŽÀƒ2¼Oc"ÑurŒx †9x=Š~„i™b' %N°¦q »*²Ð×'˜åѱpmb{ð–€à^ß±>ŒÄXQðj[À=ÎY› §MWµ¬²ÆIè.ÆeÜÆG.Ù¹øvÇ)äò4-mhD,5÷„‡$! é=>"ÄAv›R˯{Ô\ò¡B·)jcêtIŸ¬lš]¡1ÄeâRâmùfïjsæb¿KŽl$Tf.–„Yn¼ŒÇ ÇNqÌÊÈkXE.%'·.D:$Œ£n@¤tÑÜKÝ«z‘3ì{lÛH¡èÈ…¼†9/#wù¨á8uêL“¨ŒýHŽ E1ÚçÊ©·+DÏ!8Yî[”Xá~umߤ¡¥Ò,AôÎXä}ÛêÀ1M^ÖŽ…~|Öíà³oþñ¼“\•0ß~—áæ›n‡‹€¾ý=w“}iþ¾å¾o¹ïbË]VÏ«6ÚYo5Çåé¡Øð‹—ž‡”±C5aäš4]Y‚ÒAÍÄò4‚a.ÎÂ…›1\}BUtáÄÔŒ„Pæ¬C‰R.á`–¼õ¼T¶Öœ^Õ²åµòŰ`cf¡¹|N­i« [»p:aµ ¸¢´šQmQZa²SšyäÞ„j@X_ûkëõL4d f$Ð~ÏLÃræ!’H#m÷¢!5ã·ܦ/y«KÛÔlO!m• •Å5–ˆ›Fï4Qš2˜EI×í\9²Äž¡cЪYozÈÇiËÂ! †q ü¤n@˜…IËóUnÊ+‹yÞ`wZÖªK ¶D¤Bª],£ŒÊlåD,™é_o6´ÎÆG7ZM‹µÚ¨…y”CK†Câ«%+³i¾ÇÇŠˆÃ(8F6ôÖ© öTÂ;Ãäšž¨P³3»òó*˜ÿ}&¯ÌÒç=I—èÔ nÆ…fVR¯˜Íéþ†¦˜’?#SÊiüŽ™¢3Ž­kúp émy\¨ˆCJ#ê÷Œ,Ð AYh•Ó¿6k]ÉѬoebÓ%‚D<—<.Ål4³ÅLšf•Iø·ŽQ³rçœ].Ž3tvq\šsöËŽ¹³ó‘åër9\]Y,ÑJé"cf ÿ:ñÁ½ ¥1SÒØGîÂU³3ý¿ød éö?ÿÿPK!%Q3ïZ7 word/settings.xml¤V[oë6 ~°ÿäyi|Ob4=pÒf§C»,ìM¶•D¨.†$'͆ý÷Q²'­7´ÝC[™ù‘E²×_^ì±TDðùпò†Ì Q¾{Z¦ÃÒˆ—ˆ ŽçÃ#VÃ/7ßw}HÖÔÔ(¸JY1î´®ÒñX;̺æn„dHç܎’Ïu5*«&9¡DÇç%ÖḞµäiK1b¤B‰6&©ØlHÛ?ÎB¾Çocr+Šša®­Ç±Äb\íH¥û,€;G²ÿ¯$öŒ:½ƒï½#݃åÉâ=áƒJŠ+bÔHxç8zCtò}¾Û-˜ûž=GŒ xCøåcÓ–c –ç<¤üOrâ!ÝÅúÉç‚9#Pøc±‹CY—‘¢ï©q=\"ÙtP[`V¤÷[.$Ê)„…@­6:óR6ì|Þ@#ÿ)Ò Ë^3LÏŽ oHlÖi°LU…)µc¡ £Cº•ˆAC;‰µ)ñÕT?¡|­EJ{ùL¼iïŽÕsÛvÀ@qxÄ ^ìD…Ær]¡¼-×RP§WŠŸ…^Âðð¶[ÆR®w¨Â·cus-Rem$j°Oñ ¤…K¢a˜U¤d^àE3Ã0î£8¤!4“ç_‡yl#¿ñýJl¯müÚóòÍÇ+žK©£¹0l&fwZ7ÓL8bP苉ú(Jl TKòþi ì%ûA[‹^GµÂ¿ƒ6<ßð Jõ¼Z öµ«ëÿpÜ&Þå {©´å0‡_áZªçù·ñâԠâMágҋ̼Yö!~ä…ñ]/ò¯~‚8 ¼ö®^!I<½]õ"Y²\¶U¿D¢(œ…½~âdâO–}H&ËUoÔÉ]8í÷3‰ƒ(ëzÆ«Eïíd3? “>d‘MÞ¢YN£(³]?>Õ‘¥fKšÝœVÐÝÖX,Ë%AƒG³GÇF#—Ï ÂžcøY×¹G£P Qº‚géÛK,-‰ª »í™>"¹íx[ Ù+…òÓ‰ËLG,”¢®ô QuÏKÜ%áGQkI¸~ ÌÉU¯‡}Õ¼üe/í=u×sH54 6÷ó€ló5ãnÛrT®McáGTUMæ[>¤d»Ó¾i) _%ü»e?òmÐbÅ‚³¨0™v{èd“é…Nv²ÈÉ¢N;YÜÉ'KŒ v–°RžaT¸£‘o¥â€Ë¯þFÔ\‚]÷¼ u‰á5”¢P÷Ü,,ÕÁY­…ÛßH¡k8XÔ.‰ÏnV›¢£¨õ…®ÁŒruÉP"Ü„»0¶ýñ*³F oy}dy·ý®š¬)Q0•+X”ZH‡ý`1?‚¤‹{³â£Ff«d„M‹úñ Žø¯Åb²ˆ—¾?J¦3e‘?Ê¢Õt”ÅqúÞ*Z-â¿Û.vÿñßüÿÿPK!W%ÑRŠØ(customXml/item1.xml ¢$(  ¬A à E¯" ºèB’@ ËRnºèFí¢hn_)¥'èò¿Þ …L{1X™Ä€†ð)é8òǼÌÝ]^9û€›Š 6ÆÙ+†­ =rG”@5£ª]ʸµÏ¦µYVHÖzƒ—döˆÁ©ïÏ ½>­Eew|eQMüb¦7ÿÿPK!¿ ÂâU(customXml/itemProps1.xml ¢$(  œMkÃ0 †ïƒþ£{jwéGR┦] ×±Â®®ã$†Ø ¶S6ÆþûvêŽ;‰GBz^T>Ì@îÊy–ÃjÉ€(+±Ñ¶ãp}«“ ˆÂ6b@«8X„C¹x*¿oD> S—  ‰ ëåÌá+cõË.«ª$Ï7«dovÉñ”åI•O5«Òt»®¿Dµg<‡>„qO©—½2Â/qT6[tF„ˆ®£Ø¶Zª3ÊÉ(è3c[*§¨7ïf€rÎó»ýªZÿˆs´ÉéÿZnú6hìœûO eAÿ¨f~xEùÿÿPK!eˆ·iword/numbering.xml´—ÛnÛ8†ïØwìeB,+Bœ¢IáE»mÑf€–h›ˆH $}zû’”(;QcȲtcZÎÇ™_z|ÿiO g‹¸ÀŒÎ\ÿÖsD3–cºš¹ÿ¿Ìo×ÒŒ¢™{@Âýôðç÷»”nÈqµÐQ *Ò]™Íܵ”e €ÈÖˆ@qKpÆ™`Ky›1Ør‰3vŒç ð|Ï|+9ËŠóé ·Æeûn´œÃrÖÀdkÈ%ÚþÅ ¸Iô© ¿ /FÅ@GÕE½@*ªiÒô›äâ~¤ Mšö#…mRÒÔ:N¤}ÀY‰¨2.'PªG¾ò×My£À%”x ,ŠéÅ1}í‘òj$Ì/&La9*ÂÜRØÌÝpšÖþ7¿=­üëÁzð.ùW._X¶!ˆJ“9à¨PZ0*Ö¸l*œô¥)ãÚB¶ç’ؒ®ە~ÇrùèzúRIyv ¿ÖŸUä牾×áhDãÑ%„·{ÚHˆ:…Ç{Is"®ßñ±€ ˆ3Ôñ·Œ¤f€ìX¡šƒ;–†åToEsðQX¿ã=ö>˜€@—!&6q '•«ëŽíßœmÊ# _G{>^B;Ý\ÀªÿiIŠë‚ù¹†¥º›H–>¯(ãpQ¨ˆÔavÔytÌПêµêÁ|E{G_îƒê_àBH3ùuCœ7OÏê©>HARŽTóÃõdÕê|^JÄ9‚¯z‰¦P¡ñé37¸›ÇñÓä³ ´…l ‰ÿE[T¼Jd׬ Žóÿ´­Ð¶j­$eÑPæq=†O•¥ØjVCT*ËBý y‘7õu“êÞt®6z³¿¡|Û"®:NÝ€Ú.²‘õÄv”·ÚÜš>¢T{_K ¡„ƒPª×R&ƒPâA(ÓA(É…`Χ«ÿM¿ÿÿPK!%ÍI%Æ Æ‚word/styles.xmlì[s£:Çß·j¿å§Ý‡™Ä¹ÏÔÉœÊu“Ú$“3Îì<Ë Ç:äœËùô+ …aZèd§¶öe&ú‡¤wK1üòëkÏ4ËOGãÛ£€¦!Xúx<úþpùáhäI#ó”Þh>úõË_ÿòËËç¼x‹i@šNÂãѼ(Ÿ·¶òpN’ä šŠ3ž%¤³Ç­„dOËŇ' R°)‹Yñ¶µ³½}0Ò˜¬…Ïf,¤ç<\&4-”ýVFcAäi>g‹¼¢½ô¡½ð,Zd<¤y.:Ä%/!,­1ã=JX˜ñœÏŠ¢3ºE %ÌÇÛê¯$^öq€8é+Žq¤[ÂÒä°Ç9¨9,28n19Å!ö«väo‰ìQ~¾~LyF¦± 1ÌËEkåêOqøá°ÏéŒ,ã"—³ûLÔŸÔ—<-òàå3ÉCÆD»|[ÊA'Ë‚ë“(@ùÝC.bTDì¤Lb/Ýðð‰F“Bì8©s‰ß¯ï3Æ3‘ŽGŸ>éš°+E45Lç,¢?æ4ýžÓhµý·KàzCÈ—©ø{W4_5".^CºéBìM‰åNÄòè%[\™ÿ»‚µmösJdÎ ÆëÕ|bGZäFoۙ˵¾«£P'Ú}¯í½×‰ößëD*ÞãD‡ïu¢£÷:‘Âü™'biD_Ë@„§ÔMK4¢9–`Cs,±„æXBͱDšcqt4ÇâÇhŽÅMœ‚‡6/4œ}×âíÝÜÍs„wó”àÆÝ<¸q7'|7îæüîÆÝœÎݸ›³·ws²ÆsË¥Vp-Â,-GÙŒó"å ú:œFRÁR…¤žœôh楓0efÓñ`ZHÔçÍ¢‚Ô}>/d…ðY0cËŒæƒNÓgó H žG`F‹efŸÎèŒf4 ©OÇö••`.“©ß\Go,šFž‡¯"zI µC‹úy.ƒ„ypê„„Þ4N¼å‡–+ N—qL=±îü¸˜b ¯ fxi 0Ã+…^šù"Mó4RšæiÀ4ÍÓ¸•þékÜ4ÍÓ¸iš§qÓ´áãöÀŠX¥xsÕ1îíî,æòÒÿàvLØcJÄ`øt£¯™÷$#YÌyYºkö{žS½>æ´šäk]¯\äLôš¥ËáÚ ù ®šç)¼jž§«yÃCìV,“åíÊO=3YN‹Ö U¤^A;!ñ²\Ð6R ÷°U\²,÷íX|'—³RN™oÕÊá [±†‡ÕzVòÚ<ôÐʘ‡O~ÒðÕÛ‚f¢,{LºäqÌ_hä8)2^úšò;J’^!‘,æ$gªVj úOõÕMÁ-Y îÐ}LXêG·‹ aqàoqõp{<ð…,3åÀøžò¢à‰7¦¾ø·túw? <Epúæ©·'ž.)Øó0É”$y"‰e&K™—9TñþIߦœd‘Ú}FËût ê‰8!É¢\txˆ-‘_Dþñ°R¼‘ŒÉëBƒiÆ•¾|9ý†Ã³Ó¼\Ìùº,Ô%Cµ:UÖþpÃgönø¬þ ®òM˜t9mà†w¶óÕÙ³˜ä9³~ëéÌóÕ݊绿Ãë5Íã1ÏfËØßV@o#X½ !—Išûì±âyì°âùî¯G—Q<WÑ‰¡`¾”P0_2(˜/ Ì«Ãoª1`Ãï¬1`Ão¯)až–Ì—Ÿyþ=}1cÀ|ù™‚ùò3óåg æËÏvÏ:›‰E°¿)Æ@úò9éo¢I š,xF²7OÈ‹˜>×4KÚ}Ægò7<-ï»ö€”—•c«ãtêÁiNIsîé:Ñ*+Kã"Øþ§fêg ƒ›p“ÎyÑÌÒ'»­($'åO Ö›¯šÑëÞ {œÁd^_¹61Û-«J¶a¶ù„mc~Pý6£Íì–Fl™T …? 8Øío¬<ºa¼·Ùx5Å6,÷{ZÂsl¶\-–‡=-á9zZª´Ù°ìЇs’=µ:Âa—ÿÔÅÅù»¼¨6n=m—#Õ–m.xØåEP NÂP^ù†êô‹»}¿à±Ûc¢ÈNÁ„“Ò;®ìˆ®ûFŸ™œò0IS¯¾`ýt»juÙ+sþ¶äå5èÆ—'ý t-ViNƒVÎnÿ/aYÆ>޽ÓÑ;ïØ½Ñ+YÍQ)ÉNé›ìˆÞIÊŽ@g+8#಴Çe+hï’­ Å%[ Xؽ—v:P!¨V v*P¹S B :P!¨T¸Ã*´Ç*´w THq THA*D "Ð è@…t :®í­æN )è@…t B:PÕzq@ B{\ B{—@…—@…t B:P!¨Tˆ@*D ˜;*¤ "Ð è@-6ç¨Ð¨ÐÞ%P!Å%P!¨Tˆ@*D "Ð ¨@æN )è@…t B:PÕ·hÚãÚ»*¤¸*¤ "Ð è@…t B:P!¨ÀÜ)P!¨TˆèòOýU í–ñ1þª§õîóþ_]éF}3–l¢vû£ªVÙYýï«?åü)hýÝ®ª7úAØ4f\]¢¶|ßlrÕ½¨/>¿žuÿZŤ|€¾¯_}g à{}-Á5•½.—7-A‘·×åé¦%Xuîue_ÓLƒ{]IWÅeu·†˜Ž€qWš1ŒÇó®lm˜Ã!îÊц!á®ÌlÂîÊdžá~ “óºõ~Ïq:¨o¼„.w4‡vB—[B­ªt £¯hvB_õ섾2Ú (=­¼°vZa;ÊMjfX©ÝÕNÀJ NRŒ»Ôå,5D¹I #VjHÀJížœí'©Æ]jˆr–¢Ü¤†SVjHÀJ X©NÈVŒ»Ôå,5D¹I wX©!+5$`¥†'©Æ]jˆr–¢Ü¤U2ZjHÀJ X©!ÁIj€q—¢œ¥†¨.©ÕU”†Ô(… sÜ"Ì0ÄMȆ!.9†Õ’aíX-Çj jUiŽ«–LÑ섾êÙ }e´PzZ1xaí(´Âv”›Ô¸j©Mj÷@µ°Rãª%«Ô¸j©Sj\µÔ)5®Z²K«–Ú¤ÆUKmR»'g;ÁIj\µÔ)5®Zê”W-Ù¥ÆUKmR㪥6©qÕR›Ô'd+Æ]j\µÔ)5®Z²K«–Ú¤ÆUKmR㪥6©qÕ’Uj\µÔ)5®Zê”W-Ù¥ÆUKmR㪥6©qÕR›Ô¸jÉ*5®Zê”W-uJ«–n… óð8£IB²"ð÷ì³+’Ï 2üA{ßÓŒæ<~¦Qà·«7¨^n½4^å$ÙêmpâøBŒ™|š·ñs¥¨|š©ª¯£ú•KÒX¶$Ð/·Ò›Uƒõ×µêï,5µ>f{{|¾zq^e{y•ùꪽúCû««,/;‘˜M3õ/õn¯Æ–0?/+¡ÕK·To7ŒO="ú î1“ÕÛ¥T‡§DHñUJF,•O&lÙ.½¸Ú^ælN²rï*¾ªct± °s°t~Ùà‰ÒÅ8¿Ú&?§¢¹ú´Òf*êEe–ÓÙ…—]ºyŽkxaªW¬‘ß[ÞÌfî¼ÐÛj[-W/g“›OY¤ßÍ6-ÿ=+{ÊÜTµr÷`ÿò“òheªò–ð+•µV›å­4{ªGÊx×›Îòw½©m½gÇê=:qúñžÞÓ̲CÉWÕYªÊY?·Cí]OÏmµî>z¡Òpµm³û„B9ê'×Y’³~uý³SõüéuDz<¦ÚâUê¦ÝGìí.ä¡£Íj Ñ9«”« «×j·ÝÔBÑži\úøã:•Nü¢§›²¥Ñ+)Qbÿã[RÍöCc:“±'öŽ·ÕÓOÖöOËgoZí3µ°µ¶š)?vûIù6}Ç•u—«·–áV·ÿ éž>.s14j-²Þ¾Æ\·ÞJ½ST«d¶–[ã Ô«eFÝås~µÙsßÿ§»Z—3“MëOZëÉôRëw›‰Ú22s¬žw°.¤ªW»Û„4—.P ]}O$ª¸`*ËË-ï]n¬P±ÝÓRÏÎYï\ãaA›ºfz¥í\˜õžâì#b)”È\Ô?µ/šT™T~Zsžq‹ó”Ûü¥…õ¡éó¡ÉÁÔ'5_üŒCßîØå-äëÃ[nÝäË}*Eêòî±®PEÆâ4Rÿ×ë+u¨|!Œ~óÇ£òit¢ä}W—™Uºì‘(eŽÐ|pyb·*XBù¨¶×bIbýp,#¹üwçˆÆ¬0>kÐYÙrõ}Zëü_÷Yô-åO4«}`µ.¨Ò3«é—å6)aåI­Þ:4 .owÒŸ~ð³‹_ý•ùÿÿPK!À‰a™ word/webSettings.xmlœ”ÍNÃ0 ÇïH¼C•;k7*:¤ !!qâã²ÔÝ"’8г•ñô¸]Ç ã@¹ÔŽÿ¯v¾nnß­I¶H£+Äx”‰œÂR»U!^_î/®EBQºRtPˆ¸ŸŸÝÔy Ëgˆ‘gRÂG¹U…XÇèó4%µ+i„'+ VF†UjexÛø …Ö˨—Úè¸K'Y6&ü…‚U¥Ü¡ÚXp±Õ§ ÑÑZ{:Ðê¿Ðj ¥¨€ˆû±fϳR»/Ìxz²Z$¬âˆ›é*jQ,g­gÍp9 09̼c\wŒ”•}Ž.‡qf_]ö8ÿ+¦ †¸<ÔA;ÛtdUþ°räÒ0‰÷(áeNZpóåjÓº<}Χ¶Ô[êlRçÍ:L³I6»ºšfmžOç#T‘s[i ‘‰´‹>éÕú—ð úÓàcDû#Î\”¡ñâQãøV ÐG3¯q¼TÐù òe›ˆ{„éU6L¹üVÑ0mèw>Dš›Þ»Ûîú¨­þ€{ ‹€5AØKzïÊüÿÿPK!äs‹Þ2word/fontTable.xml¼’QoÚ0€ß'í?D~/qB 5TjW¤½ìa¢?À‡X‹íÈgHù÷;;21Ö†‡ÎùîËùË=>½«*Ú Òèœ$#J"¡¹ÙH½ÍÉÛjy7#8¦7¬2Zää €<-¾~ylæ…Ñ"¬×0W<'¥sõ<Ž—B1™ZhÜ,ŒUÌá­ÝÆŠÙ_»úŽU3'ײ’î§”NI‡±Ÿ¡˜¢\|3|§„v¡>¶¢B¢ÑPÊŽ´æ3´ÆØMm xfUµ<Ť>a’ì¤$·LáFx˜®£€Âò„†•ªÎ€É0@z˜rñ>Œ1ë1Vö9r3Œ3=qä¦Ç¹­™Ä0Ä䨔?‘âóï[m,[WHÂw¡æ(€ý/vëÿÂÓݼFÍ\3…+©D?Dý4ŠéP3m@$˜³gUNhŠ×”Žé„føMq•‘Ø'ò’YÖ&Ò6\0%«Ã1j7lÔÒñòß3+}ÓíÈ-nì`MsòJ)M_—KÒF’œ¼`ä~6yî"©Vø3nM\ya•\[yÅÄ2ðW†ÒA& ‘ÃLd3‘f÷ÿÉ„BìŠ ? íLøÙfâ¶™ Ó¾‰,õ&No"=Ÿûß&šX±;¾"âøað*²ÛGB·²;±:Ôbˆ˜®ûñYLÿÐŒÈÇbPÍbº,~ÿÿPK!êYç8e¿docProps/core.xml ¢( |’ÑjÂ0†ï{‡’ëµIê&Rj…mx5AX‡cw!9j°IJ’Y}û¥U«s²»†ÿ;'šOvªŠ¶`4zŒhBPš!õjŒ>Êi6×G번IX%Œ³¸)÷Ë ~x»à<†¤1$aã¦ìSòçŒEÙã â‚Ú–:­ ƒHCÇ\Ûj‰WN¾h;®ªÃ÷„V¡:ò“`9*ž¿¥ÿŠ*'³¿øÔì=éqhpðF$ä÷yÒ,”K°‰…Æ%a= ?&z°ÆÌ<» "_VÄŒ%l{„L´A~¶>6Ãpé½ÑR$Ú-¿Ó2¸èÚT<|.ò<°ù 5Ê× ÓžWÀænµ%§ÀÆ‚œÑá{²“íMj) n)>o…‰쇀­¼°{^_îÈêÈÚ/ñÑ7î*/ãkê79Ëù¬S_{!ÉÅj}:O<ë@M,*Š0¹˜¸¡W &ëÓ¬íP}ßùÛÈ;|¿'_®Ï¥}s}ú7üÿÿPK!t?9zÂ(customXml/_rels/item1.xml.rels ¢( ŒÏ±ŠÃ0 àýàÞÁhoœÜPʧK)t;JºGILcËXjiß¾æ¦+tè(‰ÿûQ»½…E]1³§h ©jP >N~ûýjŠÅÆÁ.ÑÀ¶ÝçG{ÄÅJ ñì«¢D60‹¤o­ÙÍ,W”0–ËH9X)cžt²îl'Ô_u½Öù¿Ý“©ƒ|Pý=á;6£w¸#w åE…v §°üd*ª·yB1àÃߪ©Š ºkõÓÝÿÿPK-!…6Nšc[Content_Types].xmlPK-!‘·ïN Ó_rels/.relsPK-!T>kÙ ^?óword/document.xmlPK-!Êc^`'word/_rels/document.xml.relsPK-!!2–±{ª Çword/footnotes.xmlPK-!Á £{¤ rword/endnotes.xmlPK- !ó7¼šêiêiword/media/image1.pngPK- !’j•¤—¤—9ƒword/media/image2.pngPK- !l¶—AU™U™word/media/image3.pngPK- !5|ÚLdLd˜´word/media/image4.pngPK-!–µ­âñPword/theme/theme1.xmlPK-!%Q3ïZ7 ;word/settings.xmlPK-!W%ÑRŠØÄ#customXml/item1.xmlPK-!¿ ÂâU§$customXml/itemProps1.xmlPK-!eˆ·iç%word/numbering.xmlPK-!%ÍI%Æ Æ‚Î)word/styles.xmlPK-!À‰a™ Á7word/webSettings.xmlPK-!äs‹Þ2Œ9word/fontTable.xmlPK-!êYç8e¿Ô;docProps/core.xmlPK-!(¬IŸzÎp>docProps/app.xmlPK-!t?9zÂ( AcustomXml/_rels/item1.xml.relsPK_&Cpcm-202307/doc/KSysGuard HOWTO.pdf000066400000000000000000011642311445420033100163710ustar00rootroot00000000000000%PDF-1.7 %µµµµ 1 0 obj <>/Metadata 85 0 R/ViewerPreferences 86 0 R>> endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/XObject<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/MediaBox[ 0 0 595.32 841.92] /Contents 4 0 R/Group<>/Tabs/S/StructParents 0>> endobj 4 0 obj <> stream xœ½Zmo9þŽÄðÇÝJ¸~©ÐJ ¨WEê]#Ní}@„&¨)äX¢*ÿþfì…f ”]E°ë±wžŸ{!o?’~ÿíÕàý°¢ çÃù¯Ûa”áŸs–F´×T â§^å´Ûùû ™w;ç×ÝÎÛ N„¡L‘ë¯Ýöf„˨ŠXe¨täú;ô»üdÉmí™ Œ ŽŸÒh²¼ r—’ÿuÙí|Î>æ=›-s•-ðb’÷8˦yÏg%Þ‡Eìò/¹þ£Ûº?»2º²e&oÏLî$å/ÌLX—4Šì+Úë)ÓMÀ6>~D¿ÏóžËVÓès¸ßg‹lÏá 岉-Wy¯Š¤`Æ ¯Vpo³Eµg‹„ Ò¾‰-?ž-€UÁ³»=àu{à•Ô”7¿ÏÓ¦}Vâšú£°~؃ն‡ÕY`–&X?íÁêÚÃê=uæx¬¤¥¹ž|ΞÊ=¸}‹Œ(¢/÷¥šS*WŒª&`g‘ö0‰Þ ÕÑ}øÛÌ•ÞQçà¯#ŠƒqAdž­ó*(€aT3ŸVñÁx 3ˆg¦1<”6Tk{í½Ïüú&@¨uÙ‘YN€³|e¬§ÆDŸô9ˆ8ù‘9jcÐ#?#¹¾›•þǤ¼Ë¹À´ª³å*‡ÜLËÉrö·šå›‚lžËŒû0DɈ¶^i«ö@g½^!GB»Vr¿ŽäÈ*§‰s‰S?!!˜i… ™–7›Y£Ú[˜†„êSÄ.glMéû´/WäCûZ¥¡6©´}uÐÕ$ŒÄù”Uë*§6TkÈz.eêå ôYê“ñƒ 4^Þ´¯Õ:ª“V¶OZÜqTôûV l0Iéû»— 1ÍB‚UÈÕÎc¶õÁíåô(³ñ Ùÿé¾ÄÜŽ ò»†ü¾IôKÞ~ž’Šjþû¼&a•ûCõ½&ô#·‚»]l*çZ>§ãóÍM]®vÐyº>c\0îŒóã ®ý^DL†&ø.z²Ï¸5~ÔÃ×€G‰êÇA8˜]ºHmÔCWi ›†[a×ÃG˜‹ð ÖËN樕 ßöZ÷-ì¦`c•ò­Rš1¥ ®úÑ¿¾v cF¦¿ñšB§À†SØ~j¯WÛ‘‡;j e$åúבy<±cÝ+û¾cáË“-üZÕ',äúÎ&ØrØl”Óyù¼Ã,߀ƒ )Ëcèÿ&‚À£S>9C0Ž4)Ë#E¨B#½ƒfcèa$Öu;°t d7(8¯Åêà¬u¨$H½¶:†FžÂæú,p8>ôè K}Ѻ¯qÌäñþú%Í‘Ç|;V‚äéð—‘y`·#Ã4RokÒ1S0#g!5ð0UaÊa^C®f¯šßí¤Œó sm~šý*.„[çb>°Õ¸Jt®Ãø\g v—v ©‹áócÐA»´ÆVÌaýu|mVð¦\€àr1ééQ ÛjqûjD9*7ÏLšÏ5ˆ‚8ê<ú9¤×aO[i¬‚½rDð‰ ’èK òÀ3UeÒ Ò[OÅÖXmüÒdièº#å(w‰0È ÚŸä\eßñQ­bc)KÅ_Cþ«Õg¾ÃJèÃ̳hÿ´OyØ^¤tž,Ï (-´|¥vx:›Ü?†×2ÞÞ¾ØôM ^Mâ{ÒeΡe²ÊMFÆ+”ÓÏ K•NAþ§z'’%lV'Ëi8Ÿ 'Ìp_Þ-ïoá|Ÿ?îsïøŒ eÄ·`Óý £ø[¼½ÉÅjã]û›Ká“Fíeù­_)©€*ÁJj,áÇàŽCƒ¯g$U{Pe/ÇMðï¿o§‚‘á‚ÔjÝz³µ‚ÕñìXà/ @3Ö-•ÐôEÁË[úø¶¾ÿÇ÷¼$ endstream endobj 5 0 obj <> endobj 6 0 obj <> endobj 7 0 obj <> endobj 8 0 obj <> endobj 9 0 obj <> endobj 10 0 obj <> endobj 11 0 obj <> endobj 12 0 obj <> endobj 13 0 obj <> endobj 14 0 obj [ 15 0 R] endobj 15 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <> endobj 18 0 obj <> endobj 19 0 obj <> endobj 20 0 obj <> stream xœì]-ܲ>}ã?ÞÑzG£Ñj4­F£Ñj4­D#‘J$W‰Dßk»Øû€àÇýl?~×sž9Çggn÷û=¾Ý.—Ëù|:ö¿ËÕïïòq‰xáÀ!þÓq©ÆÕR«5=øp8œÏçÛív¿ß“$‰ã8: N(s»ÅÇ£z<šX ÀÕh\MŒ_Ÿ9Þn¬öÿÆÝvóøŒ„-¹Åñjµ~ϯ×+09ÿ³XÀG´¬ ZeE™‘bÎ"Í\±ˆ»ë©#íÄÎ-õÆuÌ ìõz¬ÇˆýVšØ'šße‘Œ|«\8Ÿ—eUÙÊ<§IB“;ÆRÄRĪŽwŒC7ö­´©­L•½¤õ~Ë"g-©¤|@Œã;ðüx?Š¢÷óÌÛèúã§ùJë&údæ‘<Õö8ÚùÌ6%shƒqSqA§Â­xcbä›õƒºFë5¨=‰o4¾±È˜‘ý—"ß*Ž«3p;£Ýa·;@¼Ne|mçœ+^R~·‘6{‰ù~Y„6D›5´'çj´·s‹ïëõ®”atð\qž.­³èy‹{"xÜŠfùÉ™ïˆ47pî 瑎ä|¡Fj`Klñ<ÉX¶è´§iJn×âv%ÊF•­QÝ¢h/¯F_§VÎî K²ïÆJ…0¾šÝp¿4KÙTÔ>ç£o±áÐö–6þ;æô¶ÎëgPôÑceä ˆÅLZ¸=úœ#ÑÛßuî×¢^ZZXDÒ‰‰G>‰°±G©ö«о¸œÉõR\.,^&×3…xa‘^ëX^/%€¯Ç#p[åysOfw€OeI¥¶‘ºþ‹²Ç ´Z"½=j>$voów ù-žsœæärÏ—Ûæ™Ë•ˆmŒŠ©‚ßh»ƒ·b9žócGŠh±à!œoǶ&;ö^)ÇÛÆzùþ<ÿ„ˆmÞÙH’ä—s~>ù‹ó‰ÅË OD¤"ÒvdÌW^òòÕ’¢†3u¸ñ=æC¢ÕRSûG[íµ{—ÛRêsk´,¡pì<ûý=VŽÂîH¸wS,"9<¯ÊS'ãZç ÁÏĦµµ@§írIÓ$¶ŸO<"óÙEEd±T0j~%ÓùÑžjN~YÛ™ˆúçù!B¶«¥êí!VªÚ¾$Ï,;¸ìÝn·!mx‚‹ÀP Ë2hØåzk{›âýOKÛ˜ÔgJ9›Yý/ºgNn“X©g{'¾üw3_kœüOϰ»c:–ÿ¥ÿ¼Nž¢[ñ„n—JšØ³KÝ{j~qßBî92RòûJnä·Z¼_/~òë%ÙnîÛu·TÙ2¾å|+"qøE@uPÝ’'Àç(j•á1xUuÜðÊ7|w¸ß ´a³øñQ{Á½ðÖJO@{à<0jÀhoxZ~ÞSèðKB[ý£âüÁùÈe~;‚¿másÊ0« ÎÒ|sDçô”zÍ ÖG–ƒ«œc¬rÄÀ·ž7{ïy ç]U¥"‡Ý¯¾sA¹äT)é©‘/ÛtZÿü”»o8çë¸b´Ü¬²í*Û¬8çWlÛ¬8Ÿ!ÖX弌saÌ_ñ ¿[GV!¯9ûº‹m€–ÔjO½¼=¨=Ðy.Õ^j>Ê>Èó\Qû²Ð¸Á:üœ<¤sµO2#_”gjϦ ÷qxè\ÀÚŒ´ŸÊó¨{¬Œ~ð‹™þ-Sç]zbªýÿø¸kévK퇎,}®i«ý஄‚ù«D0³f>òv³Bò¥ÏœófuJ!j5ßÅùt³Ö˜mÚwª½þwCT{vNxÜšQ ÒþÜR{…·lfjO…ÂàLŽš³=ŸE6°º}î(W{ÒÂ|':ñï°ó(Îéÿpþeûeçô|–ùçÛƒÓzã¸JÜûIóc0û°’ÜŠ`Ñä¸cY”´»)“ÃÜ­‰yM9aꉦ9(©ï+ÅïE¼¥8´nX“¶>³ª.1+žä˜%£$>³£ê(¨'V†ŸºËIfVP'Wž;]í)¿Ïƒj_Ü®÷h“À¶Ý¤‘ܘ-gÛcm Mžk9XR|—mX!ÖŸˆÛ}»6¬~¸ÚW]Þ¾hy{¤=0üxܜϛËeµß3ÚƒÙ9×°ÁAµ×î9×üoT®TÜ r‰±1ãçÙÎ1#›¢ùŠÂ#ó²f/»"(óÀÊ3òð1’ñ“…ûåøœIj‘ú[[Ø‘Ø{ÁÛ¨=Éžع?'üX2~ʳâ¾áߦù ¼Þámþ_ï0Uc,‹Úó†djŸÐ"áýse•Ô× laÉ–æózRVž×_Ö5ˆ’¬%±óªøèÀkæ*Ëì\Ÿ£Zy8Ûë­¥ùBíÁQç·k²Û&ÑcºÛ¦Ñ6ƒ¸Û@Ìv›|·Í!îY,8.öÛó.â™Ú3Îï"È<3s}ˆ"ÌÙ{ù­-Ĭ®×Ì÷’°]D¸_hƒŸ·/5oÏ/]7‡Ãz¿gÛù¼±þŸN@û%ˆã¸¦½íÊN÷öx¦„€$¨ö‘ôö\¥3uŽægD¤zŽRÌ jí‘´+VÏIÔ“ãUssEÙÔ¦^Yã‚1$ѿǂŸ²±ÐòÿFýx€†òçbl6{‡½lùÜGXÿ0±­¹Ôx{UQÜs9ä…{Ç’\Uõ²õiÍs‚{y°£o_$?غ<ᣃpµ¯ç‰…ÚcÉÊîíW›Å¢¸ÇéaǶý.c1‚˜¢\Äü‡]ñÈ"aÿ­qÏv¬¿5ä,îx|/÷;Ü5´Z¢xûªËÛ+j¿Z­v»Õv»Œ¢U-÷{ ü¿ß/‡%¨ýõzmÝÉq{{ªœq*tLð硷ͼ ”M½.ªU»ä-^)lù, ÖSæüTåʼ#ý<+¿å³†æí…Ú3bœ2Òºf)Å,ö?å»%qÿBWl‰œSRa¨š«€ZÕÞCRõùz7ý)|#ïé¹ÉI…çÃé|þ³ý|i¡>­G ÏÍ­0Ÿ®$¶ŒÛs>ãåÙØ¡Hêóu„?œ¾šÇJ¥õüÒëø¨ÔøpbMç}¼}VP|ðìt¾:â1t<{Iäù²ioá‰Äì(ƒS«ÛÑã™Ì®'¢Í'ûžF.ÐÖ³‹âJ<‡Y nÍ Ú¡áÙžU3çkûmKë¨å_ê³¾F¾í·òSö{„†Ã…ò¤úwXUÛMÜýLNßoBõßR5O`¶ž@¶é¿ŽÕçÛ¸‹=Ïíwqµë9|óÙ{‹×Ç;ð—\][ý{(¯gõµþ4uƈ¦¿êÃú<¢8MÛ5%ÑÚ¬mæó–hÖ]þjÌÔð‡©ê®ØýLŽêóm¸ëwU’ùÎs4&¶F“;õVÁ[ÃÏAÜÎÔhý½­ãw jß6ŠçøýlïX¨±mî0çW&î`¾†µ}Ì÷Ñy·æ·z£ÍvUíûtq÷39}¿71Õj°Ž…‰6E?Gi¯õ·±bSq{í÷º ©S#jÆõô¨Ï¤–µlLpýV×>‚zçŸX8pÃp5j ïâ˜Síµ2ŸÓÖy/ÍïSû>Íï?ÆÖ¯·öÍÝoª–ö±ÚÛ+–(œ´¯+b²ºŽ9Mr"såÓD/ÏêOæ³ÃI}W1Ï…¡Ÿæ|á˜;zçgl©zåf~ «íìÖ|ç<(9ÜÖùªíô1ØÛ ¬kŽ%k&8~o;B‡-ÎÁ¶¦Gjà{mv²·á*©™lÄDÆÌÀ<Þ3=G|«ÞReÒ ʈházŒ /n²¨™Â=cîès&¹^ÏJ>6ç¬óPŽ>¾ü<üsjß{eŒ(eLì·2‰çÎçšn—-½µã–>'RÃ5¶wòünDû&Ø^s>/›hÛ2“&ÇÍyu¥¯Áç×Îm×=ØóìzëÙQ¢ã¡Q¹ï§ï«ç¯KT‹U'6bià/æF;±ä|Þ©öðÉ-¾ïÇhÜíytã'>|ÑŒŸp^ì«ã‰÷,ž,ñ¨â30Ye~‡·‡1»Ð~Õ5:–ù-ŒIÅCR¥âª’95+×ñs’£cŽ×ì·q§A&å¸4Ðß3›j¯yûÌ¢öþ:ïÒü'}¾cd èç:©g§OóU<|¼¸ÆÎ3øY»˜lïŸÎÔÙÿÆ™zÚÏûx‰.¼S¼=S{â|Þ^óöC£ó¸ê'‹FMÛ|®4\9p«Ì3©»þIÔڇϦçýiÿ#ÕþØ'GS{ªŒ¡X®üáãÈâÆŽõlVF~åÂß»q\—ÉdöÿÓüôåÌ@ö{ûÂ~'gbÍ7bk.põís©Rã æ‚Îy¡—®ïNãsºF‡ÚKS&ó<çÝGëÆñp¼·ÿTÍÿ×õßÑKûÇu‡LÞÚ>Îûè¿7n¼½]í'óö^\uõgwŸ?—Z¬0ðG0ùõÎÇÇ'ŸóõÂqäéí³ç½½§õ¢«<ÆÑÔiä\0vµG–ö{¥;yôÿL:OÇñã{ûÍ~©ra&cô8Æîþ±§¡óò >DíßáíçÑü ÆÎqäb‚sæÆÏ&þéîçyãÓÜÛõ<“S>éí)_“öQmuE3¹òýR©â²´—qÍÔj4ÊôÔ3¦±]ÉÒ‡ 6{•øegy3uo¯ý–¶ë¾=íÃÐ"-¥ÏB it*ù ÛÀ7NÖMNÁÿ>kWû±ÞZ‚­ªÞ‘\{}¸ðCÅ™ðcæü©qÕàÊŽ+ü¹ÒŒbˆÚ7Þ¾°ý–6³Çåèµ?ÕhöséÈŸ H6`po˜·ë‡ØÔ~˜·‡”¦©t‰jÛF`çW£O™_Ÿ8_C¹!qÅÕXç­öHûÎwW‹Ú×õ[ñ;Ô¾;ù´ÃGÇÌ2®³æÅŸò™fi§‹-®ctœµO9—¶dª}?µkIéÏä䣼½TûºUj GaW”G]–~ìšK|Sÿ”ÆYóWO\ñ«Ú!j¬i/ÔþÑ÷[ÚîqÄ­ýÇ©ý¸Ô=ú|ÊøœeŸüqõ”FŸýv§Ï<£R훿^uj~­öeãs¬Þ>§öe+î‰ªæø”y ^,???Z„üív‹e G–gø35ü™>/›4!ëí¤ää·ªý÷{û¡i¨î Å@ùÞ|W50˸ÒÐòŸ–Fzû²ñ9Vo?Rí§óö.< º´èšÙ(9ÏGÍWgµ ôgE¿‹Em±“?Bç½µ}V—`¨·z%W{®ùªÚç6µïõNÜÛ§Bí©Ò̹ñ›“K'Í3ÌVó¥·Qó%>·Ûúö~¿‡î} mÛKÒìçí‰Sí›U¹Ú³>§òþ¿Kµ×G“$—>´ÊTCq50ßU†ùv5Õ^Ë—x¹\âC}б«Õê‰ým³«É§Ÿ_ oßÅÏ’{ûFíË–Ú?Ú3æ?çí_©ù>úÿæ9Bõí%W{3_âs>?Ìÿðíe´¥¡}û",Ô>äí%çñ’¶Q{eqáí{Æ‘ªöo¿¢õÒ|ûèž³|åÀzùªöíM>ª}ÙΗÔºT~·³mp¶ßK{ú—Éæí½=o½™Þ¾¾¤ýo¯`úÌ\0{ÒD[ªýz½Îó\+Þþz½¢˜ì÷ûW¶Ó‘žéóóö í¥Ú«ïú.oï“\z5Ù¼àÖOÅó(¼} œ÷¾ÝnË2ì4ív¿‹|ÇñTmè?FþÀ4ÎÛ3ÂWºÚ«Þ¾ _êíML|Wúˆyá…É¿OÌ>|3í퉗ÚÅÓ›¥¬ÇÄTù+mHSéç—êðË’ÅÛ»±æíù“h-µ—̇¥zûŽÄ¼}*¼½ê÷>«Ñ?¹ê|Wz¦=®~øllzûîä©öœöŠ·ïSôK¼}H&UšÚ÷i¾ôö´šTíÑÛ—6õøÓó˸2Ïì÷ûc­öi:Bí‘ùšÚ´Vû*xû/MŸà»æOã¼½Uí t8µÚ?Æ{û2Òùæ‹¡ûúëqœ·—œWÕþ!Ôž4j@9cÇŽ©ÝÛÓ€ŸÅôÚ𑸲{{'V½=n-µ¯Níí£Þ>ýãÞþ%óÂ?®áþqœ·—jOÛÞu¾Vû‡êí»Æ”¡öŸ¢ ÿ]¬yûÍGo/¥ÞP{ä<ó9ã¼½ªW<®ø¢Xà1üÕ¾ªj—´—Þž”\íKF~®öÁÛü‰¸ìíkµ/Þ¾ö9åSÞþC4!࿊GxûªÒMŽªö¤¥öÁÛü™x¤·GµçW´ÚKÎsµÞ>àÅã¼}ÍyCíiÉOÙßp…·W¾ëÂTS{¥…2Bó’$¹‡’_¶þP±Qmµï槦ö¥MíóñQ´aj/G¥‘óEQBòKyž#ó­ŒBk‘ôönµg‚½}ï8ÂÔ­öøGäJ¼‡"¤z°…ðG}Ô¾—Ÿ öPuÞ¢öe£öÕoŸ¦õëØ„‘8ÒМæXUñX »“óhÔ¾ÔÔžÝÏgö†NêíÁÞ¼·CúÒÌ™ÊÛ?8í+ŠÚs‡Ã7ö>.ÕÛ«cJǤÏÛÚ‡4.y{û.~RÅÛKŸóPÔ¾TÔþ1·×h¿X,:Ž´ûSH???ÖoÉõ$—¿¿ûý^µU×"àéx8”â-¢Pߺ¥¦ëõÚÛ†^“<ÕÞßÛ»Ô^2_xûžqD=¼½F{+o=?…´°P¿…:°Ýnñ¿Ð€årÇ1JÄ=އƒü–Ä2m6›Þ6„ôšTö·p:¼½£·¯æ?Új_rÎcþ3Þžz¨=° ظä kP×p£JGQ$ÕÛ¥öZίÈÁÕ–¬½ ß:ND¼Q÷Áä|>µÿ„j¯r‰>ííq{(j/8_«ýLÞ^òø ƒ/ùW«•ö)¤ãñá?ëo-zÔËï¢ñН­gíUø4‘ÌÁ.µÿ4¥·ïV{åÏ8ooŽÍµo2åXP”vµZV‡[Ç…Zgãí—KPx9;tH7~ Ž hçá°ïþJH¯Lª·Wy5ÎÛW‡ÚWŠÚ?f÷öªnK¬uí_™iöO‡>Ã(×°Öo%I‚S –<Ï»k é•ÉåíËç¼}ÕV{¼ÉS½ÐÛËÌ«Ú/—fWxz{™ÀÃÜn7ëGò[ëÕ :P^µÿ4­·ïS{Æ|õá„~oÏ×É­jö‹¸ÃÛËL‰×ëµ¼ÆoŒ–·\|¼½š 6¸(¾ßïhãö—Ë&(Ó[[H¯Lµ·W¸$pÙ^9Á×ÛWVµhjïõp$~÷»R½½©=å¯ðˆÄ)hõï/[û¬ˆù-³Nk‚°·Aá}ûãQ½oräµCom!½,©jO5µÇW¶ù%Mí1)jœ¯*éíUïäÀšÚcRg™ðWÚÆ¥ÚÛ+\r¨}?ƒÚ?Tµç•wx{ÏÑ$h/Æ£hâ@ûÆ%¦öm.Q¡öÕj¯y{¾úe½¾=TÉ;9ªƒ20>Ý8ew¥n®{)!…äJœNDç’Â|`]­öüÄ;9Î50«GV”iAS©÷ÏL¨x´Xš×55^T>øŸ_C Ñ+þëÔ^.ácÉ> ÔhŸÓæÌåÝUiÎ9Ïcý6“¾qD µ)¤¹“®ö}žÔžrÚ#ósÚR{Éy¦öåojO=þ^pÀÏã’ÿŠ|œÚc¬µOç)2Ÿ ñöòÈuò»¾8àqÕ¾(x{Zvª}Qû®ö¾ÞP?š€­R> 8à9pÅÿDý’©ö¦·GÍ'C¼}£ö~^+à€ŸÄÀ6\‹Ã§|ËÛS··Ï‡y{ù×´BzM*ŸS{ÓÛ'ý=è}{?$à€çè°DªýóÞ^øœAÞž´Õþ¼_À—¥!¶Î$Õ^2ÿaUû|˜·oøïð_Ç*ñzÊ«Þ>·yû¤¥ö¾Þž°Ç<ˆÒ øæ¹6UÕÞåís¡ö”{{áÞ)¥.Ì"Ê( 1Ĺc-öýü¬Õ¾ìU{Úöö꘲cÏßvðD3ˆgù.o_5jŸoß;ŽL=ÊðTxߤÚgvµ§Š·¯ÚÞþ™±pÀïĨö™¢öš·—›¿·8àÇ}j_JÚ+wr^1x>lzûªííUµæíøSq£öÄ¢ö5çë;9ÁÛüG°æíóàíþ0ª}æðö*íGy{ÂöQ¿šÇ€ž3šN²Ô¥ö‚ö÷ŒŒðö¼Ia…„fOü!4B†h¾—·Ï{{>ëÅppÉ}\É$à€'ÇH3å7&ýü¬ÕžØ½=êü}¸·ÇŸ™°‘H•Ûxüà‹F²Ÿ™ õö¤¾{oUû»ªöDúv'Æ7@±¡È›U6)à€çÀLcëUÑ<ø©zûÌðöwáíïmoßëõíqÍ1*C q¦ÈVæVÞfÒ5µ7½ý]¨=i¼}×8☴ַ·ŒÍ€ž?êõí‰?™ÚÓªq8-Þ^Üɉª}ù:â¿ÑZŒVûŒÓÞâíÁ4ù{{¢©ýiBÀ µ'¤ŸŸÊœÌêí³àíCü‚øŒ·Ïlwróƒ··`õ}ˆ¿¿¿»ÝÿB7ß~a_ãËCçÛ×wáqÞ>sxû»¢öÁÛkØ.1õýo7›—í1DGz{Ò0¿²ÞÉ ÞÞ†Uí-yÿ󜚟q£ C?GÑ>Š¢HL¬$€9b³ÙÈ9‚‘(j—,±$ä¨ïdÇ|ܯ_ûM’d½^Aæzµ‚¯¿½¯fƃ½½Sí«æN39ÁÛ÷h/¨ýè]Öüÿ¹\.ˆ‡Ãù|°üîñÈúŠí÷{Yòz½¢Â§²äõz1÷(±õ[0 €ù€“ä.ëÿ«q¤·'4xûá¸ý®ó_`—”hȑﶀ0"¾ ¿ÛΤ¼ä’ˆ%†DI6/dY&±lƒÄâ[¥ò-ö¦ÅÓéT¯Áû¦þy%áíS·ÚáÞžtàÐÛkFBƒ +ߥ×ãèçG“¹+£¯õ-t ü«ÕLÈþÛûjÖhóö]üì÷öÅ«Z¢yûލy{J›Qù·°t×fä$âZíy×jÌ¿[«}Q9ËZíq ¨u²½tb¨“}ËÑNp>+1›|H¿MMoß[ÞžØÔÞâí»ÆÇ…ðöï×Yc—Ú/µï ~DÕÛáÇÐðcÉãáVŸ2Cž€e2÷W©yžiùê·â[ûÝ®5àl@&Œ¦·÷Õ¼j_Jo_xð“0ÚõöãÔþ/ÆF{¨ª=a/=çwr¶["^xÁîÏÜnpá¹Ýnp.(ñNÎn3¿Ýnæ^ÀºÀGì‘’ßÚߊãÛzµÂLfrÞÝWóÆQjßïí™Ú—Ïy{°Šù¸ø ö|5¶ÝÉéñöDU{ÒxûÒáí‰2j¬˜ü3jÿLì˜)B…Úsöõð“jj?¹·ÿøX¬ÞÉù„ö|5®¦÷öÈ|ÝÛw© ö!¾8Þ¾[ó5µÏ‚·ø ñ8oŸoâWÇç¼}§Úoð‡âj¸·w©}÷_iƒ·ñƒâHo_õª}¼}ÀŸŠŸôöédÞ> jâëâÞ>u¨}¬yûºfâÄEÛÛSelð ¸’Þž/ŒÖÃOÍÛÝÛß§»oO•±pÀãZ퓉½}®zû®qÄCañö  ÿU\™wr:5¿ÛÛß§ðöôs4!à?ŠË¡Þ~/¼=™ÞÛ— ÿø9oŸzx{å^=’¶·WÛbw„³–$IÇ÷Ú º fí7¡ö‰Tûî¨z{—Ú÷탷Ÿœóyž“ŒÝÒ0ßèÃjoŸY¼}G$6oboÌ2èe2òM6=«¡sp ³÷JÃÛwGéíÓàíßáœB§½›_oNЧãQ¾JMÐ9 ùö>|ÂÛ§¦·Ï‚·] R_²å}–¸þ‰UpÙ³÷ÊI½}¼ý ±Uâþ©´Ýn—Ë_HËå2Š"³À¤Þ¾âíÕÑdÇLí“àíƒÚ³TÄU#pÕ¸ÒýÂÖÓé´åé~¿#8ŸOZ™>µ×¼½÷öÑîíYë}±¸ä+c³·ËµËü³øOª=P=x>ŸÌÖbqPvðáp€|µXͳMoß;¼}ÙòöÚœ®1Õáí[ë)ñUO73¯ŒýEñOª½–ÀÀ˜™ÀX`8°_p‰ („ÌWKö¨}"½}æ7ÞÞ¦övo?T핱Y¯&Æ)”k62­)à«Uo žØºy¼$`/8Ò)_Ýðýó…¬ÖëõívÅ’ÛífnMžÿIµ— øp½^a~·~šñÔ›ÙööMVƒÕ^ñö¦Ú‹ÕB¦òöÚ{ø²íõÊØæjØÀa¸6‡Oo×+ ˆ€!F’²+(×Íûêø‡Õ~Á_г´&Ün7-rL“óíÞæ; ·¿`õEà ÀPø⫵ƒD±!R~Sò¡üjµ‚ÏÙbÚŸ¡ÕÏã¿­öL¾n7P9ó#  ®ó Ó̼2­ûÑîd¾ÔÛ[þJ;·Ç‹ù©¹vÁ×{¤\ØÙXàžrŠ"ÇOÇüw³^§J=ßÿ°ÚËdõö~W)Çë_˜Áñî½v3grooUûXQ{ÍÛeìèØ¦ö˜ÐÛËv_˜e Cp,CŒ€ÒpÙ‹®"»^¯µ:a\Šòjþ×á?©ö nR;¼=$˜úñŽ=[òÛ÷ÚõìCx{KjßÅ϶·÷Sû~ïDZÞ¾nú xp¢7› ²Ý¶6=ŸNÐp5Üó>s úóβfÞ¾^ཽ¯¯ÃRíA½ñNœ#8­ÝO_G¸ c·ì7ÀfEí[}8­·mÞ¾{1kÞV{ulÖïøPÆ)”d·_ø·xÿü@dGÇ?…¹F Î*†Fq½^C…ëÕ ˜åaé:ð=øOªý œÑAƒèz8ÁÚ‡Rí¡@/?IËÛWVµ§ñöuÛîÀöΙ$¹»nøpµ·ôá‡{û€;p }wÂgí}8··WG“ÕÛ‹VÑæÍb–(˨åÿ5ŒàÝäúÜTò¿õXû°ÔŸÀìâ'„{û€}p¥¤W¹€‘ó®~›ßÛ÷Œ£Zíµ_W‰¶ðX÷ö}š_{û"xû€¿Ïãíã)¼}ÀÏ„Çzû~µÞ>àÅã½}¼}Àߊƒ·ø_ÄÁÛüïá—yû޼}À/Ʀ·ïNÁÛü0ª}2§·§ö!…4_®ö³xû$xû€_ˆç÷öêh²c®öIPû^–jïă½=QÆŽ#ó×UuÛîÁEXßÞ‘ŒU¾[ýf÷öî8ÖÛw©àíÇ¥°¾}GR×·7“PûD¨}æsµîí;#±x{îÄa}ûŽTñߘ€œZû°´þ•Öƒ·ÿœÖ·ø­oo¦àí¿©/}Ö··õaðöß›Âoi=×·7Sðöß‹ÿªÚ!/— hxw1ïõí-}øùÞ, dèÔ{±¾½+áº:ÿHú«jç8 ¾¥£Ì õíÍôáÞ‡­öv¹àè¸Ýn»Ý®CÙ*j¶ü?‰ÿªÚcêPûjÐúö¶>ìí÷¯ñöµ%ïh“«Vy½¾=_ 3ùš™¬ÌO-û- sÌž¸^"ÈÂ…¯ˆ¿þà  W”™0‡®V+¶îôjãëÄåÔÔö¼ÿUµÇÔ­öCÖ··ôáäÞ>n{{u4u`éíëV‰Vò724÷ T$Ö·'àèp LJµ¯×ÒicøôÈov{ œ"ã’˜X @{®×+.žLù³( ÀpYjÞ‹ÿYµ ZßÞÖ‡Voß{{u490SûDxûúÒ›ày…c'¤Æj>ålğư÷˜à Æ„poïV{˜ H½Î`Òàæ×%ùñJ ü‡‡Ý6Úð^üϪ}5l}{KÚÕÞÇyûî1Eo¯kÚR¬æmŽYäj)FzNå§"I,ç¶Î9[ÿ¸Z-ÁØ$É]mÃ{ñ¿¬öþëÛ[ûPóö½š?ÜÛ÷Œ#¢ßÉiM¸€½ð7õ˜cV¬oO”õí=güç Vu[Î.l#ä~c´C óˆÿYµÇ仾½­kµO„Ú÷iþ‹½=\êÂÝnW7¾Î?~$Ö·'r}{ªè6ˆóõz/7ç¶Î°Gl¤|+œÖηàYí1y­ooëÃ÷öä:ÿì¾=¿Z—+ëÛçX^ê6»PåRpcÓÛ»pãí UÉ;9wöbãNNðöoJ•×úö¯óöñDÞ>àhÿðXßÞÚ‡c¼}ù:op´ïNüÙãbRo_yª}ï8Âäòöƒ°ºúý3õ|&.• é!×··õáHoï¥öÓxû€»0ÿo¥¤Ç¬-ÿ ˜SÉÙo£¼=ã|¼}À_‹{ûóö‰KíSà< Þ>àÇ#½}Qyª}ï8Â4‰·8`O<ÆÛw¨=ržk~ðö,éí½Õ>xû€?»o¼}À_ÇyûdfoRH³¦ç¼}ÕéíËàíþLüiÞ>¤^žóöÒ>xû€¿ ñö4xû¾;=éíuµÞ>àoÀã¼½¿Úo?kJ³bwÉV§³_e¾¯ I^î®l*´vÝoj/}Nðöï«cvMáNþûŒiÒ¢oéƒIu¬oÝrM̆“y{Tû¼ä&§G탷Ÿ/­Oùíƒiß»âÇó©c}{è–[ZrÚ[ÒˆçíAíï¹CíSf샷 ^qÚƒ¼ÌÍ®q È8÷.:Ö·gjŸ2e˜ÊÛ´ÚKæÏàí±m4àn ç”™œ©Y|¸^/ZÇñjµZ¯×ê*”€×kÈ^ÉU(çVûîõí¹ÚÓ¶Ú7}D{ ÷öNµŸÊÛ‹¸øùÑszŽ+šßW•«â8>UW×™$z¶m5ÉáËËo\ZãK²— °Hrˆ2yø˜ÙÛ÷®oj¿’jߎ#½½·ÚóöTM £”qúÓzqC—šßWO/VÖj˜FÏ=Û6«·wiõv³Ž!$p³!WØæ™³(Äïb'÷ŸYv»t DÕœƒ’/Årëf*õíÛÞ^ïÃQÞ¾šÑÛ÷éžÔpèÕ8¾¯ÛðŸBU› 4æ׿Œ0ÿB&ü—/kãwùâ¸?X¾ñ…×àô±êšUÏ¡<_!_¬ŠÏÛƒõ`V÷J³ê>/Ã=Àš¯(^/w€k˜›mS±{–™ÕÛ»´\òÀï/[²ÈÜÎü}Þ2‘¥%9OÇ#TÇÌóù|9Ÿ¡8ÆãQ_ÖSïúöº··ªý§z{S÷”•ýÐ{ð]è%æXb¿ÛA7B&®ˆ+3Ùëز~×5[µ^Ÿ @8`˜À·øÔy0õA]3j†ÞÀåF1“Z—‡¯_/Pœ2¹¯ Ë$h(U`Ÿ¶©Ç®á™¼=&—ÚkdÆ•*µõ*ñ¿Z 0ê¡´…} åxY­–¬´Û2fê]ßþ-Þ>žßÛC¯ÊõíÅúö ÃA¹êZ¼½ºú±20áåóÔµ’k[Õþ§µB¾éíëu˜ù±Õã‚—„) œw6B ÈBÛ:âLÞ“ËÛjÿëÊÔæ h0(P¦0©ÉbE/œ1ë±Ó{-\y¬o?‡·÷Wû'½ýÂнEkub9j,d¹¥™ÐÉ`qùVU·›:?rE5þ ]dýŽ·E4Þ^Ö£®Ï&÷…#ŽW®RÞÛ¶óöÛ­‡·ßvÔ3»vP³€Ú÷®o?³·¯fõöШ“˜˜½[ÇP{0ÛX†f¡öB3á+àsþªzS·ñ[Døpk´ê°UíùtC´Ô}çžCôl[G|‹·¿+7mzîä´ç vš¦îÀ%Û½èUø»3jϼýå‚ÞÞ\²¾©°s}û¯ööàxٜșœÌ_šVë*¾gJ}w˜(™ð‘ÔLv1Å_Å+ß×Vú_xûÃ…¿ úA\&8½½Ì•˜W•"ƪ(?"^•>Gð¹xq½^:ÚöÞ“¸o¿ÒîÛ¯V]÷í¡C £üNŽœø=™=fâ`Á§ï×}'G-æZß~o/Õ¾ÔÕ~´·o*$Â9˾îúùˆo$‚ÐÕÐc0jgUå9ü·¾“óS¿ °” ,šÃnÝðfàí—{…ÄÍlòp–ùã„w]àXðŽäÀ ßâÓÐEÆÕ6Yë4ÛCæ÷öϧ<“S¹×·—ÞÞÚ{@ûªõ^Ú~o¯¨}9™·ç´ƒ²~§Ê{ÖþÙžÉy¸×·oy{³÷hM{âÁOÅÛST{Fûjo&¡âG•¤sî)ó·ñîšïoD>xü!ÏKÌæ¬ì«z”|Óò³¢Ú]‹ý5·ö!P ø–Iµèí!Nâí3›ÚkwEZwH‚ÚC§åLðù ù·¬ Qû[±>çYî¸SÁÕÄÖ“ŸÒÛKæOâíÁi—Ríq”Ü‹ùûk±V~L0n‡[‘å…³ù=L`ÝXoߣö½ã4®h…Ø“Cœ5rµg´WIØÁÕÆÛKµÚÛCÌ™Ú Î+û 1ÄY"¿‡Éþ\2ÆÛ—“y{P{qÓž4£2à€gÁœöe.Ô¾—Ÿ=Þ>éíó<—¤ÃØñ_‹|¹o`Ýo›®öéoÏh_?™ ¶-à€gÁ”ÿ®ÿ8(SWkoŸÑ{‡·j?ÀÛãóԲǀžóÄhÿVoÏÕž±þí:ð¿€)>¡jßËOáí©éíKCíý½=è½T{Ï–ðh̘Ï ìí…ÏQÕ9?ÂÛ#íÕV…⼑6jO”|+žÉÛ‹A'’ú߀žÓúÅoo¿“#žOäíñ·!äí ⿹É!JNžÃÛ9èºËð”˜ÿwZojO}½=Çí/pÀ“aÅý{ËÏâí‰?Hþ£xߺ½½äüPopÀŸŒçòöüÁØÇÛ×j?ÌÛðçânoK FûqÞ>à€?Ïæí}±ˆÆ*ðÏ 2ògró&æ,æ2eMÌdd«ƒÖ1k"OiSÓTFyJü'à‹þ©£è=¥?e?שu.Zgª9ƒxN-±Íø§(t¶p*I.©ìê夯·oÔ>âíëX漏õ/øói2òǤ›È™RòšØN¸ü޵Ä\a«¬¨1¤©“ÞÃæ‰À¤Ÿ/<ÚyUξƊ&)Ìá\ÒYÝÅÉAÞ> Þ>à?ˆ;¼½©öÁÛü7p§·¯$ç¥Úóo}ʘ 8àqØÇÛc ÷íþ3¸×Û3ÎoðßÂr»·OZj¼}À÷z{és¾ÚÛË;ÀêÝàÏǤï¸ÚÇXøÔù÷0ý(ooY'çx{ìsÕèoIÐrõ̺—q ªÊw·÷=©ÕKC½½õ¯´†Ú¿]·`.Ø3æ_¯¾%Bb'´>köã•/Ÿz{kßÚK9ú¼½·'_æíq‰ªGó×Cõï¹_ƒü€Í 0Æñæy¦æG´ùõ¸é¥ÞÛ§ü…bÕ÷§%7±/_S]êü€Di¶Û¥ëu²\B„-ßïKBæ9‚W$µ—¦ñöIqû6o_ðwã"Äs;åh 5^®1_Mú©zÆáªã|E‘ˆÃô¯“Ün÷ŸŸt»ÍGr¹ÀVœNéfs_,ŠÛí-Ç8ö^Ú?éí‰x7nÕJcæPè°åæp¸Ü×ÛƒÊügê‚¹Ž¹ÞÁÇߎ×Vûž:‘óÅñTOÖëx±`‚¿ÝBN¶ß³æ¿ê§ñ9ÃßKëôö@øoôö…x7n%Ô`\„ºVÛÃé–@íÏñvwê(Þ2Š¢ßßßÅb\·üߦ˜RŸ^¯WÏ6T]:ÖRûÞÚJJ⟟l¿ËvÑm±ÈÏg06|ÒïoºÝíÓ(‚€nÇU&¾½Žt¿ßñZØ9¢Úoý½4ê½´ÌÛ·fÕ38ð僪O™O8ç—û-ͯI~¼Þ7»£«ž‚¿E_³ ôºñw C&–’`ù¢È·› ´­·=wéX!ÔÞó“Ý.Y-Þ·_ÁmB²õ:‹"°úÉv´¿ƒøï÷®zøa²×âÂqï÷;ïcŒe¿õ”ïž ÜãíQíyüvoßÁº¯¢{»¿8ó‘óûÓí|K@í—x±ÞeüîµнË墿œÏgÈTU Ø»Ùl@-=[Õ¯cÅ0µ¿¯V`l fç³ä|¾ßåÛmºY_þûïöûËœÏjåªáp8œñ»Æ§ìÛ ¾ÃÇ5ä/?ò­â÷û]”§üÍ AX¾Mx»Ýâƒ÷ÕPµŸÈÛ#çÿ‚·Wûdž×ÑÜÞŸo éÀüÕv/ÇK|8Ãvûåœï¨Î AmùÐÈgçø@ðnI_{5óóö~uÞ~~@ÒQê9çWE´-@í׫ëÏÅÆöÏõwÅ\õ°Ã„C°ÕcüÇâø¶^¯+ÁÛóéDÙ èo«ÕËCΉgKaa=  àä~‡A!ûÍ«¯†z{ùòojÿ¼}éÂGÆóCt8ïN7àÿ:Ú£ÂÃ(àœRþîÅŽzœ*Z>œ5ÄpöáT™E{ÐÛo3Õ¾«ÎëÏÏåç¿ÛÏ$ËÒÕ*]üä‹ETÎß®0âŸÿ®ÿoïj¡V…(‘H¤‰Fê‹F#•H4ZD"•H$R‰ÄU£‘è»Û`t€~þ;ç~÷ð`²qw¹›Ibu;¢~…ÒµÄvœÛ?ßï”3Q¦¼¦“Ù~fó €ªSÛÊ]›@ó¶…<_‰ñ)­‰·È- †Ÿ® `Åæ¼|ÿ$‚’õ?àÒíÕrøÆý Lçt«×Ô=ñG³½Ú>ã|lp6 ‰â| ùñcÅGÆgrX”eI^Ìäˆ+îÊý !µuWmâ|&|lèöeâØu3×.,‹¸öζ1¶E®v;²\Ç.°J£Ã"yE«f–òW“Q¤q>“Ãu»Xˆ·G!ΩéN+¹ÌöØ_fû O€ØHÊ2âSIƒvO87ÍÛ{¶?ÙÃ4_‘¿¡ù¼(¯:ölÞ¾’û°+®ìµˆË=Yæ”uÙ~öy’$Ù ··íÒ± s$üzáÔ¶UBóè|\9R:ºí`Þ^T [Ð)¡ÜöÛlÏwÚFõÑLr.ôÀ7Нp×ÈøÐø¼Ý´üÙþìÝUMÚ>™À”ÿÁÙ¾mUKcKœd¥ðù+½;¿2ÛÏ*ÂÞÁóY·+צ ÎŽ°u¡ùÖ÷6Þðgö’íé§eû']Õ øË‘C$êr¡¾|C»/Uç/6iì<RÇc¨{J§|Ë¥´Ò=²½êö/÷ð™œò7¡‹ïGÚ GýàbþA‡7—/ú,jAtï׿O`ŠÙreó:6ˆ·šçÿÇdä¾ÁcÌ›¥é;œóóyç U«í¹Ùþø}ÙžvI¸æ1 ÷Ë¥ŸÃqâ#>ßËðŸW}‡s~>ïµÒ=³=óüú£²ýÀߢÞÆ¯¿Ç½þœ_ÀGÛä—²½á†ëø7g{à ÿÍlo¸á—ø/d{à 7ÙÞpÃdz½xöÒd{ÿŒ›loøò©lOM¶7üûø)Û×Éö†ÿ¢­’íM¶§&ÛþMœ»ý±çö&ÛþínOµnßf{áù}œdP><ß ÁWáú|{´épËÈ(†Ñ6Iséöb•Ù¾é»}AhE½¿÷t±{¤3øÍX?‘¿»ë~»~ÆuÅ. _œ?ª\EB™p¤ÛcIÓ¬¨ö2Û«9_}VGå§y~åí?C$§7›«˜“}Éý1¿‰ëð|ÿÌx½.!}c~-jËL%êT¡`ÚãÆN¼Cù®pÖÂ{u6RÕóÊ•Ðcšå¾ï¯×kÏ[¢ ?딿¯«ãEý‘´}AÇó‹üUØ?ŸÉÚuJxW?wŠ_‹©|y÷kT}É9'°¸ üôFå7aþH8«ÕŠýžƒç¥YFê½êóóü'¸ý#<ÿÜÿg{þ;øù§yþ”æ™’Ÿ³Öûc–å0ù ¶Û Ðq](žÏÒÎß<¤Ž¿éùCýϧªªuÎ9‚IQužÒAÃeåçgØn¯m˜ÏCóÿ]£ÍfÇ10 Cä|ç|Œp‘ûïŒÇQþHlÌçâœvøn~6ôÓoÿg¬ÈóË¥/|jßív=[yì÷É=6±iÐàsÐÓðó}Zdþ¬ ?¡mUóÿdÇVó endstream endobj 21 0 obj <> stream xœì]-˜ê:]yåʵH$‹D"±H$‹D"±Hdemeeemee%o’iÒÉoÓRvÙ}™¯ßÜs³iš¦ÉádÒŸ$I®×ëår9ûý~6ŸÏæ‹è£þübŽ~~Iýü êi^¯ó,à¼f±µMŒ:H „v:Îçór¹üüü¢|<0ÛõvkšGóxD¯úFÁM‹ë§ðCâg|ˆÕãÖOâwñ[|íýë@ï®ÃÂí×bÄu0k ~­§ÇuàíjõxÛ®YM.×'½9¨8 ¸Ûív¢;¿¾f×ë Z¾¨ê¼l²Òãë`,|áÂU—"pfÇ•‰-¾ðâÖW©§&ÎeŠÀ9û¯†ŸñâX†/Á××ã_q«ì>70÷I¿¯$n7Šs ¾Û<Û2޳õ‰'zzÙú¬º1_¶>mýù‚¤z²—uãÇ*Š…O(ΩǭL9h}¡à°¾ÁÇŒšª!–WóÖ×^ìò!ù»£4ôè¤ uìÖ«º€sKªä^§à¤aþ¾á¸Iî¿O:Üp,÷Årj^~Ý¥=E\õ)kö»v½Ý¿¾¾€âØÔ•+: ºªnܤdà›ÇIt•At#½“úÂè΋­™ÞFzÝ•.tÃ[‰.À‡]Ñšƒô*+öPœ¹µä“û(Ž“‰ß5¬“˜ —d_“èŠ!:ŧzÊÝê‘èRõ™4˜—&&”Nu4±ð:Ý1ÂÉTÂÉ é¹paO§û6fÎB%4Nt‚îT o³ZÕyQÞïŒsZ«…¯…o„oz¹/+‡o¢|zÄ{çP¨Öæª@q@w»Ý” WË9Õ]nõ⚤³­¨ìx’- ÁÏlÞC§Ú4Ó‰’´–ƒj¶Îp7Õ CRÚ´Sh(aÖ„ k…Ǫ»¥×ú–è*‹Z³¨»Â§ô<êÎBq¯‘›EÝ©J/u+=qåèkr• Ukî<…%¿FzŠºÙ2®ËçÜoè[:º»ˆîþáÀ«tGÊ—GD®c5iº:ž/›ÍfÎct”¨;Lt*ã3b {}ÙùÜ‹Çlœ¬T\ixV„ýtgRŸ®KIÉí?€ú:"dÛá@õè¡Dƒî*7Ñ•DÑQm™´{À¼uÁ—uj®÷ê:]Å Bk ê«Ö—÷ûÒ‹½>·ãÚÀšuaÃô;ŠHIº}«ŸwéêÀøªH3÷zQ4©OÆú´8Þ Ô…ôn†¢sy]Ýróh¼~ugNc•8ž®îºi¬5^WP]7DÝùãu澆¢k©ÕÎŒO¸®ã\w½–× c¼ë¥¾º^jáká›Ö_€Öä²§»£–ydþš”V‘£”Â#×q]×H- Ô‡Kº¬ß5Òˆ“îT¢ó{“}˜!ÀšÐ+>Æ}4X¤gxæ6¢Sk*äÎç¡ÄÒÀ¥ [¦ù=Þ­0©b´Ð¶l¡ÇÖžPw™BqcÔÛÕ]ÑG}ÝT7$Žg*:MÝ]§Æë,¤¤îŒü.u''­BÅ©×ZÎ`nY/UšæçSq9çS)|)|u9WîϧšãšãËá ‘ÅðW™÷_rŒ%—öXp\8:Ôa½˜sýVK- ‡7¢p½Gñœõœ/·Óåz:_u¹ž9Æ —ë2s"j·’úšQ¾¶àJÁ…Oêuʵ«M ú”¤ÅÛU±áÎɾVz ñ¹ÛT¥Ué¤7]ìÎâUêÓÔ“ô•^à*­[ÝÝm±»ÄTt¹©î0ÖÚ ×iñº‘±»ÐüMA]&bË4ijà–ÍbQ& p£;ØNÜñ ˜ ='.Ýkt×Þ:ÒÝóT³r°ØK{Üð¸P¨ ÎU%Ý=¨®3~µÇªªâ·•õdºS)#(7Ñ©$Vi¸vbÕ~Â×,Ôc)‰‹â`ÚbÝ;UeßtÞ˜ÚçèíJÒ¢UŨk<…è*EÝiªÌ®îB•ž™_Ñu™â„r»’¨Ýµ/ŽçQwÖ8Þ˜UZê‰êÓâu’î2g¼Î‚êÎÔuÊÊl7uí–´ô» p¹éºùx&;óÓ1G:§cq:”§#ÛÎà[\q\þ²gt×cŠnèò}e9¼pv ô°ÁÑ¡P¶ØÚq£±Y§ëô™86ȱÛí6ÄÖÜ–ËåjµD—exyŠ¢k©ìQ¦çÿþ}||üÛ$yÝ@‹”ÔWÁXõe0î<«É9éÈ6?~|Ó^õ(°N°é©=÷›kÞÒ—P†µ“¸,¥u­jǦOÏpÜõ­TT_~_C}NY¯2$8;@ýOyûß’ä±é@ô4uGWf_¢ë,‹êα<Ñ{?ž}#4¼J«Äî,+¶$^×é:º Qt†ºË»I«2U&°”èòî6*Ha·úúÌÏçd½¼¯–Éz å[&ŸÔo®EMÕ&&DÔç«&9}ÀˆÖ•!þ§¤S†ÙëF\¬þèÂ^XÕÄòñqHßÖÀ0!:Uý|šÆz=+ìt'åàåøhk®[u—^7ë{jðjq?u…×\™•Ôd’Ò¸8ž¹oëS!íúݵWÝ¥n ã9Wi­÷àÙt]­LcMu×§ô|ñ:Ë={ÑÕŵu+e pËòóÌ"oËÅ}Ŷü’yØR¶Í³Õ¶|½ÈWóÀjQ|Ú¬Ü*aÀuà!Òi~À”³f¢OWÙ!–ü ÌßBMP×ÉI·ªëôxê:¤8Ttšº[ñGBò¿±‘žÛ&ÔvzÔ*É*:æy´½,êNÅvu‡å0;¤m~ThÌŽQêNPåøCM×IO‰Ý9¢võ$«±N]g!Ò€xwy¡ô:3âxUxO{ÂÂr?Þ“©éº¾-(vçõ”NiØo’G]·üúÌÎ'$œDxAtŒš`ãĵ`òlDÇ<*º²l‰NÃ'Pw¸£Ø—µdÅ2¿d¢Ž{~è%ð-×uŒÜÊÅtÝlîŠ×¡®ñ†Ä&én-lÅnNn€ë¸®»ÒÁX¶Ó¨S¢Ñóåuý!Œ)=HgL²9ÛôÍ­”u>µ9Mq½„s4$¨ô$“î·SW8/GÕu} ]Çh°Û‡ BAn"¥Ãœ½b}@b;MF¦fƒ”ôˆªOœL&q;ý,®ò°H2ìÜ7ÇÓ¦«I[sÖŒ²E…épMÖL‰I¥·¾_Ž\×±A}¼mï<Ïýõ,Ò¹&¬J¨ÃêZdZ ”¬õ>HNè0l÷ãéÀkŸX¢vꚬK‰Rz®ü&ÑùñE¿Òyž¹JÛ{›J§ëÒÂŒ×SwfºáKC׿­Îí4¸…éºË&’¸ñy%›½v“ÙM7¥ž“[kHnZŠœºædêÚN`7|»i'°à¡B×uw±r]7Wãuµ¦ëߎÇõé´º\–»Ý†Oj×Çãêp`sX¨ r©ÃIÆèPË=*NwlÄ‹ØãgàŸœi@˜#w•lTqU†ó0® Ylm ­²Ó?¡™nÜÂO!©U\MG}@YZ”Ošˆ¡ýÃÂY9ÿé1üÇ(”cEt{¹Ä¦ÄZ²ôX^ÖHbœâ€¾8w±Pžœ®]—°SÍJA8¬VE²æÍ”±¿â¹0šsvÇÃrX~¤Á¶–“•ÉnZÒkî§F‰²Á þCÀ«ÚÕ™è:(íŸÔ‡°;¦ß9ýË5;zßî^­îBãuW {o3¶ª;GOWwÖ8žga"!ºŽ,O˜ ÃÕ‘nÉo.Ot·šèØdõõ•_.Év}߬Á'›uº]eà·ÌgÛU¾]çÂ;î·ë£”=Þ< RÎ$åÌSØ^»vßL”ÆJæGI¶íÁCD¼®»‘žÇëæþx×u«Ýn±ÛÑ-`* ÿ=Ÿ‡ls˜Vߨ{Qx¼NLmp¸‘IÕu¨¦$å\hÈhKGœ«”Õb¢|Z‘É%†.¾ÇÇ~®OEuúju69íârœŽrK¼®[zøÐÓ”»c ŠÑE‰tqƒB€Ö·‚ý5¿¯˜.¢Úït/5ÝÕÊKl•ê1¬[ªÇë 3qTnXÎæ’‹üì¯ëk.þZJ—¡:]ñ5ŽTªD‘¿Àf¼´ u]êŠ×¥$^—X¦™¨;ÜzbwDÝ(ºu—:Õ]ïm*îIn©ª;At6º³©;ÿS.’t(=ñ¬D¦Þ_§ÝkÇtÝ×g~»&» n©ð°eûMÆ}¾Ûä̯‹=E‡×ËôœçÏE ¼´m*|ºßòƒ‚ß·ëç°AñºZêºår±Ý.6¶º;Ëí÷°Í ÃårQtTw˜œÖZ]Ç}KnH} N<ÓuN8u‡[Ê’j°sã¾-ÆëRo¼N(Ï–$;ãé”úpâöÓ¦F‰Ž%ŒÚšÞ©5$™DR §8NàLYµ©/;JZª7)ê´†Dö¢¸è:,;Y¤Ä£˜º¶ÔWYµå²VyÓþ•' lÓu"[·Q,º{í*TJÀ~¹Ôuþêâ{5!%©ÜN¨ëR—+‰®c˜‘Շ؋ªµ–¸Juªë:6Û]] u–—¹i×doüì’²–\×s;ñÀxÝP¥çñѨ»bÒ»w4Ø­ÌöߦB©ÆôÄŠm{SqQytKé _™¥ôèˆÝásaj p L?oçsq»åçSv:° ˜çtȇâtà÷ÚñíÌ|y>”§ý _œö¸¯,-çÇî%†yæù 5Ñ^fb»¿®ÖtÝ~¿Ûn×»Ýf·[ï÷­ßï×‡ÃæxÜò {¹6ÑÅ‹hÔNÆÇN9'®ãÜûNó9E½Y̯§cž$U–ViRÉ7Ë¥Âgü¥sÜ7ˆÙv€…—eV)?V–Âq¯§ԡć†Äböç& ]×Õ,èØã1^'MRWcÛz·ûï©³Ý þTEm™rZ¦¢¥2 -Œ'&Šªïî8Í“çd;ÌYûfy~¶Í“ë^}Õ€å9YÿKÆ?&Öú²ö`m3§®©åÍ'Ö7“L¤î<ùµ©«UÅ% ¾Ê”Ä‘ÇC€ö8Þ §-¤®«LºƒÙëu×÷T…I‰nu§•Ùù‚ßËn»_Ì׳¯Í|¶žÍÀ³m1¿U}»Íç¦ÛÜŽe X&; úÙŽ^ázA%üw>Ûùóõ–$IY–H ¯‡l™Ü‹ePœû^8“ ½Dç{ºAÒaô£dæÓ ¥{_ ?†äÆdL ;~°¼fJ!@Ýù^ðý>·«7yöµêÎ=µ­Àz'­cÕ]_OLêmXmyBŸÀv¼4HÝ9•žëw!ᆱeݽX~Zþ¹¨ƒöþ=¾ ëÓui^‚Z;œÎÇÓåÈ}‡Ïm ÛΘíâó¿¢Ðn‚uÚ`" #«B*4;(ËEbnú¢JÌPeEçÛW6Ýùºí ï=éEÕU¼9Ÿ]u`ç–y1¥¸>E§ß2•ºóÞ_GßTì_µ+º0uW„©»1ïijÒ]†·øöéºÑ+³þwßÜ–¬Lrw§ ÇϽXžéº…S×uÔgbÛ!D×(‹nºëâ{]µì°îºËuì¡5s3TYè›C^éIî_êöµ^ Ëu1qö ly²ãµNߤèlêÎ @‹º³.O$O)½¡êN¥8c+ˆ.U&°]Ý Y™ Rw}¾xîñµÛüç°]×ãõ_ùî!-^$ɧ´‘”F¿G®çÍÆv‚BVYiR–sK•oR”+Úäî¼rwº(ÁùazO‘‰Í7ÓEuAaV_Õ®=Æ®Æè^©è¼vÉAt¨âÒVË™>Lé=õ´…õ¹ZÝQwA÷ÚUwáø9ï'F3뺥O×9”ƒ¡"t¯EuTrkº¾™ù½¾–ØAe¡›ªZ©jRhÐEç ½Ø?Áãz”F<4èÍwš‘¯?Ðè(äâ-Ä•ŽÂîwÛSaïµóß‚2üÖâþûñÈ-(7«Æ#“kO]Z4œî4Š{X}»¹•^ºBS“©µ¡*ÎŦéºÌªÜÜo*¶ç±*½áŸ×qÇ…*ƒ úÔ—›²2²ê4·P–A_Ý+²4¥¿¹ï„7¦'™ÝßÍtr·•v£©”O­¦R¾Öÿ¥°g°u»+4ÔÆ¿+·‚·…ô¤Oà»Í}—]¨ºs¬Ì†©»NÅÑ[S¨ê3u]‚t×.ÅŽQw“Äñ&SwÏ)ÀpRuëºq1ûw(z°S²*.—W¾º•™´¦R\jWeô0ç÷^».nìT—ãyFíŽ)•…oúýWö­ÓTTeÑ;Ü”»Ý¦ÂÂß;tWÿ¤¢SÕ“ú´‹žÛŒ½^ÂBù.íMStý’)º“뺦Æç »!·‘ÛHZ›.Ö÷”D]·°é:Éd#•žz‹ò€¯bY?Jè"®*Õ±þ-WcRi#4û´±Ä”;Þí“Ò½]«rs+ÛÄG‹€™jŠLÕ‰äxâœÐTÓ¼U1ZÞ‚bãuøfÅC}bë&Àö‡Lê:ðmÔŽj<º=:ÜÒ`ØÓNÕ×\u Á¬ÌRl£YŸ®#äSñOfW ÷-~¼“òŸó•‚›ãFqíÂíVØùž½ž·%÷/#ôÛö2µ›´x+#yúøû<{uE°+ýg0¶’Šƒî ‡@Iö5¯©ûÞQ‡×ÞÄúó=Ž)ŠköúÚ‹_µ=^‚5ug‰× ¢»Þî›Ý~»;ü„?¤ÿ^üJ¿øeø;¼»ÏìØî­ßSܽÞN‡z¬ó@_Àx¹G׉É&dƒæÅ× ×üŸ^\¹òÔ]Šâkš§îr*¾Öò«f¦øÓÖ ÅíÃq+éøÆ…›.Eñh¿Æ®ô‡‡äqåÚ·×g¬kÏÚlg㺘×kRoö½ñ}x ™ãÔ…éw¥ðCçx0И~ÝB¿¿.ãº~>øŽ!±g°Ë+yBÈÓIª^lip…ý±H·XUvuwé©É³Ñ±mðz;IN¶ŽrœÙíy”ú7æyéûNÕ’^ï¸v5¹Ö$ÝßO‚-¤¯vØB2†ðp‰–q‚'dìOÄ3-¥'u]îÔu©ëž÷ÃÕ`Ý]Wz8vYHž^Êraê]éÓ“X3SBøS6ô¼ümõ¢+8°ÿøûd¿ ¬‰éáœ0ÚK]—{âuE¯ºŽï^UÓà÷ð®Æ7ó8;FI†›³‡‡o RW9<ƒÊ¨Ûk½ãº¸®£ßús …÷ÏÂ#$ÿxÂSñLU ]×­ÒÆë&#™¡ì¸p==¤3äW`B¹ˆáx óŠ]Ã'ú—SSžÒ\},œ”ž'ã}ÈŸ}õèºâµºÎߘNL/¢‘Çrqƒ;ƒÓ¦#.e˜éJ×ûÕG1U>^Üøqð±BÊiŒ:Ô®:;ŽõÞì.z¤ÞA•öT— íçcÄ5î\cóUäàÛx]ÕÝv¢ÆëºG®^¯«Bò‡ü"ü4ñ:/ºÚ3ìéSíöÎáãn ~fÿvïhK{:ÚÜeý9üæïWÞ>iöÏ¡ý2~'â ‡Æëا¿I× ™‹kv¦öJRB3»}c¤ ©wòÍàŸô×Ëu}M³§NeÇEÈø 'Ìé‰Ôë{ãu]ײe5!®\yjÂÌLó›'èÚ÷©#^»;’½S¹Ó{Ì54p$5FžÆÈcɯ`ïpnÞ÷ÔS?/˹»ÚÇÑVµ+= š¿T¾<¤¿Õf èŸOø¡cÍ5N;ìûSã7Œ×=O2µ‰ü6Âqìë¼ëÛ[ȰRq£¥Û†³1äïJÿ?ûº[ÚVoÿÚÌcxÓ\} À†öÏþþo/¾1å'Õï0¿¯{1W®<µŽ]¿ Î_ŸÚ±ï÷’°I¶!ø9"Es ¥©Ž»ha(pÜ:×ã-x_Çut\ëÊ’î#¢qd20¿kÜ™yŒ1kîëã/Ä>]×üd¼ÎÕÔ®‹2˜pBžÅû&¥çÌ>T-T`äW¨£õîkšîÈÓ„äyRǹ»ÚÍ’§kék1 í{´ßúóŽ#?ÙŽ"RmNï·Úsî¯ûFf®Œtó—¢ç×ÄÕ¼!ùûpâíîŽJ»«Þi_j!Ã–æ §_šÇO.b®ü£èÈÕæ~Âq¥ëdâ'Wž¡}r¼wŽ5ÿØü1ܯ{ñs“ø²,“$¹G‹íÿaø•êA,¯Ë ]‡ö‰.Ïó2Z´hÿƒñ.é.+þ€®KÓN¹ao0‹-ÚÿÂ`¼Ã¨ÏÒt°®ò<ì´ªìŒ ‰.Z´ÿ›Á¨‡±Î® zÝþÇUœé‹¢øéV-ÚŒýñº®f%Ð÷×åo¯‹\-ÚÛŒÕý~¿\.W«÷K‰ÇÓ…Ž×Y¿7‘ëñ:¾»w¡ùëëKK)ýûJüùù˜ßä:Ø×Ó>×ëÚy±XÜn7LIÓt³Ù@%ç³Ùn·“3bçó™–²QÿäÅŠípÑz½Þn·0ŽŽÜ‡ÃétÚsƒ1ødùD×õs…]×Éx]iÑu½, $ ¥3âêc] —æ7¹öu5ð4~Í ÀÓðÛý~o¸Aà=QÎ×zµú"\÷åeÑhÑ¢IËóØ FŒ)” Hz€“$ôÙlæÚ÷Ê­7Æ~ Ÿhñ:·®kr=^Çww`SÅI¥uÛ¬×ð_8SÎÉ,Or¿ƒÌ‚D¦µ®WÌ/µäÅë9®G×QÝ…ø-Ï3y-à¿f;ÏfŠZÓtëêD‹M°PŠ:¡Hq8i‚a àMÇ>e¶Ë傜@"%‡„ðÕu¥¯{ˆx].¾» ‹×™*N*½ý~w:«ª"¡MðVW»í÷ý⻌‹×IõeÒå1h4ó—JjUJ#*q>‡iî ijë½ÚÑ¢ýO 8 †Œ# 4ð@w0ºå_aÜAúr¹ü÷˜ër¶ûfx0æ¼3¢ûü„Y0Í6"^W8u]£êºÐx×Â<ÀX=ð@˜xN¡•£“ÆëL]§EØÌÙ.~¹µ4j¬é¶Û,Ë\+Z´ÿ­€A h Êä_ϸ}||x ]4ç̉ԑ–gD¼]iÄëäÂÄTñº–ô8ÙI Ür8à\ q€À«6çñ:«®£KT×áÏ þŽ(¥9•v­)ÀhÑ¢=¸®ûÇí“è¼Û¿æ¯J€1 탛¿ 7ØFx“è£âuRוV]WñºŠ0§;B3âuL×ñêrÓö½Ýn( 3äX#âuRŒ$^‡ F…ªè´Ò4ã1K¸/Z´h÷;›u"ãáÝþYžáT0ŒÙ^]÷àòf|ÀrÚÔUÓu}ü ±?^'ƒu@z2^×kR×™) K¡Ú@ux ˜--SòÇZab‹‰ {¡YBç‰×sâèF¤,\‡ÅFج×sKÓg­íI繻݅dç°Ñ¢¹ F‡Ðu+\x…I- vnxÉ'^'  Rר¤ñº^St]-uÝu]Á]Q‘xßËÏ¢Êýu<]*=¹ËD”˜kƒâ]ˆuØä~Ç}¡¡à¿HAlÖ}\®K9yB9мRãñûëôþº/ï]sT×±KÆëϧÛúl7Z´hÔP¿ä€°Ü^` ¾®ë-?pöWyãu®ëݱ™ö6L×}³Åç&¢E{[«øíŸÍN§Ó“…c¼.Ф®+Å4öAâuR×q® ×}3ÎmA¶hÑ¢ýyƒ±?4^'u]ËusS×5up¼îyÃ{iøtRý™ßs-ÚÿÐä{N꺲Ö×a¹¢Ã ¦°4^Çý{`|w_¤»hÑþ?†ï¯ ç ©ëJªëD¼(Žêºw×U-ÝeQ´€¹ß‡ W‹/ð¸}]æÊóL9ÏàòC,༊׵¹÷ºGìà b Ôu×1]÷hu]]ri×´ñ:¶×›(º¿†+_žÊ’Û¬M\éé–üF»|HWþ¡ÇzÜ€öÚn!ØqM#þNLu]ÕèñºV×U­®û¶x]´'­À!ù]yžÙw*üL^Q·hon ëšP\#éîAâuÎBy¨ëo¯‹Øƒ+KzÝ¥(¾väñâùz(ÿ™}Ÿ©³·þŽvváwèÿgŒº]§ëºx›ÆB¶·×ESmXÃjõ@<Õ¾SÕçé:LІÑÞÊ„®ëèŽÆë˜ÞcK´Ìs]·u—‹ø`SA½ûëS LŸŠÆ#~ º®!º®åº9Ñu‚îb¼.šb!«`WÊ^b®:¼CÝ¢}£Q]Wº®bq¼¦êt]ËuïÀÒÿ^\ä‰Ê*âi±Ôur£ñ: ¸ªÓu1^-Z´ßj¨ë*§®{p]÷àMÄx]ÄGü[1Óu.ÅÖ|£ñºV×qºÃgÄÄ~4ÖáÃ%ÚÏßeqÄÿÌé¥ÄER×IºSt{­®{ Œ×!ÑÅÇT£E‹6¹uÏÃpQ¯{´Š®Óu"^'‰ŽÍaÛx](‹Ê¥É÷ÌG}ôÑOâ‘ñðQâ@u×ê:Îr¦®«¹®Cÿ¯ƒ:@üÊÑ¢E‹6¹!½„^‡ê:¯“DÇþ¯¦Íù‡™ÁA¥EqÄOü«½ŠNÆëtûÐu؆ÅëÒ4e\WG‹-ÚK¬áƸNܯCE×é:¯ƒâèð¯ñ:ÐuÀuX“gþˆ#Žøob¦ë š^E‡&u¤;S×5"ޝ“º®Ž-Z´×H©Óuiàj,èºÑu-×ÍmºŽÆë賄Vt·Š°qÄGñ$øÁ?îÜQ——üº®Mìt]è3bQ×E‹í¥&ãueØk¤®“›¯C¢ãjãuôn¦ºN00ó¿'†Þ²Ùl¾¾¾f³Ùn»åßþÖ:à×â¯×ëgÊ¡_ Cü>íñ´¸îâue/¡®C*k\ºŽÿ ãuU˜E]÷[,Ïóù|~¹\x¿(¯×ëv»ýæ:|2FšÒ€·§-0ÚZ¯KÓÁºÎ¯kÄò„x§“9x]ý>¿[ñn·;NÖ0!]¯×5_áM’DîÛêºtÝ\Ôm†ƒâ®KÄxp¼Î©ëÔu5Ñu1^÷×,Ë2ìXÓÖa÷ûýñx„žÞ[³ê: ag11k¼n¹\µ2Ãnš¦Ê` çµZ­è¾X àyYøápà´Ï*¼ßïüííÇmD¼®ª’èZ]gÄëÒ¯û».Öš¯x²ûëv;¾èÉÒ‹¢ÀôÍz-Ö/ìÊJÆÇj%vWmÂî Î¥ò¸À-³/\‡ÅUÆ{@G¨¾d™e»ûÉ—q ÜתÍó¢õá@™/wûÞ®W¹‹éÚ snüñë±×Ããu•Ðuèñº:Æë¢ýJƒŸþ%»ù¤’)“ßÑíûmL¼ŽëºÌ¯KµxàHÎ4q§ëjFĵdãˆ#þvŒêô~¿ÑôN¾M=#ŒE¼ŽÑN/Q]çˆ×UF¼®÷õ)¥¢ëDÅ"Žø›ñ’®ÃÒtE×½A=#E¼Nêº^²ë:-^—wñ:?sšï9y£_ˆ#ŽøáZÆëøGvzyé‰xE-ñ:Q½ˆ#Ž8âç1ÆëR=^çÄ=ñº"Æë"Ž8â·Äcãu™©ëb¼.âˆ#~Wü²x]ãuGñûà:Æë"Ž8âÿ¯csØx]ÄGü‹ðèx]éŒ×¥1^qÄ¿¯“ ™¯Kc¼.âˆ#~?\ˆ×Õ]÷Pu]¯ë}}ŠªëjZ½ˆ#Ž8â °¯ëá%-^—YâuFíJ¯+Õ¸œË£®ã5ª¢>úè'÷u¯KJ¡ëüãu)纀x×u½,Z*ºNT¬²â ÷¡‚Ó›ÿ1=õ¢üîZ«öÓõù›øôÛ_ŠÛxÔu}¼$t¼íD×åöx] ®eG‡˜æeþ¿ÈÈ C|&]¼¹wœHI¿Ÿõ=þ/µê;{Þ˜¥«‘ß¡†Þ×"^7@×Õ¨ë,ë°étñ:Q=‰Yh?á®ë9â ŠÖ}ß “iĹtçe¾rüuX­L[­Uù•z‹vû¥XkdÏŸ:ÿIü‚x]«ë&Œ×EƒXMÿ… ø$†6”ÚÉs"Oª»ÀúmU×±ð+Z³Ùl³ÙdYöŠvû¥xÜ·–™ç94ïŒÛn·ÑÏéWÞÞ¤}^ŠÍóÅÖ¯ËñºDèºIâuH¢·°ßĺ½-PÇøûX’/°óôëz8Q¤Wý%jjäo}Hýi«úëãoUy,Èp¹\æó9ÒÝä­÷}ÕÍGd{ÖI’\¯ð´ýú¹Ès 8h^¼¶Ûí›ôê·õæùÖÏÅë2S×冮{"^Çÿ^`g?ƒ–©Ÿ¤>Š qjô4«ä~_.ÀöËåâv»bú—Kž¸ÄÏ£Tüár9ð…Æ_Q«”3Cg6~Ò]çÂÌ¥¦>ùg_ø—eÆ×¹Ë5•¦X´ª]uh¿žçóTb¨äjµÂOÏà›~ñ¸ O êÿÃ}áŒOÂïå|{]Îç™Àx,Ø òÏø^²&Ú^“\£i±zÅY{û{óñüáp íŒù­å@“žO'뱌oòÚ{µú[_{Ê–?«WA;.Øa¿çfü›GíùâÅöæÊŠš3c½±Å³x\Þ‘–Ø‘øW,ÉÕŸký¤€.»ÃY›³›fl¼.¥ºNÄëPÑ%ÓÅëÀ@'°!YwJÃåq`B~ÜQ+ Z p’Üwü3ÿ\;´?ðrâò¤P1k5ÎÃ4„ê(¿‡œøÅy³è³ò ­ãêÏ+“I…ÙÓªœ!¿µUµ_Oèí³YûeX¨ÏZ:ÞB|ªF~ú'~|°â_é‚_ò/0Îf_»Ý+¿_b…¢N§ôð{þ9ElÜ‹åå¿•×®8ÿþŒÅ%8¶­W²Ûû¯8\DüF¤¿ý%€Nnö ™Óßžü*Ìð*\YÛÎÍãBNìcP±(.(/¶bÅî÷ò¸²Xàœ€–=³÷¸ b¡·`¸ÐZŸ¡WöÅœP7‡®¯«1^çÒuÉtñº–ëú¦Zr‹\g– r:q¦ ÓYâéD;RMâ]üBL»£'p.í‰ðBô2á"ò ¯¿Z_{Êa([U«Y¾'©Úï2 =\èÕRvQòÙÄ p%쫼íg²á‡›‘n/Ü~Ìz’ë5–ܶçývÛlÖ[=Âßï7ùWígš–‰MÚÛþÓ^a½.aíYyÚvÁJÐU¨üpyÉ>†>7«õ̡ǥ%Ð>Cû þ*0Q¼®ÑuÝÂÐu9×QµâB¾ç;‰<µŠ¿îI ººîô† ã÷+­œšGwá‡Z~Xñ{îÿå)}Ô>ª>ÑŒ•‰Í2‡àz2!ºžsiøG{­e~}~J<®þj«öÔ§Ñ[U©V>ðˆŠ¶nY¿È 7ufÓ¨ÏO>™ú”ûНt91ËÙV¼ëØ´(žèzM€Õ+ÎFL(¶ÛôYÖ]qÅK³þ‘[e½Öÿn¸Ú+*¢s†µ§µmåOMD¤`ý¸î=nÆ;›øòNãé'_ŽcU-Ýa¼Nêº^¢ë°µsëºFÑu_ZÞs"O¿Ó2õ{ðŬNr(•+ù_¦Ã/¬òKD}å ÄödÚù]¾'R‹îDËT>Ü<¶þ0' lU¬ ÎaÍr´òi¼n¹XÀq^öIyÉ2GMÎȃ¥ä\:7ÛÁÚ&?Œ+Ë¿\·ý¦à[®x]™eÂH‡ÌÖcéJI̓]q§¶§µmHåÇeú|6cr‘«üÉÓŽkŽÇÆ>ŸO¸Z'ÏÑÕO„^-é±°œF‹×õyŒ×¥žx].âu•¯ó±¨-^W¡Á`‘«´¬cÔÜÛ0Ê\Z9Øaxh«„_º9‹)±t˜À’û]\tç/‘Yæ ,OÄUÿ“1Ë¡¿hã꯴ª»=­ªÔÇ\‡VÄ<³Ù /tÑÃa/ëÌãugvÅ“DÆyzÙ!çñx„ƒ€ç{éí@ñT×ëy¬\ñºmU»pîàeŠqÅõr 1ÞÎõHí¼Ûá:,Ýð_hdhy-x¯hãu’Êà§'2`P{ZÛöh×a±x¯“ûZqïqg³/Ñ‘Ý^Ž~­}ñHrÊ:ׯK´xËx]êÐur+ãu”E­¸´ÆëD%ᯠ¯žˆ¿Œ˜Óú x»]—ËE·šÃÓ¡e䊕ü*¨ë—È,s†‹ÅÛ¹è<–E5lWåŒrè/Úèú«­j©ƒ$:™ÓZ½¿nË&eYW7¾¸¶˜Ï¯WØéðË Ù ÷ÂU¸^¯¤Ûû~ÙqÝðK¬z~ÙŸ¼FÓb¼ŽeQx®5ú¶oºR’˜¯k¬ñþ: Ù7@¹±Ff+Ý7y­Í^Ä Ùøò÷°ö´¶-g6\‡ãbžï†»Ù¬å¹¸f~…F1®XAåñÇ´àqHW?¡ë°Z‘%¤®ëå%Œ×I¢sÅë’‰âuˆA*àòJí6ø+äÉ1®wÀÿ†¾WˆÎov{Èc¬&OŽKh«ÐVÍíq¤ˆýxÈ¿‘+þ.õÿc¸¯sè:œÀZâu>uÇëc(†°ô_FÞ£@¶adOÛ÷­0*ØöDDW—¾faXöW|à«ë#ZõŠa"=R×¶êM¶ê›´áï•zŹTn¯;yÅ]Ê9â©°5^çÁ]¼® ºŽÄëä6I¼®í0|‡ÞízM’;r²`€!‰õ¥4mgI꾈qÕ®þÔʤ»+;‘$T$¿þªu{³œ)ë­Ê+ÃZõÎ[•7+`¬ ÆIªÊ^ŽÖž?Þ¶ï‰õ+Λ®»íŠ+ßÚþ?~¾o‹ëQñº„Æë#^—O¯“¡ÐCî|$¢挑¿¹¢£4^ñS¾#Ëðç}žG§)ѽ¼>!­úíö«1‹ð+~»µ— Û+Ý·àñº’ÄëR¯kt]7U¼Žb¶Åü>¢ºó¨ ·Åí‰í©pAUàÓm?R^lÔ ÕÝ8¥± óF.Œ+^Š+þ.õüø¯ku]aÄëÄ 'ÓÆëLÌ|UùóüŒgñçBkòõùƒØ¼âïS·?GÄ똮+Z¢K‘ëzâu%šJ¼Žv†è£>ú)|­Çëzx©'^G¸n`¼.é×EqÄƸg{p¼ŽÝvÂdûãu½,Z¨ñº>~Ž>úè£êkŒ×I]×ÇKŒ¾H¼.Uãu‰¢ë^¯‹8âˆ#„GÇë’Vשñº,Æë¢>úwôõèx]!t]Ó£ëèìØê­ñºè£>ú }c‹×y<]‡5ãuw®ëîY9.^×V¬ª¢>úè§õõèx˜ÆjñºûH]§?7ý7ø¢(ØMü—Î"ÁàÙkÄ;®£ÿ^‹×꺤‹×54^‡ŠÕ]Œ×½¹ç¯ý¿$É=M“”Yô<´{¡ÒÝ\Íè{}ÍãuÉxÓuy-éN‹×¡®º‹ñº7÷÷û=åÏcâ³K%ÿ'â@Ü>Ñ,Þýûûfl¼®Õu…=^wñº·÷wöb“ìM¨ãmqQäí{u,t—áëßájFßëëqñ:ªë´x]KwøÍD©ëJ™:.-ñ:^½ˆ_‰ñ•¹%¶0~¹‹XÁ`ø"°ÂÒV~®ô}®iÄÌ>ÖÖê:y(ûxI×$^ÇtStR×m¨®óø¨ë~Âó×¼çÞŽ}ç{Óý~C¡Gó¤ø•®7¸šÑ÷úZ‹×õyO¼®Vuˆ×ù˜³’ñºDÆëxÅÓï•óïäâ›ö•<.<ágqþ$æ½*E¾Åãx8¨Q½d<0ETHéß÷YeõƒXžàöÔp˜È«ÚÏ…×|÷‚}YI| Iæ—?|RB:\ôwøey+ì×±éM§*ÓU¥§("mß_‡ .ØÇ°£Ê_ùQ ’wµ3dÙl6Zÿ¤y `*È´3î“ëPìŒ|~Ú/µov£ïûçñôñºLêºú™xýU‚ AÿÁ<0Ÿ‰{ƒÇï/cþóùlîû&¿,o…Ýñºï £éTÅIugUD8Ÿ}…6 —‚*™.ŒæOší¼ßí€Äp2‚KÕòì0ÿH눾Š=†ƒk¼`ºkvWx¹\¾C?ü6\Š×ݽñ:œÆ–/Ž×‰o—³{bég ³ö³‰1^׃µxbPk8E%ꥴª8SéIE„ªx…6ãëèµtþ‚ú¬Ëï×Éþ Y‹…™ú3°¡}õ‹B/þ™Q-F>ÌtÚ øO÷ÃoÄOÅë’€xZU–.\Øâuhø«dµOò'™íË–Íj.õU°˜[ªÅÜLg*=Œ_!E¼BƒÁ؃`×+Ž‹¢Õº >N‡äùºš÷‹|áËÚñÓÕ2óÐËu€)Ìz½ÒjEOaZ —oè»q]×H]×ËK¯ët]¡Äëä '/Š×Q,~7k¢ë”üãb ÿì¿¿×=*NSz8uýŠ=œÞV‡Ô žÿï‚™ˆü¾xôɲû¶%fࣰ¯› ‹×Á‰€¢ƒáö>ýðÛðñººÓuñ:4;öè:ãWIÌgÇ#ì žÆëd†år)¿¥Í4-^G×RÉ:¬SÅQ¥'Ö^s-ÏïÅСðκT|Ý@Í¿2)ó§n]·ßïAw•<>³ÛmÍ Ðiyl¹<ž~®Ùv»•×ãuô¯f9TéÁŽ@tü»œÿG«mñ:þþx‹¥K¾û¥¬s)ùa. ø+`¾ûó¿,o…ƒï¯+M§)½;óa¬1Ý÷—bŒíÀÉàeüçXÉó_{W ®, …o¼ÑH5T£ÑH5V£‘H%‰T"‘H%~ïv` þ{¿³ÇçÜ÷Î1ösvvöÙ _'‹ãÌ- J;Iñº¢É±…cVíÊxZ“ô|Ì`%È:I_Wšß;^tOÏÒØo’Ÿ ‡/ÄÓùº¼äëÂìlàëâÜ_ÇáyáûwKŸKsJãO†®™fˆ¯›ÎÝqxR(îãë„­»Â×Õ™ôá¬ûž“ªlŒŸ‡ù÷°Wq.Í]VÉN±†½¹ý·ŽC¤(–¢Âß{·>üy _ªäëÄæµèÃa»¤óuµ_Wñu¾Æ×¥àë8Ëýcù\ÊÍí…-–·ðè˜X~M(îãëð)4¾Ž<:2w¥_7h9ë}XÅ×U¥Êß= üyœÉÍS¼o÷ ¾§’áÌÔNëgô)ã!\óu黤ñu¥¹c¾î{CFÞ ,^"%ã)8«žæð¡x_WÈwu2_ǘ1ãÄçùºB˜»¾Îg¾ŽŠÏàëÂ_Ç’%K–•åëü~¾n 0_LJg#_76_˜ø:¿úùÿ|•Š%K–,*_À×±_LJ·‡_w5ŒàëJs×àëeÆ|K–,Ÿ-_—¦cìRÍ×%#ùºT÷àŒXúuaå×éÏ,1f̘ñc0¬‹Æ×]·K£øºé~]ûw,Y²dùPÙàëFúuНKÇóuCVÔÄ×½`̘ñ_Â_—ޱKÝßMð똯{‹Œ’tsŒ—‡„?üùç”DqÚ«ó·òuÁ5¾.e¾î³1 ÝòC=ŽAz R–,¿]nN‰}æÎ¨óÌ×ý·Ò9ÅŽ›† þ¹Ð]Ƙñ÷â(-0w;nbÔùóu ]Æ|ÝW`øün˜å‚ÃàÀá/˜&7ŠmÔù{øº€ùºo–P /*ØÖqø3!?_¼(·f¿î)|ôûp,üº(g[Çáõ#}»Ý®V+Û¶¥°Þï÷7g eöj¿®­ó¯á벦×ÂY‡¯+Ë&eÇ›Íf.Ú'I’nš·cu0÷‡”g –~Ý]¶n¹Xˆî7˲nÏv¹¤l¡[ÇA¿/óÝnGký$,|«Õ½¯“ÑÙ^2èWÝP’‡ä3О)äò¸4:2òx<Ø7’èôþí9Lßö11ë|Å×¥•_7l—ȯ &ðu£^gÝöëÊ7¾f°lPK×=eò×u¡«­4Ÿ€Å¹œï.ÃT|§_4³ïÛ›‡?¦]Ê6—ÇIû¾w‘“`”ÆjfŽK0öÝWÅÈ ç¾ïŸeØí¶·²¬Gغáöü3#Ö £ØóDÏÂÜ¡[añ€ÑÅ4õ]K§“·#]WE¿ªýºŽÎK¿îR)5Â.Måë†-g—¯“Å«­1µCõ_ÃJ£<õù°i¹Ë ÅvO§…ð0L|´äœ°çѵHHÄ Hp<*Lù#%ÜiKë‰ÆÏ›W!·@¦DH“d½Z!M-NᔑåùžZ9?_‡æ!§šÔ€Î¥Ó^(ÿRdX¥D€ZBo/Ò˜ õ2`Ò¤o‘ÍÒŠ)µH‘@öN#Û͆²Å,©Í¨ó]ƒ¦œ·¿–¤/C˜>±v°,HåYéwQ¸[/ýÄUÊ -ƒ!UVøuD̘öD€“½ZV¦^倛BÛ‡KhÌ¿¯TÝ>Š£h%/Çrú?Ü>ú,CÉpTë"gŒ£–›Š» ^¸ÅÀ¨§H½øj`E@í¬w"°jù‹‰¯Óu^çëÆØ¥Ò¯KÆðu…0Œ#+Iåùé¥_×´Æè…$1[i2ƒ™mËì FéùÌdô<6ú!:Ö³½:¿+¾£O÷UÚéwùTXTDä²²ü}~Ýp½ç¦CK¹1WÛÓÈÚ!UÂÅ` ù7Kµ¨ ¾…AË›“B_û¨Jé>Àn·í®Çi­´’Á–¡å¡á.H3`ë.r:^H—˜…œ1Õ·]¾N7òuÉ‹øºÚ¯ëXéʼ4°Î›q™²cRÃ2ŠîTÞZϽ¬¿Áשa’IgUÕ½ÔŸ<Ǽ‰H,ØÕŠªm¼ ³¡0YEÛÕ®ÄEú'½~]??6ì×õeØcÖnªWi„gdŠ»WhOƒM6O(¦Hcþe©f½¥šu.‡ERkê«í£šú»fÆv _ýþþ’ƒGö‡B.øŸõ ÆlU€“IÎ!dkÓöI|ø<¯Sž0&©Ü4¿.¹ê×Í:ZF/ŸNGi`³á«pÓÒ¯KÓ¯õëÄóuJù1â ¹œÓ»F}D.¾8ê“ýº8ŠtÕÅ-ºK¤îµ­€*è£ ¢Ö†0ã~O†Âo‘¦¨œ±›ýºnþFóÕמ(À_'JXÝvóSªn áÿ¨«®¶¤_·3î³ H¿2À ‰*ŠÒq6„ÑY˜àð­1[аƻíö(õúW:_× Î×Å’Ì´˜8q½Ú‡­ø:T—Cê|ºÖˆšŽq;òrÑY˜¹²~7Ey`”_Ë×é~­ÊÑ hR5k£ÿCÊOOþÔä‡×R‘Þ’­l‰š z¼½ÛãQ\$‡#öª¤¹C˜úÁŽH±µÚ>lÍGaJ½Èµ¤²·0´i»“ý;P/ÜEX4™hÔÙv¯ºÚž§Š¯ª¥å@‘:cÖ(aµKµ3”Ja\N<.WËUcû½ë¾Ž²%ß&÷„{¶q6HLkŠ«~Ý¥Ò‡®—þD¾.y:_‡€F@{¨çëÒrI+h:4Ô\îæÚ>¬ºð6¿Î›bsœÆ¾”øJLnf©üiö+‚z¾NÍé´e@ÐjÅÝPûzPTD¢Žº‚=į“[xl/r`ÒÆ CÝk» C;é(gk¸AO6›5éƬ,å>ã¼±ÏHûžK¹G9\/(0‡v$¥åÙRs)3«_uµ=/jVš2•.D¤¾[–pÙ(a+UAŒRé:°Z.ﶺ#šT%£•é¥gV5õ)êâlín+¬rÕU¿n |5_Çøy˜7ñEáQ+HÈ%Yôk jSõ†ðL¾®x6_ÇáyûEážß¡ü?á«ùºIXÛišñ yþa츉ã¦qv~û«x_ÅB{ß]†ÏÇQZl½Ô9ÅFÿ|¾ŽÃ“B”¤öQ˜;/.0²dù톫•8ÉŒ ÿp¾Îg¾î{0ÌÝÖU¯°Ö_ÆÎ˜ñ÷a,U¤¡3ë<óu8pø“ø:ÿ|cÆŒ÷áÏâër=dŒ3fü(üq|]¦Yc–,Y²|”~Ýt¾.~*_'‹WÆŒ3~¾•¯+úùºLñu››ù:–,Y²|¨¼¯«ÌóuŒ3þ|ü¾®öëvÌ×±dÉòä]|]Òåë2ec̘ñçàGñuÖ|Q¿.•†.…­+ý:æëX²dùò~¾N÷ë`â¼ÊÜÁ$næë3füøQ|ùu^˜ÂÖyä×eÌ×±lHñöæÑéï¥yá}ï—“J~gÙØJ/Ëù5zÕ-ÿãø:غ³0t! ä× [N…™¯û°þÒû«éµwA¿ô¾÷cYò•mÒ½&á¶ÿ³±± »å_7GFn˜×~b6…¯‹"òë2iY>U¦Ú1â©ìˆVš$Iè5æ§Ó‰i · ðéè1HÏs)Þ÷}ÛFä ’ÎàËÄ›·gòHqñ t$.§ñ¾t&éFž(a¬J®"é:ý†íÜ,‘Ûº?7\U§Ž{%– ôµdë}‰iZ¾F~SÐ-C·MôZСÆvnݫՃ­vèÔ]lÏçèâ£:WÅØþò¬5­Ò*ʶ\.ôz˘$-¥7 Wz¿(ŒKèHnz}ùçò8ìY+”­Û&ÒÖ…¶h_'ý:aë¯ôëŠ |´u•ÎX>UîªSè|™nqlÐé”És‹ê£!{$ô- 9‚­ãP<‹#"}_œ|T dØ:R¼OïKƇ°~ú®:\i·«"Q5mØçfl”A­žÒÑêóúhõåb  zÉ‘•+oá{²2–¡Û&tHVÞ¬…±ûz§¯êÖsª/0Ý×þtx–]}e ¦ÔUÀÔzzJ§*­Q¯ú®" ‰ míHz½ü45ûîUâxúNË_ǯc—úù:é×±2w¯sÆòu(ÀY.`5;œ1~ÇMʹžÎêꦊ‘oSó=”'fÞÃ~Ÿ$±¿\, Ïqëé뚈Tý +‡5¸´Kuý…Z¨xáìU&¥«?0/X<ÆÂ¯{_çËÇN&=_‡JÏåãu²`,Ÿ(áqN't+¦lb]Zi0¡Ÿ$¯B¼Çpn˜ÊßGntª¬ÊèФ”:]|uËÃx_ýªò0ô4U‡¡g’§*OHgŽn)¥m/iÔ äfl“–wÔÅÆ2À•UK­2Û, åYÞÃePXf»Ï$µ«®2¶³~/]Û¡ÑzŽCõE›·ª:Ò‰Õ´`4–!— bÁ «ØÐŠŠ¯ÃŒ0 Wt3L¾U%)¤—«•²Õû‚šk–¿>žÞ¤·ä׉%öøº‹$ôˆÝ¤çëÄÜ#üºÊÐi7eù )„.Ϙ&Ý@¼8ŸJƒî ý,|«fU:ݾ››ç¹jÇÔ;'JÙ^âÚrM¦,gí6ÊÚcѤ>³c ¬éÌëÕ*­X jÿqVî?Ê”‚S’'/·sÓÊ`l½ Fl,C$),jODbÔSã‡ßjIc›´wQûÛY¿W£Mí ßKnYŠ}Lc_èõE2¹Æ2d2ÁLn‘×ZáRõ WÑéó¾ç©þmIO¼_§ü«rVk·rVË_ɼ^Ãfy¾NºÒ¯ý{Xá×U×eµ5füfœˆ‚õ9åaüp,×°ÖùÉŸþMz%Ž×‘fó¿‡-$žþ{X@Yº\»Ë·HGnè§ri¶²í·—‡åÃ¥xfC¸a)¹}ûH™$1ÜZäöáz•ËÇNJ¿î‘¿‡Íȯ£eÚM»8“Îgõ1­xŒß„±¬ ÑAo/ã‡czž] ›‰Öó±EXú¾7&ý»õJüLLøuù=¬ôèèm'ãß_—I¿®^ÀŽ(cÆŒOÅÕ6»¯ƒ_wöo}ôë„©{•…g̘ñ†eHÕÿ«oûð¸÷×M>ožòëÆƒ1cÆŒ§â<¯üºGðuøÓù:²uu‘X²dÉò±Rùu8o¢âë4¿n_Wýf° ú¿Œ3fü,m]BQZü |Ýåf¾®~-ŒÏ’%K–•ЬÓbÌxà|Ø¢ã×9ãøº,Mk#<2=cÆŒOÂÒˆ©ä^M/–¥ù¾nêóu§ùõ4Œ3fü"ü¾.ëÈO˜3füã |Ý”ßÃ2f̘ñGá1|ú=쾎1cÆŒ? —|]Ÿ_w;_ǘ1cÆ„øºî>ì4¾Ž1cÆŒ?_çÇù_ÝÂ× @Ê´–i-ÓZÒoÍt¬ÁÄ(“Z&B&m)C\˸ƒ…Œã6–2îàX…¨–‘QF‘’QG*„‡QK†ŒÿnJMLzRÊ>íjj`t-mh²AÏ›c¡!õ±ÓS©.i &†±™&íñ[ëRf œÕ6¡a1šöäŠýy-_×¹.é1ã\ÇòEJæºl„B—òu -Yè²?ÈÓk‹s‰Ï]‰¿Rî —ËeXràðØPêÕ5Ý»ŠZÃ;ú_‘ž+©»öxÔFkÞÁíñ®Y€¶}p¥JxÝ.]åënxcÆŒÃ×iï%f¾Ž1cÆ_‰ùº³2t?_ǘ1ãoÆü|cÆŒÿ<НÓüºÏ±ÒŒ3f<Oàë2æë3fü­x4_7í|X·ž«ùrœf×êËøíX„Ò™çêØg”ùÕ¸2etc_¦Þt¾ŽŠUÅûDz|8Ÿ ùÔt|;0~5–Š÷½7ž‹Q:ö'Ç×ø€þMåi‰ãuc4_—Nãë¤ùU¥ú3’BuLÛˆv`ür½£ˆ¯“£t쎯›[é|ðèÒø:: ûR?¿]èk·0Ê`YÖl6û±ìŸ•û³vòäÇÃù¼£R¨ÚøÓ‹¿wïmzòT›4¾þ*®Z)©Oâë¢(jêÛPXm¶Ž³ÅæTʼ‹÷~²:D#3|j  %ŽãlÜéEŒ_ˆSô‹òv¾7\Õ±IãK…sã„Ëe´XD¶;Î9ÏŸPƒW„º•È ÝÉב_'åx¾îu†ÔúoKûðz½^ÚöÁv»Ÿù¶ü(l9Ár çó*,9 ÃI\ã—à4¬ïþ¾FŽ'×Ë«_LŽŽM_„SÏ ,+Z¯Òý>;2×Möûhµ f³ÌóÞ=¦nÁu+ãω¯xÏÉ |B¤©\zÝÑÃá°Zo¬¹=_íìCº÷3”ÄEa¢ôì¸ébŸ,Éz çóŒj¡j˜X«fü¯æÇÂH‡çrw_#·Åj·;ËÕVÙŸѱlâø:—†n–ìwéñÚv0Ÿ‡óy¼YÃÜ%»­ÿû s÷â:ÞɯC+¥wŸ«ûu“ø:©raY ž·ˆèòpØÃÖͬlÝ꘢ü‡ ƒqÃÇ‹‹¼¸Àoa_³ÙìjÌøo‡åyâlÂøu85øu7ÈLº£I¶=kg?k¨ÍfcYÖ%ĤѷÖl¦pžçøÖuÝûulêøB}|xtŽƒ«gYÉé”Ã8Äq4ŸGëu¼ÝF›¢æÙ@>¨Ê?—a»Ý¢÷´ð°ÔÛmT+[a=‰¯ÓU&L~Ýl¾\¬÷^$Ìõ),ìcŠÏúD¦/†­ëËšÖŠ1ý÷í¦Õœ;r6aü:¬üºËØ>u=¾ÚGNËõnïn˜xQ²?+çЗOš$‹ÅÂs]zA‘çy‹Å\<ñ¢M©gñ IºZ­P¶G騤ñ+,æÉv C‡êIË›Åö2v60Ó0wáf.¡ãô僖Õôž Ó·õràÍ1ºÁ¯£ùÆm³^Ókµôo1ƒ¾© ªˆ’?c6aü^¾n@ÂÐ-×ûƒ¬¶Gü!‹ÖíÑ;xáÑ wGnoã8éËCþt:é1øWØÒ.Kø'(´‹ž…9"†ulêøò—ËpeûËEr<²’Ñr™næweüÅ<°—ÁrÙ—UÓøm¶mc|-q—0Tã ,†,$PJ„ý~'"çsåߢ:åðÜlò<ÓÛmB+=‚¯S Ø»ø:½x&\ùudëòõ)£ãæ(ƒòßÜ9F}ù ­ZñʯÛívž¤^Ñæ0ª­ôèDºn­¢WËÌ|ÝgbÅ×ìG{½ƒYƒAÃÇÞì÷‡Órå8û“ˆ9ùøj±* ]_>ËY5ER<Ê€HåŸxž‹Õ${u,ŒÑ±lâø‚;ÀÖÍ­¬üºåæ°9å7‡S Âø8§¸/4Žx>Rþ!LiHÑÈËåBóë šAä·’¢Y,†ËyÛlÂøu¸æë®÷£h‡Ób½ÛìOÎÞ…e[®Íî@Öo{pç¶Eñp>Õ¤YÇ“R•îl†A õÆL:¦<#ulêøráeÍ~½Ù¯àè–‹xn%³Y2·„¡Ã²4M$øýqK–ÌhÃ÷ÂøÒ]ˆª1 …—åHl\‹%.' ¬ÀÄQµÛ˜zÊ×)Cy3_§Ï,«}Ø¥½9l½Âñ È_$9V¯…Š­ëËA5r7Æ’AšAKÒtoëů¦¢W%óuŠ;~ÝU¹Ý0q{ŸíÁsðÙ{«íano¤¡»’ƒîבD–‹EK»°@dÝèR]Õ±Iã˳mwöã[¿á|Y¿ÉÏO:û=ýþ¤r½“º§Ðúõa ¥WfÌA9 Ýo“$ÙnxXœÖÔ6µ‘8k«ò[›$Û£r\+=ž¯Ënä몡èÃä×ÍæËõÎÝú°rgºStöâ3aHÄoݤ/}¡øY5©„1=šX²m¿n ÌSgÆïàë®÷£Â‹Õf³‡¡ ¶Çp³;ÍíuFc®Ý툯«ã‰¯#L¼S!i@EÙݯcSÇ—ï8pêÜŸŸpþýü$š¡ÜÝbþþx³ŸÀqúòq‡¶nþ¶m£ÊÂ& ¿®¬¯Ù¯[Ö~Ê&²Zà×ñ*Ÿázåc'åët¿î¾N²“_7_Ø›{.{ù9E—c(€Œ9CîÜ´/Ÿ¦_Wh1Å~·Ý”çqi¤q=ƒ$‡€þ’ÂP9o›M¿‘¯»ª{¥ßµÝc{ðâÝ)˜Û« ŒF^‹;bë”Â*·! Í?) ~›õº?R¸WDz‰ã í8›Á¯ ~~bë÷øûXº¦§S¼˜Ç³ßÀú=YVÞY`* ç n›ïûtíÃRÄÓoÙ‡½F ü:Á×’®KåH)å_XQT°ÛnÃmõq|]mŒÍ˜ü: ¶n/lÝ!ö ¶N‰Iî¼´/åÅ©xÑ\ o0+‰}¢ÅÂ÷½VzÌ ~µ[ñ½å$|.¦Í&Œ_‡+¾nL?¶ðÚÙ-7Ž0tA8éÚ$Žîóu¤]Ä;U‰=Ï¥á|§ŽM_±ç~`Ó¢_aîÂÙolÍë7ýý Éú¹îp>q­×빬&¼Y”â\VÊ›ËMX˜¬r £9.„¼£ÍYÚ(ÄðÌÊá¹\"+c»õáÚ¯{ _—ÝË×U¥R…ìbòëËÕz{:JwΕ?jDI€)¶n8Ÿ°š‰¦\Ë|ݧâ–_7Q\ÏÕfÄãtì&<š¯'ÌÝAzwLÜ|–Z¿ñ\ü{´¬¸22ŸQ÷ÑøCø:ÙM}ÝȯÃv½=î¥ã–Ÿ/qVûubaëe×óš,mÒhåÂ|ÝGâ”|¡ó8ÅûØ0¬cSÇ— òžãœ–K=˜¸#ü+Ç¢|g VŠÌ× ï.Â×ÅqLO݈٧¨Y#áØ+ v½ßȧˆËŽOÙÁOùèû8-9|ßÖŽÏÕrªU–½‰|Åp;0~5NSôKàûª³Fõé­¸OëFêÞÍ:–M_wâ§ÔñnLÿ¢¯Ç¿qè|œy0+¢¿$}ñgªËŒ 7å÷×}$F¿ wÐGß«xct쯎¯ñAµRõºÎëºñ$¾Nö†Ð:t‡ëº(•”î·cßó°|˜ô6TÆ/Æèô‘ï{¢3Oѱ?:¾Æcôo…“Þþ ¾®Äò}øð0ãHúóíË öè>‹÷ö&ñ‡è̳tì/ޝñý›Žöè²§ñu-ná”ÿqxBÝ¿ŽÎ¦Ì/ÇÙä§ žÄ×1f̘ñGágñuŒ3füIø‰|cÆŒ ÞH¿Žl‰¯Ëîäë3fÌø0óuŒ3þð˜ó&ȯc¾Ž1cÆß‹É¯ ÇœË|cÆŒ¿;_¦½|ywÌ×1fÌø{±ðë øugæë3fü‡±ôë.=~óuŒ3þ#¸ôëÒúÃ|cÆŒÿvvʯ+—±ç>¾.+N®sïŽ%K–¯—›nünPɃ:fY}\?„­«œº‹Î×›~]˜dIVÀâ¥ù=ò\â¼È$2ÿ\™À7ËôMø=²ê÷ZÞ.3N²ü8›×äÕ1Rœ…*—R—\Ès^ãÿMêuo´ @Z\’üÒYÃÚò¨ ?LrÅ×éܯýNÖkàZRú™ùô‰kÄyÇ—ÁݸOvÓ·¤/k§c_«ï§â©r8ϲÓ=‰ûuào}:ö6°&“±‰ÂS¥WÊþ±wGŠA’ãb-–‘¹Qv‰2’ÿû‡ZcXÆÙÙ÷ºÍfïsÇc”*R污ØíÐ#k3pp¿KšÌxKzáÿ|¬ß÷ž*õ)¬‹+© d]®M#¥«™/W™‘ЀŸ(Èdî\i†T7zÕÔÐhseîR±±+W™»ÿý£·CËÄÑÒ•$ü=˜¸õz-N7[,<ßÓ|Ül›µ:¨6q=²×¬ÕÆêƒ¼»1r¤w7Ý#ú.ïn¸^ £:¬!Û»ó4g,UßH º†c¹àqþÿʨÓ2ƒ•ÄzÖó,`Ç9ö3Ë‚¹ƒw'³·*R=kO÷îÌÞÑwxwÍrö ùæÀ§÷<ïοͻëŸ:ïöîjköqÞÁ^'‚šæ®|,¶Î`pÞkâôvèø{2Y&Î(„Y³äy¾ûýþt:An·Ûùb¹ÜÝù|)ΗÑò<*åe¿JžŸ€ÇÊ1íð·ñíòü<\<Ÿ'âkc§Ù†g]VŸËWÉó˱/Ü9Ø4› Ýñx„­ƒ†w'Îû^,æóÅ\îϲdÉòýr¡Ë¥x&¶–6Ë ƒF†îp8ÀÐý¤vBu endstream endobj 22 0 obj <>/ExtGState<>/XObject<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/MediaBox[ 0 0 595.32 841.92] /Contents 23 0 R/Group<>/Tabs/S/StructParents 1>> endobj 23 0 obj <> stream xœµVÛjÛ@}7èæQ*h³³­TŒÀ±ÐB ¥.}(}0¶|¡¾$–Cèßwf%ß›[ë’•ÎÎÌ9svV.n¡Ù¼¸iê€Ìs¸ì´á>hH!ù'M‚›Y¡¤E¦`Uï`4.{AãâJ¢z£ ÁÑP&"U`'¨BoNq×_ŒK* cÿ–Öo×AãGÑOè}]ªø%h@÷¦ °' Ï/Í¡œ—æ½Rˆú7!æ!FŠT iJ‰i›&-m‰RI´2OšRg©D—T+¶x;GMa®“ǖóœ^¥Ô3™§„`×gÐkµ)JÛ¨êP•Vísg}XU¥•ÇL`[9šªáÆ)¶ãµ¹*„Ѭµáåø\’WùyvTºb=fÓ‡Á(óXív¤òê«NjBU31Æ]Ó³¶©O—¶»•ÉUµnJiÍ¢),æV’«ü`Îpà­°øä{#É#LŽL&s÷瀵[´½òÇÎÁYÝ}&­l»íQlغ’˜»YB•þ_Ý?wï”:;¹wådù0FIHµ¢X…åÃ,²á¢X‡ÓE„´Q.çQk†&‘‡m8Ž ÝÙ,œM ùUðì"ÊUø1:w+*±õS­¼ô Ñgÿ–¡’B§/ 9 _&´y%ßIãæüÓ’™!½I> stream xœì+˜«:×Ç·<ò•ÛŽY9väÈÊÚÊÊ‘µ•••µH$‹D"±H$r¾\H²n ¡íì=ç|åɳöÚ™R.á—?‹Ð¶mUUeY^.—ãñøòºyݼ½ûôŸþÓú?Æß(«ÓË«²¿µ}5Ve¾ú|d]²ëú:©µe|=¯x]h74ç…¬ÅÛ#Ö¹A­úŽý&´PØ?¯«Ú¬¸ù»¾{[¤éX£–<²Í©séOÿ®¥6Ãóù7´9o[ÌÆÎÛèõ%®ûJÊÃu÷ÉâïBå“m®Ù…:¿µoyúÐ?§Óéz½*Š®ÌRÅù|¶®E9}éåiÿв}𦤓ïö‚åm[ØWO?ãüáûö{í¤­I“¶£²Ó`¬ñ¿Œoófk×M×?’ú絬ڀ|°"(³h­Ìsõ6_ØopŸdïŸÌúÅßõM¿å!ÛÊ©3]~ÿ¹lã[_Ý_el1Zg¢miÿQíäÇžŸ)?vmN̆’ëú´¥ßž_çšóG·ù¶uý>ÏäŠý~ÿõèår¹<¶Âëçá±Þ³ì>Þ•½^ ;x9Š¢ ³(ç÷Ë˵(Ô¾íG:e'ãOÈÿ;vD9Èï|÷™í"~~ùui~;¬°í’míx«S/ÛÚÛ^ÙÉÚšØN¶Õ:;ykôoNe; ý Û¼*ßZ’Ð^rû3êßæ³–ü(K’x$#ð;Ù_:Æ¢®u¯Ò¥ê.U{.›s©l«ýªµ™:•Öïlaµ–Jju[H_¾~[¹­¿˜×ÒY×:(™¿ê2¶¤±,õó_[Ö’°÷Ì^÷m†³Ý—?øwu:U:™=£"¶²ë¯Ü~ˆþ®tk×ü–Çl ÔIö®yôg,©AL±k„œÃ¤/rùè\ò%iý /Šú|ë¶¶Pg¨Y¼Æ] çVakë™s*˜oj®YÍB_‡·UÃméß>@«Sãl¸ŽœÿêËãVÍûÓ­[«µz»®J•)P‡ýLU¶mµùÕµidÝùµ‚­g«î­ÐÚÚÌ_{_ftIû¡¼»/››o·5Ú‘”AmC•Zƒ6-ðÆ q}·?Œf¤0ÌEÌϵ*].W;X»®`ÍárØO}?TÕPWÊŽÎŽÖÖÕ¤RU}çËù_Àwù%õ«Ù·5x;‚úýÕÖÇ®SD­š4˜_Zåïß/ ªÕ ¨ÓEÚcGmÚuÀÌý1šŸÕ6‡µ=F\”‡mªy=ÌÞÌó6S‹°y‚À,Ãsèô&וaÛ§üÛ¸•”I›W¤.ø¤žJB빓ÿyà ñuE?39 q0 !ôâ±K—Y<î–r-–nN…‡êÎBµlÕWÕ%cTö¾ìpº‹A5IÃ$6i®ÁÕ#à}ØŸpߦ}‡Ð¯ªÁa‚ÙfŸØýS¡ý~£ß32ÒÓú ö„ßù]¼žÿžm-’í%>äá=ogâšu5À¾ ëà0Áã_~ï£2{R?AßXzìpQàÆP§;"®æ1§µõ nk »N»«f¾Ëùªìá@•B5òùTkuå\‚êpO ÷G„ÓÀiçCÌhšÔ3Îå[ÕµÇc‡Ó¼íZ’å¤ÿ:TÀªœC5AëHšnwûÉàëcÇe_ñäù|ÑÌ™*?Eü€Ó@ôëa¯Pv¨Ê¡,­õibéKÊL§ÑYìVªy[óv»nû®‰Úòžj°‚j«T~~¾¾nôŸˆ:ýP¥z¸ÉÆÖÍT¶—‡±ß¸”Ÿ@÷u: ªq>ê4N×Ä‚ôÈ}Üñ†òUGýŒn’nn[§£xù‡#M °±®¹¡ Ë·ƒ?ÕâS€/4P]1[/Ý”oHð2pZ:ë@Z‘F­Ö¨=4Ê5WÅIý8Mκ¸:mkˆ(Þò­ÄÎ>Vü.ó‹€zߨvJ5i•\¿Û ‡vøÛsëɱ߷­¹G2¶IAµ€Óy×Z¦?;˜tç98—(N“k6®N3¨†×Ñ ô~d-P1&ë†+ëÉXï¥-äê´°-¯'sëuépõAsæ8ݹ¶aœ¶)(Õúw!ÆwIp¿p5S<Uk[Ìqj3´ªN j6MMï}_[Ài¬Q¥š³Gx£3õ6hÔQª5Q_.^£2ÖJ—Ñõ~7ö}_šl5 [ô-&g§²0 mlž?a;º:}ýØîضV£ö¢«jäfó¶Ýn7›Íµ(zI¹]¥N˪2H ðlËä—O«Üw'jE´ŽçG=¢Q‹Jµ¬<÷?¢H;k€Ùwø¢ÆÌºÁ'ÖÃð;ÿõ6Ôª x$~¼t¿>  ¹á"Uß(ý8*DV¼#K7€+¶XÑÀ p‘bz`S𦃬ô |>d(·~Ïc+eZ² ëbœ–4jþl4°¡„@å~Ë|9wFq¼.Ö¢ÃYJB}Ûø×¹¶.Ô9ßÐÞg«ÓYJu@P‹Óóäb ³•j½—D@Åï[ÈÕ]û»¢$p°7dxжöÁi4çc—5ðI¢5ÜÿDeõÀ ‘µhpHר³Ÿ _::íwð¡BÍ´Ï k•¨ÍÅl<ä5/Èx[˜Ý~ðlKg¯ÍÂÄ/ÍÖC5 ×±í·j¶)YÙÔºu ›b$„ÃëÛòq гƒ/Sá2Õ<q¾m‰±•³¾ Ñ® xÃ$a¹ûë\g_»’ªYøW§¡F=j‚š ƒi¹˜ù묪ít¾L“PÃùRìv‡íî ívÏý˵è½jí’bjEÔCÛöŠW‹ëàì l©­¦_g'g'c/Ç£¯_ù!Çl×ä\'°–­gpÖÖ?€í*¢Þ¾½yZYEƒ ¤N¿n66ÞÃfÞ¬T#0f¾};?Ì0_HÉu´f8-')f[¶£X¿Ý‡Ó"T#xžÍp?…Óî!ÑäGµü!éˆàU€µBì̇`œ¡:ò„{¤ñÉà P±¢ˆ²#ë»Ç ÌK¨ âØr® ÷èþ›.#¨Ç¨ \ñpƒ ø'rìÈðƒtH¼Nf9 Ðǵ`ð’òPxƪ¯x-‘Z@µkFµ½íÆâ¨“5Äi öᬠí±¹s-ì` Å5ÝžxPÁÍqÔàé–Ì ]U¿08íö’ý92šâúCÈØc؈f´Y²ßµ­Tœ¹ðlB¼$qC5ÆiŒµr×^; ôæ¨Óªá*„F4®÷“:(: mp¼7R]›GÒæfZ ·Å”dn;wÄ»§ƒ•jM ë´=†¾›«¾g¨¶kuÞ+=[ûfå~{(€Ù¶/ßûT8Bu…´+9—wÀ<`ä†ez#\M¯¥lòiÉwqÔ Œ‡Û}UÛÙ5Ìï¯ 6ýÒŸ]^§ÌÞmÇ®ë¯Å´ÞjÖuV“°µÅe2¾æäâ·¨úóHrlIoGWÛê‡ÛšÆu"Ôí[ŠªÎ£à u:¥ft#C5AâPfå' ¡GjÃvBOÚª8Œšx}NháÄÛœHÑIÒ{ÒŒû õãA;x„Éãqð{öÂ,b´\d5“ŽÅu³üÆû>& ©¸¾ uÝT‰Ô˜e契£TRUÆZ4˜Š̬"uÑú]¸ׂVSm«¯è‘ÁKA×Í(­cíÈojØ¢¡ÆZþØ¢8ú-®•#õ3Tî Š—=:"ôU¸3 j09S}ÈÇqÔð5É6DÝã_àÊ' ö›}@…U€CSl«T‡£;‹£6ûSï7ÓŒsQ;Cu´=Aâb\ïáõhhØÏ\7Î2Vo×vµd!(͈£F 9ê7æ£,¾¹ŒuuÔç{dÜš‚g°U<Žšl+Ü¡$;ã4½µ~ :¯T_g¸íý#ËТç +‰° jqÚ5 8i ¯¨AÚ¡¸¢hmCyÕž¹õ/'FB>bJuë⨃IbÛ*·È*C_Õv:ŸÇ¹ÎAÿi.3x ÝÙ``½F Ë[«ÃE¶ïCÛ´ç“&[gñZÛŸOš„uP}µRØÌµq‡Ós™ÁÙÁÕÖƒú;°Ý¾®¶› Œ£VV-ö£³F=¥ZPÌ< ƒžqiQ?UAµ¤»8œ<ûXâÇaÖì€9ØABh6Þ…å{ iDùHñö§Yìœ4ZæÄž­}­ ¯à=!¡Ç@36W0v 0üè¼Fà™(äe+jýw=f<%—øð`ª ó i(uÃ6xéÑ­ <è ß i&9ÖëKk³á‘z Î~Ћ¤ÄxI@îÂÏŸªó1ÒPéµçªÓ{A°GÁ4j)Ž:Õ$ŽºÄ‘ºH?häqúÔò€÷E­cOÚÖ>Ç\w>n™BìýqÔ•¢sä9€”îÚûÇê¢ ÅØf†tcò Bü]T7^2«ë’F ƒÓÂ32€!”¨@£^GM†!æ}à‡G‡@Š!ùH?CÚ¼¨¢ÃáŒíÖH_'õ36ò?(ÕôeÃð--ÄiÛ™økÐ*Q§û°fbu²Š·GtОÞnë®LËÚ Úƒ4д}y€â´>8DÒ¨uŒh–Fýe5jES ’5´tÆÊþékúxRD=ç„2Ãv»ŸÌ¢€þUÌïÜ_¶?Þ‡¦nOG“NÊj¾Õö¨l>ö§ãà¬I§át¯˜ùrÐÐ>¹EùçÃAse¬okèõõwv‹ÆöUõñšÔ¨ªQ+¢VÝï÷[°|˜åýýýííMY…ÓMÓ({¹–B,tsùõë×?ÿüóë×®ègµy”j>= *#”8  z h]ŸÕv?ŠèÒcß–ª=§ÆÁó8¯5€u\ÏМTûOM·êœGjôô Z'<é cŠ*¯ùK2<„b/q¸ÎÄBˆ¿¿×èXx‹YС˜;®=2Àîàz°®W1ú–Ÿ@y„Çä1:À-]Øâ9 ÀÃ>WA¡u {àñ5ìsŒ‡3øŽßÃ_Q:xŸÎ—ÇÛ%uŠèN‡þF n—á\Š(Eðè‡FÊšú8Ø€AuJÔ:œæs}ÜGÓƒ§-$„ j_8äƒC5 ±hüÙèž•Àö,¡ÚZßk€ñ`ó*7:Ñ-(½kb›oÖ¨¿o[+ã¨Kêi¥Ú7ºúg1%Ö¨Ãù90ÃÆ÷&Â'þLµð\ ô ÁiL’Gí¶…ï\Ìš0°èåÄp—Dƒ_޵Åýù3D^ftÏp×ItoþAtlŒÓ]:¨Ó1œ–•jð#ùÀ³‡IÓóÂ8jÅ0Z ¯[bŸ'¢$ûy<󵚮W+*@Bù&é¸e³¨u}&Íå­¯àêøò¿¾,Ë÷J§·z«eë·fûÞ|¼µÛ÷vûÖmß;g{`Ï»ÝÖmÂnEå2«AÙÖÔÙ˜úí¶*gÛËùý÷ÿÌ{—ab·¯dõùZŒ×‹bik TOúƒeúÃ>Ѝ)N÷•Â×SãüSÀ˜jÔS€äæüë×¹hÍâ®1B»ÔAÛ}}ú¥—SÙÏ9êOõõCçkø¢OZ¨l7ÅöׇêœÀ£Þâô<†R¤®œÆûö ÒÔ¥!BÓ¸©pYõþRZLÃ2¯K«tºVçk¥­òÍž(ÕHy†°TzX ÚãEÚ•CQÑ5ï„P„*~1PQ8›ÄlÕ$ød~eþÕ`¸ÑÒ=,tõDIâýþ„5_Y×T_6xaá: ³qÛ¢Ú5 Þ`H/©Ð°ýþ(û$ìq¼Ö&UΚTø4 è‹ÎJÃ'ü¹q2çOf5Õ¨cqÔ0TIÀiÍ+ÅuË!H¶g mO…UеJ5R§c¸ ¿á-ñõÈê+Ñ`á>‰Õ¶v8ðØmq¥Ú×”|ô„.²Ojj…!€Œ¦>4núBOF€²:a‡ê𩾑¹>t¼Y†ôýi8fº 꺠Qã0¡æ…©A'2×’‚P@ÐÚ÷¨á‰'x2 ±Ÿ¬Eûss‡ ƒ¢C>œE9‘0— Nãz6cœFqתNweM£©E´F5Ž£nØlc¨F­¸¥0Kl΋„ºdÖªZEÔûÃQq”Ï1¶QV¿x8ŒÃ0ÐY>¶{ŸoK:«×mÚ^uW\Ë÷·ò}cì›âêê}£‰÷}£é÷}Ó*®þØtÎvΞ5½Îõ»eT9*¿%ÕZ­³Í‡®³6õWf[¥³Š¨ßþ÷úHÅQ»˜É–¨=Hs¥Z£xÛz%ÍÆ—ÄB\sÑ\…n­sí¾’ÃÊOªGÕ­Q+|vù}}tYy‚éAö Q—5‚mÕóã‰Cõ¬Z7öÝ[ Õ³:^+€8]àè)?Jå±X,¾«#Åé¢"P ”j!œ)o »EV ôµ,„@}€ÙVµóƒ~‚ä P]btA~þøA"x}d¬É±Ö ÷„²"ÓØ?¢s{‚Ú•ÿÒ?Ð Z ‚m¡Hã›Q<©ëLæŠÂû :(T†Ée¤T;Ȥ1ðäÜ£ÇAõ}óQ—TÈS’`Lµ02 Wƒñ“ BnŒ£N|El OI’è×x9´—(æ|){…FýmÛ‚¸¾4õâ>©¹¡¾­€ž’HJ5}-ñ–8j׆̪4ðù¨%ŸÐZÈ‘_ÆA´ÇQãù¨´’µ}WÏú('=ù’Â{ôY›ë û€Óè=zGŸÂp¨?j9ƒdææ –<‰Š4 ð’ŸDÖ¨ãê4Œ£VèbØ£%v "%ÄEp(o¡ÚjÔGÅHo¢÷}àZèÛeFt _º0Xûîpú­þ0µIZXvV ÎÎZuÚ׿þ¬Tƒò¶†æ#¤Ú$½9£+ÛžFíæúÀqÔZ£†Ñ ¶€Õ¨6[x¶íqÚfªЍ¡F ôäAè¯mÝúŽö¢h¹™ñ¸;Y[#´Y¶jTãü_ ]5>ëü_Õÿµî½=æÜcåÉy{m-$@£>]ƒª?N§wUÇ~¿µa Çãûç§ŽúPí1D]ðÙ9ÆiTHûÏ?ÿ(r6õPnm5*ÞV]_mÿÙרuùZ»©G£µZq読¡mEך~u„¶†áê¨é¼ƒbµ&ês¥§l«œþjÀ¸´%ûîºýG¡uïAÚ€wy´À¬Ê7G×™’ÚémÐÈVÝ£ÅlSù(Ž¿þùPWÕàV÷õh?T»Ç4` ƒ=(Tcœ–0…N†…| …™>vÇqÔ)Ìf1Ì%U§ìϪ Ì^Ž´¬–㔆¨…Wôò‘¼Ç Ü â˜eê4ÀQôWb%ÅkÑYqÔušß¤ÌÐÀ!4¥Å˜ ;xÊ>‚à¨(÷pö!¾¿¨ˆ¡z֨ϡsâ¨Åo& P tEÀ­‘jÊf­!ì[‚Ów«ÓÂËq d"<—ÉÃé¼8ä ïZwžFýømš׼ô!©TÚÏO>º$Aµ˜‘R§¹LtòåA»'» T£™Ž‚%3 Í5׬fG-ESÃO½@Œ—f¼‡¨ Àõi°úJ8-ïc¿}W€ÑZšV4èϰ¿ ˆN?øÂrÀLPµìpR‚šùø0pFë :žpuGl^5]kŽ£îÇY£¶ùÆ?ž.‰Ú”U\uX×hÔ¿ûª¬vÕn«l½ÛÖÎ6ûm³ûh÷íî£Ûk§é¼ßùú•orö g ·û¹eSs ¶e·Þ^/0ŽºgqÔ$äÃkÔ›ÍæããýpØì÷o»Ýæ|~Sÿ=Ÿ7ŸŸ*½ªáIQV£&߀ö4kÎ_Fju kZ¶9 ’?L˜Ç Õ!Žº=ýÂË©µ±ÙÇzr̵úëdaÛ¼“¨cª+«Q·åû¯EÚs4uSXð6umß7t~gQÅQ·ºü¥qS»4…ªMý×hÔuãÔéÕóÇC5T»©ÚÉ‹‡ªáËDy†1½¤…WÉ#´fšmˆ.–^# !!!všÀ÷ZÇ€™)Û°QÈG™n³!‚Ô¿j‡âÆ î&-z1^*Æ>™þ,(À$äCP¤SØœ²@oAkñ­ª@3{á@ÑøjK|Fh-ÇɃ'8²„%\áç³iȇª~¥z)Ž(Õ’C8-ˆëÕXµ1êpHá7@ÿ¤‘ºw+Õæ…>)ð#„©V%•“ФÛs]÷–8êïØ‰£&5³˜yxž„òt?Ç”jy°Fë° Õ`Œ©)uZŒ£f8íjÃ0k¦k‘Ï—#xŽ`¶8Xšñ£fÛà â3åGPãI(ÀŽ÷3Ø?xrQˆú\Áfu±Br<±ê?Îì6Dhò9˜:jûIÁàLÔà}.É™¶r¼ÿÔqÔ$•¯½ßr¿Âu*¦:½¾tЍ»JQî~§RsPv«¬J­¶ÛV;Ûîs×9Û3æð2ʶ¦_§ÞŠùoµ×Žj@Wo&ê~;ÛkÔ:d¢_K´õÛÛFôv«“röûÍéôz8¨´9^4K_.3QóÈç/;íbªÇ¡Ö:o[kÉÙÍþ¡þªçÙªó ”´a!-žåCkÔÛ¢3qÔV…\„³ªÕEV›¿žÍ_gD?i<ÖóuT&Ö¤ Úµ‰”îÌ»Yë¶³y8¿wõd·Òøò­‹²6µ)¢.OsH‰™³QOIæú¨Z“o$¡ñ,|`Uk8×%E#ØÇ÷ð4Yåtþgòž>ÈöøMv',õ †+›·*·¨=ø·Ï13b1‡ãI?Ü—Yå+²:n^úº®?Íñ`móyhU©Óiß´íí­µ)éw§y-SƒN¶ÚÆmkÞî騖åÖÌž£fßC´õÛÛï÷÷—ÍFÛ÷÷ß/ÊþÞnu2!4[GíS{ùuî, kªµÀlý_>œÃ³Eåþkôµú˜õj7Cˆ‰£VDmTè³V¡ B·æÁL¦g@Úþµ7ÁØ ˜uÉÊ¢µ ±!½ 1¨\9ð%G.¢gá³[iô¤y³oJj´!%§T›éÖÁ']ØÉçZZƒË61 †—9‚d {)Õv…ŽlÇ|öi <£ø+~ÑLBè æ­JÚ#¶?šÇ¿KxMLˆ$„C›‚å…DTŒ£ø*¿¤µeú¨Tn'º=1ÍŠAµÐþTL£ðážgG–kcìÁ&@õ•A5˜ë£¾=ŽZÂéyxž`Ðò`Ì’¨N£3kÂè…PªÔµ–Dz·ä7Z¨ö(«$ˆáa:¶Ü64ø®m¥öÏŽµ¬ û9¢Z Cñ) P§ÅçHIÎPª9NcP—õdy­–ì¢K#%ÙM÷— )Y˜@÷¥¼È ¤ ÜK4+u—\ùT5³,ßg©´‚ûj¿֜Ӗñ ‰F\ÏëΟ„ PW§g_ñÕùZž.×Îy¾*q8„…0áè‰UÀÔþÕ×ìÊ nB"*ºW§‹B§+ð&£6ê“0 €ðRšë °ꋯ+²Èj Õ%ØpþH9ð¶èKŽÞGSïâAàÇ×ø±ôZ¢ý„b?Œ»ýát¾´ú<üˆÞíöxºLÓ×ýõx{~Ûè—âêj¨kkmu›ÚØj¬«)aï× ßÖ£¬®sÞŠ±MÝVUy½n7¯Ã8ö#Ó¨7 µn:m…¹>ð§Ã½=Ò?ò,¬“}]Ziþjø™rPž|iQ[|ည+ïôQrÒÝü ʯÎ}ˆ¼ PýeÊ8¨¦¶¢€¬ûÌ+°]°´A~VNð!5ÄoQ¯,~Ì-){¹‡ˆWÿXœ*~².¸iJà  É€Î–ìቖOEÆPh‚Û刞hXméz>¿P=TÁ¦õ$ÊÛ.lp,H8„„Óþó'RªÑ|ÔªÛqÔ­á(æ­èûàÌäð¥]ŒÁœ?‰>-´Çïy0À¹W©žÌ‡QÜhïæ‰ ¿™¡ºAPí>k"‚ú|¶€£ †·\'5¬|Ƕ„êÍÖ†xª»Ô>Oµ¹éá9CÏs¨N‡í"@­pdrZ©×2,ÀgL"®ÇcüœnE›kÜf!(…J†>% ³á ²ðrôUøÈ TÙ›,àõgÕtè‘{9¨ÏµŠ¡;üž#~%¼pŽAÝ|5´a"‚’Xð€ê Q7L‘ýÖˆ“—¢:OÛ~kÙý þ.d> í__¶/¿·Àî¼}}Ùmf»Óöu¶6½¾¦WÙ7õh»µöõåÃڗ߇Íëe¿ O†w3⨯e¥–¾ï»®KÛª®¯E¥‘OI-ÄTFN…¹©óÑ:ÇNZ‡˜óO¥CðŽÛ‘ùÆúD&F£žm8ÁøŽâ=l÷p>R47i2…’¡Ã—,¨X1ÿ[vZ¨M)°‡èNUt ù $[,Æc† h?y(æÇlOs*æWàÝ|è çC˜ù­“Üìhâí팕‰êÞè\Ê9îÂŽ£*ú QßG ° ÎIN”êA€:«O“Xä?Є1°‘½÷¥:üºÂÿ:4kŠ›ºpÿ°D=P4«»%TwƒßÛ1Tªž,uú›·%î)ÔvØ>—Ôi¡Í~*KŸáü§ z;J…×oÖ,4 €1RqÇä7I{B€„o-ÂN2dƒ TÇâ¨åÒwIè+ÒdJÏ+~ÿ"(ü~8 Ï4í'š)ËlÔc„_G•gh;ÜNÐ*ùP?'¯.6›i`'ÄuŒèsË[ðFýŠ"Ö¨ÍspˆÓÌŠJµ³éë_l'h¿˜Oõ “À{é8ê¶W–òt¾ž.×´½\K+Õ%ɨÓf†êØçÅÓ¾ˆÇkp:à P òç4ÅAÌìí4j4pvA}°5óQ¡wï Ô‰¥Y7k!¿‹ø«À;¶“‰*'™XNN¹%4ÍÚ<&A¬MàqHKíÌ:¯Öµ œoÒ¹¦­Äé %Z­5DS£¹>òâ¨iý ¤d~a–@5T¥wÂ<$Ñ/~ZqXÔ„=T߯N\±6ì7áÛŽA·X"CþŒ‘Pý&GÄ."$]Œ£~ø¶â >S[z9q?£:}›;Ôf‡á¸#œöƒµ"|Ôè½9ê´Ÿ6A#~ž@õ!"T;º$"_GU\0“Œ¿eûÛ ‘"]:ÌÅÍ@:LHñu¿÷ìLù«Ø|)—ˆFÚƒû ñUAº®bõäØïÝ–¸äXwØûEö9Hº:Ò¨‡†#óÞ[¶öE?UaP—â¨]ðGÃý+„fw‘nͬ5bÝ¿u‚fÛÀCðr"üÎc,ŽÚµ0l )Ò‚›_Vƒ-òÉK¯àSà0À*óL—r9W ùd§øûhS]á4ûÜ9ùº·GÝËuÚò„ó'±ŒËÿ6Û£œŽZ†Ó HÎã[ÙXG}'ÉÔ$J2Äl‚òÓþa8žº¤Ÿ¹ ]ƒ4:]Ð`b~§±º…ô–0ó|áãÎi{§Êñ£å!Pµ)´¦û'Õ1˜ü“àú-6cà Ì0€Ï·Ÿoò°‹©È,dúŽ8êÄâ¨É6QÈ™ÃÜ… ó¨ûùÕqà[†×ÝÝJ5¼.°FíÃZRqÔ ïˆmêÉܲ­Ì˜íì8jü+VÄQûs;LD ^Âå1Éð5^€Ülh/\w\Hñm€qÔ`΢Ü8j–(ì!1sQâ…Já>5Åã¨áä¥Ô¿B´æ_g€­¢!Í_Ñd Pµ!äÃáý’B&ÿŒàtÃpÚa6nsçƒ=®ó¯­ÓÕU²Ï¶êTã—àÉjÔ5Ô¨{ÀgåËJõ6¢Z êt“¡KGʈ€½ Tßú ¥ÅQczï#¾¨NÇ,…Ûµ=ÉëFÕæ¸ hgú7®ÚÉ>Q¨é&ŽÙ@…€~BˆîrqG³óÝø²€<æ¯H\Õ¼ÑŤwÓpÒû[3‚î0:Ë‚ï-Þb§µ>T¡¡Mç#¥z}Lõ²jS”â¨g)Çìãûé^Äé6C器ÐB€—fö10ï`c‰(Zuì‘Ø1?ŠÙ®;¥~{2êÿ ¨N¤Ÿ€µ?ÓÆnÙäöKb@£.NW$ŽGA 0ƒ±Sž—N,l¥höøÕNw+Çs¡ ÀÆ®A¸ßÖúªùÜìTéuíÁð û BD¥ØF‚Xbõäø™ÛšVo 0þ)雝з¹ÚŒÏóp™ŸIÏëóå-;ü—5«™_§sy|ΗàƒÖ 5/ö •Wªù·£6§ ŸjÂx?OáWc´fõ, +À>¡ŸŒá-D³Rw  >ñj|ø0 ™ãºÅs\ƒÏ»¬ÂévÙNÌ¿%$ý¢b†ÏAZ ÿøê"zõ ÕúZŒ£’~ÜöÞJ´ÿ(n‹û 3¹€Ý¾˜I†§Ü6ó‘;ì|VJÍ´䨌±Yºár0ùr»‘xfxSKûJø#&°ñv_k&3|&rÖݨN;ì /Z––_KœõáNœ@¡5Uªj2Ų q×C(^@ƒ‹{ÒüÑ¿™j’ 3á^â×iŽP ¶3h¼Ò> õDÕoŠëüÚDGù.¥ ¤#H÷X^Íü»™õÇŽ÷³öO¬ èy|Ÿ³úZ Ó¥:è]¬“¶¿ux‰¾_à‡=Ng·¶0ÔÁøŠY\f “ûQ•ÎÿÚçö÷CÐ’\ȧ_¤q=ñ¯1¢¯ P¨.yˆFqä> |þ’Ô¨ã¶Yg'æ‡Ô2ÿAv\ÈOªÖ@¯¶ß Y¦ÄÕH²Ñ8êñGÌ+x§¡?…Á—ÒÀüLk_¢\ž™$2ÑßB~¶í“>µcÒ¿Éš4eûÓýþZËêAm§RÌš°ñé'ÏÉÄ9–:7Ìg˜ô,ªoìÇnP=êØõÆÌÚ¤{ÑQ—wŸpbÇ—Ô?ü´|ÎôÒºøhüü©sÅø÷ÌP$¶Sø]·¶„_ûßö[„)O_§t\r¶•ê3—ŽõÒ>\ÝÏdüöœ{¹FûØvÊ[áûðÆ/îõ¼æôýq]ýéã›*?ÄJºždaÿŸþ®Äþç¬2þ›}Îl4Þ{ a µ£Ô­ eµ;÷‡£³Ÿ;ãïžþÓÿfñsÊü¿ñÿF­¥¿}¯ìçvoÓA¥¶ú]ûOëC«òw¦¤^qÿÉÎVØ Î‰ V~ÏìBßbsÛyk{ø>ù¾ß%ÿ–øµQOºÎ…ú3|¾Obû<ÿ¸¬ðs·›{>Ï×ÎøÑó|©¯^úí±m¥ýÏmÒßÒ|^n+©iŸ¤ê‰–­Ý»Ðþøo‰—§›ï{•kQ)T†ä 5j(b«ÿªUFµ ƒ1ÃÓúßêÏvC¾ûÏ¢-[Ö–_¿Lªgš2ý¨Í)óh;åøÓDò'|¸Äò¿ùÊÈÿúúZôѺùkë¼§žåÀ"œ?î?©2‘óðO[x=Æ®M›mìÚ‡}ÅøS—ÛúçØý"Y'/?Ü}ozúO_ôTÓ¨]5Œ3QÅQóòiŸöçÛqe™XyTfסu+P?VòÏ.S$gŠ”á7ô)æÿ=;­Åø†q¼,Ñ:ã+,—ùŽå;¶Ë÷!³|ÿ䯿cóÏíÈU»^bËr‰ôëgrÊdôiãʾîþþöiŸößew‡OY£~ÝL6êÃ%¯Q§/‡§ÿô¿Å· Èy>ó‡XùØ«óç-ôcp m€c ñ”][>ê ãþóáò·ðõ¹Ü¿Ä†6ésãG!zòúÊ¿–á2Eü¿¿ÜÓß®íÏ#÷3«/ýýÿÓ>í£lZ£¶SŽØiF,Q“Kàé?ý¿æÛ%âÃò‚åK,m™?»L•a0ñül%í¯Ûé&Ürü璿ܳßÒÇ(6Ü€þÇõœk*v ²k–KìzÿYK~¿ÊûçtßË÷ˆáQ÷š§ÿôóü]4ŽúUÇQ÷£ƒêé©Q?íO±Ãð5ùé2c²¼]buŽ LÌbeàËÄ2å”ÉPðX©þ‹ì”|±2ßí¯mÃ¿ÏÆÎ¥ØyÈý1,9ç?\Ö–Ï]ø5Îú!ÒÜØ/­ì×öŸOû´ÿ »Û/iÔÕ¨Ó—ÉÓúÞŸ­]Òù¬ÌócK¬Lκv™2|˜ÃXÈÏWÞ~’ÖúÓ”éOùÑ2÷,Ûšµ­ôv¡eeVìÏ)ÒÇ® ~-À+.ó°L†ÿ³–X›S&§OŽø?á¾óôŸ>ñã¨ýgViÔ¿ÿ§—ßæëÛü[Æç<Ä’mY_hÕïÿYÿýýÝ·óæív]÷ùù¹ÙlTµÊžŽÇ¡ïÓûDôý>ùÖÿv¿2Ëÿ; r‹cF~´Œ]"åcù‚ëa~´ÌŸZ¦ˆ¿¶|ÔÏQ6Ø?ícì=çÀÊuó—u¥ÓKÎõËû¾îýз¬í÷Væÿ[îOû´9–ÄQ÷a>j«Qq^1­Xæ÷ïß9õdúª¶œò¶U«Ú™ð–+¨¶~ßueY*ÀÎÜ'÷l÷Q>oß?ÚWÞŸ­]ò}XOz‰•ÉY÷ï-SF~T=‹)l·Í¿ÁNò§éNºÃ@´þM6ÿ\…×ô3@=¶îÏZÖöuéüì¾zH÷ÏOÿéÿ<4ê.2µµë4êˆÚéó=žõ}¿Ýn||ôI7aŠ·‚^…¾///×ëÕ«¬¶ß«Ê‡ÃAa¤òÆ{ȬŸø Å{¿ß+üSà~<=xûòiU6í׫Zº^©öeüR›ÖÂ5Û–è«V??USÕº›ÍÆ·_1³òO§“o¿BìNSnkB±O·_µSP=ŸOjë7¯¿âÏÖ.À"ù#¯‡/ÊÿË”‘…Ÿdù…uŸ@ëìôýÿví9–>Wǰ@ŒäÇÊü¬%ÖÆÊä¬{[?–ŸsOyúOŸøñ8j«QAzw5Q‰ ôö}¿Ù¼Þ6.à °ÿëëë‹UY•å ù±ÁMÓ(àT?áýý½ZñëË‹ÕÛíäÛ}ß*µ€ýóÂUb¶}:µm{sËÅcäëO·_•´Uöåååž6|»5?ámq¼#ùã¸.?VÆýg1?êÿíeº©<´ ù øcþôÝþO€Ìoðö-³±2<íÂχÇ/+¯µ×cìÚÓ}B¤xTßõ´Oû´Ö¢8êÆQÏuëäë‡ÄQs•XQÜoCu¿õr£bœˆ£†ur…üQ±ÁUUm·Ûóùìë·¿ÈZ¾-чJ;Ü?¼ýæ¥Èƒâê÷÷·º®nks"Ž:Ý~±÷ïÃ?ïÏÖ.ùÏç>\Öæÿ2=hݨ¿DÓë>JiüÛvúmø¯Ù{Θoýpþ Uå{–{ú®Œ¾q¡Ïdö'ÜžþÓ¿Ù_Š£¶õ×÷ÅQ¿¾¾z öf›ˆ£öõ+ åjpzv‹UÖ ¶Ö5NbŸˆ¾ˆÙ¾ýZcgŠzQ¯·ªÄ‰8êtûƒFÝu^Á~Zñ»'Ÿ—Yyî eÆ1áÜÕùó–ie~´ž˜"óïQ\oòcvm™XùØo‰ùÂoÌù½ãºåQÇ÷O/éë+Ç]¿9×x$ÿ¶>çOô™ì^ó´Oû/µYqÔF©þî8꺮uHp^ýÄOÇQÛ8d1ŽúííMÇN¬Ù–÷UËÐÚ ê®mO§Óv»µe>??Ïç³rªª:ö|[ÐçûúªývÿÀöÿ{× ¶¬ÏF¿øÅ'RD¢Õh4ZF£Õh4ZF£•H$R‰üîí†1 ¼þŸçœë¹öžwŽ1Pðxþ2þ0GÿSŽüå\ÿ²Ò›Âx¾:;írѧޑ”²G½h¨—uî¼ùVÛ8ek?6Üþ]ÞÖþ î'mámm*õi½]6´I›[æõÿX¦V?iß6Cykéî×i3´ÿ¶ciض³lÙ—tygY´—íÝ66žy}O\³îm÷‹½ïuòéÜçÁÁ?À9GÔ<ê^9jþ®—‹Ð©Åb1úx¤åðOáü¼š‹ÞõCË^ÛÊWµé|;“-õ}¶mhÓöVn—„]ßæþhÐÖ¦ CÛ?@Ú£~h›¶öÏl;´þit?×OoÛöZê®w^ÛM¯ÛüéëëÁ¶ï½‡´Ý£†Þ÷¦s?ÿ(GŽº6S„)ûlû¼q_f¯Ý×Ãcì¹zcž<<µœJ†SÿËñ{y^öy{u%¨Û'Ãn3Àqêlÿ*¹òjÙùo}¦=êÛ¸Û~@iuÔÏ5M›Û8N¯Ýg»óìôù²m­ñ8ãïsŸIg_mø·çôŸðª×öÐk§ÿu'êõÿvßpʶûÒƒ{Wg üòõ¿ÌG«qpð¿ÂEŸöÏ”½ú‘-ü™ömmd›“6ôËÜÖ·ciqÑÌEgû>d/—Ûæ}0tÛWµyf C·íS?ô¸Zž¯6Þãyúi{-uñ|ÿÓµÐÿ.ÑçzïÑþ÷+ppðœ×<ê¶ù¨#+GmmŽ%ʆR¾ˆWú”²Æ¥Õ^ºý8í…ÛÞ…Ûÿóüï!íQ?´M[ûwlÛ§þoá ¯ù×i[Ù²mŸûÒÐûJ”(–ÍuSŽZÔrÔB€ƒƒ÷åÏ”¯êç{Jis)›ë{ðþûzPƒ¨Ç®okßYßÐO'o8'mãéß>Æçaè±ôym |<õ:|‚®|ǽÂå¨Mð5J”/)å‹xkÿRã}Êça÷Ó‡À;0ôu8ÿã5èð¶+ñßîÏÜOP¢DÙ³,=êb½Æud{ÔB”€ƒÿ"ÎHF‚È[·µnª¥|ó¶ »½t¶}†·§µ=‹÷iãËÞ§Ÿ¼Ï9ê|<®>å=ŒW‡Ðß…Á> t þ>¾:ÜïQÌZ‚=jõÛÊGäµ09jxÔ(yÉÈ~øXÌÑI›Kùa.ûp¯j?e<3ΡççÛÑûµDïkú .<\¢ý%Rå9zÌû”}úy-?w· [xŸ>CppðWñÕ1 öa)/+_á¥æQ—9jåN×=jK~¼Ìžû¨È‹ã\%ÚDí8?Ÿ ï½:†8Ž"MÓô«KB’¯à)Ïy…O¯”ëgè¾\.{´™0N)GÔÊõ1$9Íã*üœD5ßyJº°£+9jQŠjÛ£f dÞ»áÈœ+¤)@øüÉQÏTu$$§Í0IÓR N™ÛeµžªAT?*¥Íù–aqéòÞe[Ÿ­ýÛèC7ÿd? ¯:‡Cë[ÚûèxMš7xWõí˜09êBNÛ9jzWŽ’T)j]Znž½^@Ÿ·Ë_ϵ¢IéäØrçŠ9Ë<>9æü°Že}+­ ÞÍy§IœgŸQåbú»P¼ê\ÐÑ„aè~„ùƒ¥|QýXãœBÿ}¶}üZQǦ¥Û¹ÿ œ>ÈûPÚ9jQËQkEËi%­G]¨ )Zã”Þ Î• þTAçD¿•u/µ i0å3E¸ßïß(¨KT¥5:`Em.üq¾ÅX\ZmÚʆmÛÊÎþ[Ç`ã™ú¡üUý´õÙ†wìëmZêž_»¾åõ£õ 5}Ÿ³G½Ù ž=ÏÉQÓ»r˜HÒÒa¢tµ){Ôìæ ­ˆŠ2g.ôOÛŒ7ˆsUåêäÐ9á“Cg)ËC7Âx*ÏT’)”©ü%‹jú˜ÀG*YrØüUe[ÿïØ×ôJùæöcKŸqNðXJzÎ88ø_àGú9j“úÐ$­e‘ú`7O©Ç$)% x!ÕÒÂĹªq¡E¬‘yJQkƒz¤ñˆÌz¦BË£–ÄÏ—©åQ?/«úðgú©lË·*‹K«½ü·6õ ûuêe÷±Ømœ±µWZú|Š÷@¯1?q®†>/ê5àQðy”5‰d¥¨ÝµoyÔ²ô¨ 7OÉ’8NPÖÊ$!]–‹Fœ«jIgÄý¸Á'm¤gª"ïkršñ¥\{ÔiéQs)ùßf.ûðgÊ¡ý¼j¿(ÿL‰588øçy=GÔ<êÀxÔ¡þYb˜ç¨w¢pól![BåsÏ®ó‰se‹ØÐö¨³t¬ñˆ¤AÞ»õÏÏO‡Üý(•žç5nEõ\Îf³Íf“èÁð£Q­×ë™çù~þÙŠYëít:¶!û'úßJù"Þ§ÏÁmø–ç¶/èªw¶•ÝÛ¶µél?€·õÙ9æWõ? ý?϶þû¿\š®÷ý~]kTú\/?s½SI×5]àüs~*9jiyÔ¢2×GcŽšÝ<%MªÐw‹]üNX­VÔÒi5ô­¬ó¶–n%µ´;×ùtÏÕo·[sŸßív¤£í¥m´ÿŒöæµt•«C,bGbž/–ôîC"ÜWîÇ<è¢ãJê–67×;WÒ©ÕÔöÅ÷k×b6ó̯j“¥Û'!ð}³;ºÓ˜ú¸h„|‹£’ÞDºK ¬øäÀ a<êUîQ§ü`zVšƒÔÌÎQk±é"/IXÔk<9ߦ´ÀŽHº,— zˆJ­*#Æf³™i°^R-Cj¹Ô-—¦%Ý7‚Àgk—·<ŸOÊïýù¡òx<Úc¨Ž§!‡o’t缜ÏfœÇÃ*‹ùý~¯Wãxje¬&÷¨;Ÿî¹"]fú7%L¤L×¢> éÎIõú¨ÙÐöú¨m×½mlÔ†ŸU:E†Ó±»#ç~jõ4~zÖô ¡'+äz:í|§!ÑÀŠ–wª¤Îixöy®–æã†íQ? ¿˜“dÝívgž¶"9z½^L “j‹wjÉûša`-h÷ô.FgÒ/ÞæØmfоèÄÉ}°b+÷ê³]ezÿ³ß‚é…·|̯CºLL =×ê£DÓ.M9ê pg<¢G›‚˲¦•‹mz·ï³_Ñ£Ÿ¶öŽëüéóü ý3Ç8ô|ªÝ5]\êâtŠãz¾š/X¡¿$"ÎõtmÒÅK·nzˆ6äöt[#QM-©Ì/@!øÃ/*õæB‚\:.VqP<ÑA”üû&!èú¥{f[Ÿô.ÉY¯óéD·Á³¾ÃÜÕ{è’šÑ킆$õ!èuW¾—±ÝèöEo.ï/‹ká0K˾VRœÄ³†õXÎ÷ê¸úQ—6¼y’Ø×;]tÒý}Û8¦·ýI¹­OºÇÒ½—Ýá齉õ<Õ°<æd8ÝÍjk<.³^‹ªG­\h%ªµSm<ê,ËÌò.Ú£æué»ñ\¡JŽš$–ʸ¢”Ôszˆë©$ÎÛÒM OAX}rKv)ùD„SǤÌ~µÐòuåÍËU¾·±¾]?Ü :¶ÂqmO}_•`Cë¹¢B’ˆtŸ?)_]ÕÓYÊ5ªnC\T~Ô[#&#KêwŒÍ:Þ°pÚ5÷ÎCíQñ¼Ìøy¡Î•^=^‚ €zæ–mç96"6©yÔqí¸Êµz!­Ík£øÖ£á|òHXES=^ŸÇÅ¢º%G-¬ EØv› ×n³(¸geõ=éxSnŸµz©Ý°$‰Ýz³/’ì;mÖk6¯ì1˜ö©ò¨íµ.¿œêXdYãò>m¤pÊ>íûì÷¥Ç2€÷>–ç°>fñt=)êCË|Ôôèýv[­–t³åzϺÏxEÎYQãÚ(ØÒäÊæ«ØóÌ©ë]X×»µ_·žäîñpX9juýíÝ>ùæ/õ·Wtg`™ÍïÔŠîbô¾@ÿÏçôNÑó¸Ï88ø?ó"G-–ë-Iå{,HQ‡±rªS­¨Ù£Î'ÑNum>ê8G³\+­dl)_#Ëôš¶­}®çzÚ;) º±,æóâ—h‘®Üð…¿÷o‰=Bõ·ÕJÇb=SoöbÍ~ÔO­ì‘£®—tËåÒxøÄu”">ŸOZòQßø¨çÖQ×Ϊ;6ûxÛxãsdJûy1\¿1­8…R>ƒÞƒþù´ä¨$.¾¡Ð?El}O'võI‡ó+¡ã™r¿M,ñ\ºÇ†Û2[ùKùU&Ý–}ºÐ_Râ­hó  a›36ööu9ê—bÜ1È ó·Ï·Ÿó‡óQ `ž1o¼`?8¨ IaGûêïÚWRõ¨÷ëä¨;GmêûT–ËéÄÁ0õ‰@ç'ký_¯WÓþáqðZXµRÔ¤¥ï±T¢:‘Z+ê¨)G­uPõËzXú¸ê¶{Ôam[m{Fm}òÏÄjõTiú¬µ×âêÇp„ûýžýLSÏ_ñØ8ÑjW÷x,ÑXw>ÝsUã´C5lÍuZf¡B ‹E.ž­öùQ;µqlöñ¶ñÆçÈÔû3ãQßõ QõAàó©‹ :VÁN€kº©ÿ¸´pæúh çÔú¡·•=ôh?‹Aï/*´ß~\Å3忍éâ(†´$Ûe2œ>àDü%¯»í–İ(ÖVË–>¥ói—_xô‘JI½ÎvÞ’ÛР³ÿ©Í}šlŽú«¸xQ›.žØöãÇøÌø?yŒnŽš®ÊËåœè/Åb`ÖÒT=jòÆt™?O¨9Æ9߬׷ëUèK;®:ó¬òBPi稯ëJŽ:IèF‘"®^¿}{ýû”ƒæþ©·§a«ŸOª±¢îu\õ{88øsÜ䨗ëò¨#q;ÕijyÔ²æQ»9ê¸YYõquö†#Jã· ©¬£Û£¦QГÅoŽ¢˜ëƒ°énåöÙþÙ‘WÚÞª˜@tôöm9j”n)ÞÐÏ«úœhézÔt±"å/Ô8h.(Ú„'~¬<=jzt^ÌtZ¤>x^Ž¥;ׇlBm>êÆëº­O×Ñ­Æây"šî3f®3°‡ÇÀkQzÔEêCÉéX†z²]ó¶G­œèÎõÑáˆÚõqUlëY)Ì\a^E$Þx& óÓZ¾­0uÒ0õ·Üp:|R±k¯1Ž|ÇÅùìü™dîâ&VŽz ÂàùRÖrÔ¢8øoå9jpppð7ñë T't\¯à]k&Ž1÷Û9¶$xU)«9j†ÿÕÜÍQƒƒƒƒ¿›sŽZˆ|®[”Q]ñ¨;çú`oe¥lr>û÷0ŸÏ9¸2‰cyu™¸u–{Ô£ —ÿ”£6-»µ\É‘]Ö†Q§›k6#-}Ì~ö¤¨³Õ9;‡Ùíž<ð¨ ëû3v©$­Ë•$‰e皥nÊQÿ”ûÜ•vç$×Eõ‡“p§àÐu>u"›rÔ…¢æ…©$E½ÚlrÔàààààãqù©u¦&‰•Q$v×Ì;fÁ1#r¹ËÛ-¼^oâÑDùgê¯îNl6›ù<ð4zõcéd;g²ßïçóùb±àa3xÔðTÖLÔóQßjóQÏ|RÔìZóú/ÔL)jä¨ÁÁÁÁÁGäŸÊQ3®×û-Ê×ô¦Q$Ïç^r:³ô­åQËnš´ôù|âÎ-!=Ø£6 Ï¦²QÃð$ÊõZ­BÎî4‹jö¨Uê#Í 9­2!Ê£^×<êüæ¦Kpppppð·sùÁu¦¢Ôa)--d¦¦§¾&·»¼GÙåwÏžgç¨Ç£zç=wNŽš¿-¢Î¤½“$¡Êý~ßÓ£nìœ:äwê0 Mâz>Ÿ L  ¯†åQïHQ+w:ÊÓœ£öf>)ê[ÓßU—Ê£&ù58888øx\~j>êLÇ0ýÃü{(ÂHÞn⦤¨OçètºŠNjò²e®Àµ¹>®— պƟ͸qwŽZÏõ±¬ÍõÁ?l¤ ©4SüÑé"uMÍž=)X09jäPuù Q]xÔ¹¢¾Þ#ÕÔl¹Þä¨ÁÁÁÁÁÇãò‰õ 53ýËD&ô†˜$ôßôtº]¯wÙù£BþŒG½Þ²G-µ¢¦Rf¹G=£Éiþ35rÔàààààcòæ¨i_a¨ú¼‡â|¾ äüz˜õj³œú(D5 i_ÿ2‘{ÔWUÆ:G]ó¨ó››.ÁÁÁÁÁÁßÎå9êÄqŸÏ×îÈ4–G½WŠZÍ’'õ\y2«xÔ± ~”9ê{=G ŸtrÔŒw䨺aå¨wBfœ÷`Q]ÉQ«Y>ò囿úÐÎ@q‹7—5º¨×ŠúõõK¶à·¢æQ‡ŽGÍóQçSçYóQ'Õ5|’=êûÝõ¨ß15@8G-ÊuáQ›µž:,±(D𩯶G]ÜÜÄ\ ppppð¿ÀecŽZ¼+Gý’mø­hËQ‡Æ£ö•GÆ5j¦&AŽrò9j™/®ŸÏç¼ËP`Ñpø TsÔé­˜:Ïä¨ý E‰4LÒ(Q¥L³uÍ£Û©ÿk\Ž”£îår!‹ «‰ÐNI`íÄ^!€É¢i>jiÏGMD”œ.Dµö¨÷ÈQ#BŽ”£ö¼¾¦±ïûÒYFiþbApª‘jÊëP-q¨ íëõÊû"èB픎w½^Sƒõº\‘ÚœÏg_aF¢ý|:i:»ÝnÏ&ƒ`å¨Íš‰ò•óQûÁ\{Ôù_îQo÷9jppppðñ¸)GÝ?†Aš–´1É]YÕÕ»ÝÎ,8N ÎÞ5 á(R‹3’´Þn·ü¨íQS3Þz>öf<‡Ã6UÎÛÜšü\0{Vƒ ïí¥ð<¬µñ¨…Y3qæxÔô×àQkM]¸ààààààoçuº¨wŽúß~*†áz½>ŸÏü_âF„o6®¤ñ‡ –ËE…\i{ÔžÖô¦tÇÓÆx7ŒG½Ú˜µpsÔ…œN©,=ê50ääsÔ5Ðhg³sÒ«ÕŠ•œô°q»Ýü‚®yÔÝãiã¼Í9êX„Õ5éë9j]‚ƒƒƒƒƒ¿Ÿ;9ê¼þÝ9êþØív¤yœTÇõzmσëõº\.MÍv» CeM“Æ6ây±XÐGæûýž]nj@s%ê´q®¨Ñ£Û©ÿk|¬õ«üíÚïøj4å¨kk&Z9ê9j`+GýÐàW«4€_ƒ2G½Þ&ÈQƒƒƒƒƒ +Gý¼ ^,óùr~êu$n˜˜<¦Ÿ£àïÀ䨗ìQ‡É-Õ¢y>jä¨ÁÁÁÁÁ'À¿=G Ào‚åQo!µœNÔ²‰‘H‘£¦Š¯ÎQðË`rÔÊ£òª<êD;Õ‰íQ#G >)þ½9j~êu˜\óàG¢=jóQÄWä¨ilëõzæyþl¶ÝnùS@¦—_QK‰«ÅÄfê¡yÈGºqõÃ.à‚ÕàyTrÔ"½†1iivªeÿµ}‹?¯yÔ¦þÝ9êþ ñ¾¹\2½8ËõzÝl6üçyµñð¢äY“TnϵžVW€çQ™ë£ÈQ[ugŽ50Ø£¾ò[88888øûùX9êwxÔ•‡\EÝÛ£¶µ±i@§e³ÙAài¸-áQÀó°<ê½ò¨eSߥ¨3;G G LÓÏQ“Ž=ŸÏuˆØù|Çåø‰›¨ÆÃŒ†·=jÚœ†Ag&kò¨©%rÔðꎭèíu¡fçø¡²MüsÛä±¹Òšëciæú¸\.³Ù,ü«&ÆÄ&­žÏõ5<2GMŠ:µ=ê9jppppðÉòÎõE5x-rÔùŸÌ*9ê´Çš‰“p-ÀÁÁÁÁÿwrÔyý¤:ÿŠ5VŽºÁ£FŽ|š9j¦7G}‹óY©³ÁóQOµÿ 9j¦ƒîõ óQƒƒƒƒƒO’#G ÀtPõ¨íuË|ÔÈQƒƒƒƒƒO€O?GmÏ=›Í6›MÛ¢äÛ¾Z-™Ø³X/—Ë'ÇV¯Á:/ðÊu£Gíä¨#ä¨ÁÁÁÁÁ'ÀÇÊQ÷GmÚ骷 n\¦c¥Å¡pûÇʉð šrÔ¹¨Nݵ@Ž||¬uÿ]××HâÛí¶T«"zó Ãд§úõzeœmÞ÷S[!±iÄ•Y'‘½\.þlæû~ó"æ cËkè”Îçsú/•¼„:X/ãèÖ€ó…=ïr>ÛãY;ã€_;G°GK]V¶l·jÀ¤¢GAèð^ü€‰£Ñ£æ?ä¨ÁÁÁÁÁ'Ë¿1Gm0ŸÏÏç³Ð>p)P­ö¼Í£æƒ<êyÐy3ÿãØ„O n·›é=N¼#ϱdz&ÏþLŽz©<êô %§uYñ¨e—G-à³hô¨EîQ¿1Gݵ?›‘ %å¹ßï=êÅ|.ŠÜ…©_,FÛ¹åãñ˜é0³£6]5Å×ëu¹\ro$§—ËÅí–ÿq»Ýòù Ã0rE=Ó¦s~8Ìxhwô¹€Uš½ìw;WGQdbØðëQzÔ›Â£ŽšŸóLÔMs},]Ç8kö÷{>}•öG þ±$Ïõaê©r¦'úPSˆø¾ÐjŸçú ¨ÒÏf³áÆfªøõ09êÒ£Žš>ýµ^‹û‡ŠÙl¶^¯ã¸Ï)݆î…ιO½Ì÷á·zOöß±‚$üX9êê|Ô‘”Í5rÔÀø+GÝö¢'¼0JÏ 1zÎí\“¸ïžúÉþß-ø`\Ô=j³fbT[31EŽ|:|¬uÇJˆõ–U y>̪ÜF^†aÈÐQiV¬­‡È‹z?쟶Rº}6#é~»ÝLâëõ*«¬®¸ê^]Q-†¸XÌf³óùlŽ·£ª\.ü§±OÛQ|âà aå¨IQgFN·®™˜ G Œ±rÔý½ÖZK0iQæÆï¥š(Š2-­ÞæGã8&™jgEºû§­‡C¦—8‚À´1‚|·ÛN'"T¯Äæ4–÷§ãÑ쥭ÿý~Oúœ´wmü5øÝ°<ê}"Ó{¬lj.GM„„4ÉéP•iƒG=¶Sþ×øçsÔvnÙ8·,ÎíÒnïôP——óùüx<ÒÀífô( ÚÅ|n\ß¶ñÔ¶r9µ1šœÔ;whkûÆ­èQcbW¹­nù°O¸ÓÀ:-)íŽðÕ(sÔ›6Ú'rWr:Ñ¢:QŠz½ÈQãa¬õŸZ3Q§>º¦¡F:U:½')Ô÷"GÍõ-LHKóIjRÔÈQƒƒƒƒƒÈÇÊQÿl·[þàp<»—wÄ À¨å¨ïmuT*jÞËõF G Œ‡±rÔÁ£Vs÷-Te±èš;ƒG ê9êD稓jŽºPÔ7VÔŽGmûàààààààu2¥5µuñ³Dg®jŽºâQ  88888øx“G­ê‘£àó8T=êP»Ó\f<5Ïõ«Y>ø¯m®‰¸ààààà×ꨜzK·­jŽZßÊr€ƒƒƒƒƒ¿—Ó›O–{Ô‰]€Ï£ÈQ'®Gm¯™HZZÉi]ºk&ŽîT€ƒƒƒƒÿ5>ýõ?¬†aøËårµÊgØÀOÿà+Pæ¨×;‘–uXxÔ~ <êX¤Q’r)Ólm¯™(l$àààààààcå¨ûãÄðl63ë¤0º§ƒ`"psÔüWæ¨ý€H$´œÖ¥LÓõ¶âQîT€ƒƒƒƒÿ5>VŽº¿óìYK®V+ÒÆëÕŠ?pår¡Ö%<ŸÏܧ^bñǬ´ÈÍì¥Ì׺{ÅC:|½äâ•×ëõE‡À`´å¨ku"³Xp©.cRÔ9jppppðñøX9êþγi¹ÛíN§*÷û=Wn·[ÖÀ§ãÑZÔÛ«mkj¨“ëõƒ7ø¾Ïk—SIþóAð$ܵùË,ÚÈi"*õ±Ý'ÈQƒƒƒƒƒÇ?Ÿ£6î1;É\ÉâÜ.íöL|µVšzW¥’x­2³ÜlKH×k¨=ûÛ´U\Iäx<Ò¿øP;G-Õ¼ÓÊ+õ+Gýµ…6Ü®ìãQ{ZÌ{¦’NÂ~¿ŸÏƒÅbEÑ?/•£V©H»Óº”™ëQkQízÔ£;àààààQŽÚ/~oØèQSé:Ò®km{Ú.n·›é€ÏÃÎQ+ZÍ—rɳçùú«¥D–Ú£Þ ä¨ÁÁÁÁÁÇãcå¨ûÃÎQG"TçÊív{¹¨\ôápèãQïw;nE‘‰Lácái÷ÞL4ÃÎQ“¢Žôüx\f–GÈ.zt§ü¯ñ/šÚÌõ±ªÍõ±Ts}Nî“£¦ƒÝl63Ï ‚ÀLëAÄÌõ¥g`D˜õj£:ÿ59j~Ü5ÿE¥G­fãQî´®'E­=ê]‚58888øxüÛsÔü&XóQoy®íQKãQÓ_ª<ê\Nsýz³‰ÉQ s£"ÿ—¢ÈQS¥U€Ï#ÏQ'I9×G!ªéÑÙÌ÷fìQKí]ËDhzSñ¨Gw*ÀÁÁÁÁÿŸ~ŽÚó~Â0¬UžN§þ«.ð-09jãQ›¿4µW!Ïå4•T¿Úl…™ëC0Ê88888ø»¹•£NìúI­™h74X,fÑ~ fÍDRÔ2Í=j­¨éQÏxÔʸ–\’¤&E85J”(Q¢Dù±²1G°G}y£GÝßaö~~öû}’”ƒ¹Ýn‡ýÞ³–A\¯×žç­×«Te*óþÏ糯0££;ŸNšÎh[nи#õI –‹Eà××"‚ÀtÀ›Pæ¨W›Ò£Ž…ò¨µ¢žù~Ê©D&ºÌ=jä¨ÁÁÁÁÁÇãcå¨û;ÌԒƳÝlLÍj¹$ lz ½}¹\2VÚ‡½Ùêp8¦Ê™çív[â×ë5r©¼ÛíN§S¦$ûÝÎlÅ]™>ĩ柀ž(sÔ*õ‘ÆVê#ƒG%J”(§ZvyÔïÉQ{éÖ]ü§šÅ¹]Úí©\¯×B‹ÿ8Ž7Z]›m}ß7î±ñ–=K±Wx¹ÕŒ·¢Ò7[y?¼L ù|n¶ZÌçTóŠ£  eŽÚõ¨Sö¨y…ö¨‹õºÌQ‹‰.pppppðð±rÔƒ<êLûÏìo·Û(Šì<­ÑMéößÈMK›Û-W«ÿ"’s³Y::þ &G½ÌsÔ²-G­Dµúq"!Ý»³Ñn6§–Çã‘•[æóùõz5€w£ž£©K«2cE­=j½yšäóQ§«¦ù¨Q¢D‰%Ê•ŸÏQ…q•i¨A¸õ4þÍfãy=J8´-GýSöÖ0×GÕ9'¡N Œ†àݨ娭_ ¦ìQûÚ£RÙÔ\æõì-˜êʺu?ùIdld$‹D"c#‘H,Ù‹DFb‘‘ÈØÈÈÈÜ·ªHQùágíÕPá¬1žº£“’^w7“y&Uô¨a†aì«Gýk&ªOއG½[­ù¨«J÷ÐŒeÝn}XS}oÍDFÆ1öúa.‹~®õ7ãå’­V+yÿ2Y“™ÖË=>ÝÂÈø?3úêQ_ò›1å|_Bÿn=j½fbc§ÕXÕjÍÄHÈ-*SëQmG]У†¿ŠÅë¾\.???â«Ïçóo>ŸeYv=–‡õjåñívµe¿þuöÕ£¹S],æ³Ùlä‰Ðÿžn=êd-ŽZ—¥«¼TÅiÙ6uc§Õ(–:^mr2jƯ;iín·‹ã¥a³BÙb±ÈUÚ|‘÷#1¢ò£°}îá°Ã`>ŸkçÜ=EƒÛ màMãÑœðúŠ“‰\€^-ûmŽ˜Í¦r12Šç7GÞ»sÁïÑݢϳxpžÁëddó8þ5BèßÑ­G¯Ä*7ß@¬L_ZgÔj¢x1Ûâ¥Í¨2êÕº G wÒZ1Àbh O´¿5œ$Év»•7e…ísW«ÕmcïüâKÅËI.MRm‘'š“«àz½2Ûå7ëµI˧ê2Ôñr=ÇãQ^åx8è—~t=úœOî±pRëU’ÈkÉyä2V&?oŸgð:GòoÃL!4ÝzÔ:£v¿XkG}ͨ«›©&£füƱ“ßæ:(6, ß”‹­½\俈«å¶Ï5¹®Œaž_ΰ^­ä)óù\ ¶Ý.Ǜ皓__1˜ØçŠ­5,Öz³Ù¨.Šsæ{×s껫¹Y¾G§èr1ƒç¼NFÆ1ô¨Bã‘Û£®ªÆN댺v3jSS]«ŒzSУ†¿Š;ãkF­ÙØNÃâE:Ï͇ºÊ>Ëår·ÛÚçºk¢…óŠm–K2_oœÍfiš¾r=ï±pRë@Ë\Ãày¯s$ÿv0<Èô¨Bã‘Û£®ZµÚk3êÒͨk2jÆï‡zÔ±a71¶èN&œ7õ+ù­‰² G:Ljmd3‡¨IÂï]Ï+÷ènÑYw·í?xŒŒcÇߣ–¿-û<\ÁüõÕŸ¾î¯œ!ôºœõºjÏ’W·{ÔöQÓ£†¿mZkÚËSU[ÜÄ8I=÷].£Û[^¯ÕŒ‘ÛíV•{ç—‡ýÞZ_yîRݯhúÉòÜ4MmÛMÅ-Ë^ó-Åãñh;Þ÷®çñ=Úí·uë2âþñƒ×9’;d_=ê×%ÿuÏf3³b¸ÝòøøßzÝ_9Bèu¹=j“QÜ£.œ| ‡áq²;µØÝóù–k¹)ñå’]çÖ˜Ï;sb„j 3×G÷üò4±Üj>êÉD̰¸Sû\8ŽÍ<Õzux“Š·XöÎfSy-µzÚáðøzîÝcg»M¡óf*¹¼½þŠbçøÁëÉ¿ òPF­¶ëuö¾÷Í×à@M¤¾ Ü®\¹ù/.^.åv¿1"s˜Ü£l± #ÊöRk`ÍÄÉD>Çñ²î­®(×ðwwŒz®VúAF]w2j§G]hé?hŒŒÿ“ã-ËeddÍ8УÖã»3ê×`s¤8çS³¸}îf³1FW®D¸³w¹\ȇwùp-Ìå²Eì±€|~—½2nÖkû\ëœÍyÌœ™æ<¡w«ß£6uÑΨÝÖGÓ£¾´{ÔcI-`ø×Ùéâz`Î[õÅÝþ¾µ;‹ŽM›9wG÷øZųéÔ„É6=Ž¢È¶A¢&‘¶ç«¼Ýn’$ÿlRnÙbŒt…æ¹2ºÏ mYÜõ|>· 6BèÝjzÔùKuíô¨óvZÿ)cdüŸɨG8–åwdÔ"ÝLjÝ-Ùu:Ç«»Ð ãml³l1±¼=Þe÷ªÌŒ÷ËåÒzl„лµ½“Q_{ÔáPFM†aöÍ_Ñ£¶¼Z©¯ÛçÚlùÞñóùüpØ›š‡îBïeKó\›Q—·ŒÚy®aùýS#ô1 ÍG]åEUÜæ£ÖŽºÛ£Vó¸=jó­¸ýYƒa†á7rÑô¨s-»}Ts}X<›ÍÜõ~¿¯u;zÝt¡ç³™¹)ÑÏn'nÙö¥ïvf—¿Ó,ãÚéQ÷_÷¢¿’ü¶ûCÝd2ê|`>jeƒ0 õçß¡ù¨éQÃ0 ÃÞøó=ê?U'Í–KµI²\y’$A̦SóÅC‘ºøéÔx`቞†¨Ãu3ƒGw®çµ\–“[×zŸº=ꢕQ‹£Žz­Qœ ÷¨Í4†a~7÷{Ôf;k&"„>/Û£V+¼ÜzÔÊT×&£UF-[TF­GÕ£N®­zÔ0 ðöÕ£F¡¾îô¨K§õqͨ­©–— =j†aØ'žzÔdÔ¡¾z=êêšQ7Ž:¸eÔ•å0í¨éQÃ0 ÃÞxü=j„п£¡ù¨+3Ö­o&VíŒzUУ†a†ý1=j„ÐxÔºj­™èfÔæÑdÔ«œ5 Ã0ìéQ#„Æ£Vº¾Ùé®ðºõuû2¾:êš5 Ã0ìƒ zԡѨգî}±Ý£®Û5=j†aØ¿G­×Ÿèa†a’$£ZÁP~Wq‡A…ájµú­k î¯Ò.ÿ^›ÍF`±X<:ÃË«R¾.y]³Ð$BoR§GíÎ’Wë5o«ëeuͨ zÔ0 ð?z2é®ð²|è$?)õ‹Š¢ÃáPk£{<ÅðÿÊ™'÷µøö¼Y§æ¿á?K^W^ý×O‹U«GÝ[ÉE9ê¡ù¨éQÃ0 Ã~yü=ê~Z1iššå×@¬U¬ºCµÐšñ½µ¶¾qË‘q¼´‰ëù|žÍfjùÅÙ,M¯Ë/ʯEoœÈh×dtµ^¯vͲæ ¯ÉèÜ‹e¹ ¹¶HÍaÉí˜-6™ïœV.É®ÞhSèëÂötªoޱ·,coY®j±X„ApØï'Î2”qsÙöH÷÷,¯Nc½ONZ9j³Îø`FÝ™ë#§G Ã0 ûãbô=ê~F-æÐ°¿ý~oXœÞÏÏ€Œ›Æsn·[q¼*¶* kD7›q×â·ÛÙ(†6ËÔ'±Ö6†ƒ*?v6º’îÕ<¯Ç½ËrrrU³étð®­äÞÍ%Ý;Ãtè r1æ–å·g#zãÊl”_‘ý"GÝ#Ýß³¼ºýý#ôëêQ_ǺQË3«óQÃ0 ÃÞÙcÚ˜ê{£•Û£+ÎÖÍNó¦ÿ –Øl—1Ò3ÖŠÄ^ö¯ìµg˜6GŠ•cÙ9xª7úCÇ ¬íkðzÜn³›0Û7“|ÇQ»ÞM¹Î༖}–\ŒµÜaöŸ5x¤û{–½övúuÙµqÔùÍ9«½â¨£h*ÛÏyy¾”f,*棆a†=syͨSµÑÙ>ڌڕkhyðÛyöèv4å–·ÛxÈÅbaÂêZ»ÇÍf3›Mçó¹ÝèêAF=x=÷2êÇG¶Oû_Πϲ÷kÏpç78Gý#û?"ô‹r{Ôâ¢ÏyuºTf¬¯­ùD\ÎÅñ\ιŒ—¢lÍõqý#–çúo Ã0 €2j½}Ì=j+×4:™pù4£~ðrišN{ÈÆÁg%Ibë½W¸ž{=êþÝ›©£•Q¿œr»±üàF7£î鞟Œ½UMZ9꼬çò•ežåOÕµG-pÌ”VùÓUTMZ}‰€5 Ã0üy.¾­GíÊ K×ëµù’ Œë^ZîÈ¡7›±ÁY–Ù#e¯é'ËFëe£¹NÙ5ØHùÉÁæK‹¹>¯Gþñx#eËã„y6›Cm“{=êþæÎluü’eö÷ ×`~ò[²ÏÚ4ëÌ9Ò=?=jôV9=êÍ¥¨´6¦Z}Ñô¨UF}Rvzº\3êË¥5×ÇõZÃ0 Ã`y÷¹fÔ—‹»]gÔc™úÞ®Nv:8ׇøÆþ\â{åHío¯3x\çú˜N­em6ª¹>î}çi^7ŠZóQ^Oš¦r1òºr¶Ç ³ú'˜NÍô®Zs}j†aØ+¿G:Z­’ü…ù¨_—¸è«Ñ9óQ£7ËíQ?Ȩ÷'å¨uF}¹ä¥^…œ5 Ã0ìÇߣFÙ5ÿRI’˜Ï5»Ýn>Ÿ¿òÖLDï–íQ/´£Þëõ¾×£ÖõŘj“Qçô¨a†a\Œ¾GÞ¤4MMÑEìtþ«¡7BÿYî|Ô*£nì´Œ6£.MZÛé½Í¨éQÃ0 Ãþxü=j„п#Û£^Ä:£>â¥Í8У¾fÔe“QßzÔúO#####ã‡Fۣεìv2j„ÐçÕ˨ kª+w>ê[:§G Ã0 {gzÔ¡ñèÖ£ŽW⨔m.~Ò\ ?µZäÅÌGíô¨ -ý‡Ž‘‘‘‘‘ñCcÑëQ›‘Œ!ôyu2je§S-F:#“Q‹£¶‹¼èù¨×ô¨a†a<þµ^{¢‡I†I’Ü[øÛ‹äwÇqQØšúu=X¦¯Á9¨ÿHvª§g\­òÁ³˜ ý½œµÎ¨u:­FqÔe%Ž:šNåÿËÒKyÌÊ4+rli×L¤GÍÈÈÈÈègºcáä‰ËÅâ?¿îïJý¢šµc:k&¾®ç‚6\'ñ$WXèô§g4Ïž•3[5úkµ3êòZùHóxèkF=•mòSz©Œ©Î‹*NÖô¨a†a<þu?)µ¦N,_š¦fYÀ{k&n6k&Æq,GÆñÒfªçóùºfâl–¦×µå×b×L´ ºZ¯Wf©ñ¾¯Gîe¿ßËÅÈ%™ÏnïŽb÷ ½•ííÈÁövÜó¸²K.v^WžÏçæöí‡9F~ir©‹ÅÂ^Àã×eEEô—êö¨uF-vÚdԡɨåÿ“s1Õ•~”EYkGMš‘‘‘‘ÑÛXŒ¾GÝϨÅÈ;'Õ°x¹ŸŸ7Í:ÝÛíV¯‰­ìâÝ›ÍÆ¸k±‘Ûíu©1¹Y¦>Aˆµ¶A«X_³"¹»Ñ•p¯æ1x=r/»íÖ¤ÙÓéÔÞ…}ÖÝ;j–tégÔr˜¹ùÍØÃÜó¸JâØ®±î¾®Üü6êfáuûZò SÙÛüºrrû¯ƒÐÐ@º)~¸µ8jkªeG¼ÚУ†a†=²Çµ1Õ÷F+7M+ÖÎMbófi±Äf»Œr˜Ù(®µïx#õͦë¦Í‘³éT¼wçà©Þh>t êAbðzlÂ\·“v»±}GÑÀz=jëêå0ëÒÝó¸ ÃÐýí ^ùÍ´7 ç·ðøuÝ#úêö¨Ý¹>®õÌ:jcª‹ªŽW&£¦GÍÈÈÈÈègôÕ£~]¾ ×Iwûì:Xw¯²èÍh6Ê-o·q†‹Å„յö‡›Íf6›Îçs»ÑÕƒŒzðz:æùÅ»<ƒ{ö^ìýÞ³ú÷Î/·Ÿ$‰Ü©>ÉÀÕ:/÷èuë;¿s„^Ôð|ÔzÆù,¶3jÙ&c^Ö*£¦G Ã0 ûãoìQ[¹–ÏɄ˧õƒ—KÓtÚ;@6>K\è`¹âÞõ¸÷28kGëŽÂG)·›÷gظ÷1DgÔeÿñÒûýO¡?^9¦}bÎ,¿C7ý~ðºdÔè/ÕéQë%È[k&šŒú\t2jqÔô¨½…§õëz1£^¯×æK‚2®{=j¹#ÛÞl6ÆgYf”½¦`,­'”æe×t:`åw%›/-væú¼žAó<ŸÏó¦¡ñôŽúµ­…_²¬ßvîÈíQ»¯+þ_ØL¬çfÔ;Ý£þùù±ðøuéQ£¿”“QoTF•‡³zˆ£nÖLt3êú–Q_ÚµùƒÆÈÈÈÈÈø‘QÞ}º=j=Ž(£¾ß"p ê½¹>Ä öçúß+GN§S;ƒ‡ùFžšìb:µž³Ù8qgÀèH¸y]1¥î|ÔÃs} µ#ä·-ç_è)ŸÞÑ`²Ý¿{Cì\×=ê9=ÌL€Qš—“×:6s} ¶¯û¯Ë\è/e{ÔjÑ–²VvÚ˜ê¬ìgÔz|УÖÐ`†aøý|íQ§¶G}Ý>žŒý®ì|Ô¿®œù¨Ñ_«“Q3SkSMFÍÈÈÈÈ8ÚñÖ£kF~WvÍÄ_k&¢¿×µGm3je§«{µùrâ­GÚµþS6ŽÔ†aþ¸îQçdÔ¡Ïë–Q‹I.¯µɨG;zéQW•* _.y–]Îgõ8Îoz-„Ðé~ººÓ£®éQÃ0 ÃÞùc=j±Ð⟳,ÿœeEv)/ò†˜×‡Kušâ¨B½ù¨›¯%Ò£fddddóø±µ鼨ÏâŸóú'¯7—:>×ó´ŽöõìPÓŒ .BÈö¨í|Ô{=užŒô¨a†áÑrñ©µŠ¦/õ¡¨·E½Êë8«—ÚQ‹žþÔÇSQ¼g „Щ“Q+;}ºšj2jFFFFÆÑŽëQŸNç<¯w—j]ÔÉE9êÅIgÔ?u¸­öiq¹<2ðVN‡©–]ÁPîh¹X„JÁj•Øùœåfw»]g^hµ”öDúÿüí­"„þ«lÚdÔ?jýqõ_M†a-ÿMú$§½dåxé‹rÔËs½Hë龎vÕd-]WePŒîñx4K˜%QÌJ.I§ijš$²Å.ä|oáo„_ÙŒz¯³üæ¨å!ÿ5áóŒúú'.¿ †a†ßÍýµÑ¯gÔò*çs~Ȫe“NO”7Õÿ_UëCõ¸CbS從ÏçYv½Ôì|6‹öžÞZ•ÛM¼§ß¡OjÛô¨çË•8ê]šÿ¤¹éQÃ0 ãåâ/zÔª4=e—zyªgGNë¾Çd]ý¿¤Ü¤jïƒçZ#ízcÃaºßj”;Ͻ\.‹ù|ðl¢HK­?>›ÙÕ´B^ädÔâ¨Ë²ÓcªK•QG÷2êK»G}ýƒ–ç0 Ã0ü¶=jy?r·?Ψ;Ýc³Ñ„ÌîØÑùœe—*9Ö¡xé]=Ùˆ£®—ûz®ÓSþ$£žÜͨ;µN-w·X,:•·Gm%wÇ1ËÖ äQ¦G«Œ:G-{wÌe£8ꢬÄQÓ£†a†GÈŧzÔú®Ì²b}¬ƒ]=ÝÕ‡S™¦çã1-žMôñß2ê²,c]¨¾w¶ŽäøNšú¤2jm§eû㌚5 Ã0ì‘?Ö£6:Oi&ošÕé\eY¹ß¿d§ë‡=jñÀ6‚βÌö¨UÙc±°ëÖÙ8ê¡6Bè3rzÔMF­í´øê;5=j†aØ?ìQתJ}Î2奋²VÓSóôTž²úp¸<ž=Ïéétz8äSÀz½2µ™ë£ÔZ.—G=×Gš¦ÂůîÚò$IÌÿX,OO’xÐ#„>£{õ.ífÔöˉô¨a†aïüßzÔÿMÚ®«…ÏNç✕iZœÎ•8êŸ}öós¼ç~lª,VÙ|—P®Ùãþ|Ôצ·Ó÷nÍéQËy‹¹ùfb¿‚ú¤œµšZŒ´±Óô¨a†á1sñÁõù|µèUUçònx®~~Òãñd—_AýãêdÔj&ê4ÿ9=Ÿëƒ5 Ã0ì‘˲»f¢Ñ;2jqÎç³ré§s±ß§ÌªêÈö¨¯k&*;]ìô8<õe¨Gmþз?w0 Ã0üV¾fÔ§vF¿«G}¹\öûããÊ4B蟕ͨ—ÉZõÞ,A~.Œ£îfÔ—ê”Wô¨a†aïüÉ5B=–íQ_3j±Ó©n2jå¨ÏW;m3jÓ£nÏõaþ Á0 Ãðû¹Û£n¶¿)£¶¢òêk{hgÔçR¼´zœÄ8+GF‘ʨ³R9ê‹ó²RŽš5 Ã0ì?Ù£F¡Çêô¨÷Måã–QGÓªÒŽZ?R=gœèŒš5 Ã0ì‰?Ü£¶"£FõÕíQëŒú ÇkFFâ¨Ós~:é¹8eE^¨Œš5 Ã0ì‘¿¢G-×ÇqQ®V+;Ûžk:&¡Ú•Ø]ýUíÔûýÞL^}ÐkÁ „Æ#§G½n2ê«©n2jå¨O§<=]MuQV׌š5 Ã0ì‰}õ¨_—š;ŠŒûó<“$1»Ìš‰Vò¹`¹\žôµÙ"çY.fíuµÀâñøö@½¬Nú•â¥e_ÝdԡʨÓ,=]ÔCÜw¡5=j†aØûêQ»«>Öz½Úívƒ»ú¶ÙzìþùMj½X,Îç³Ù’eÙ|>ñ2BЭG-&¹¬6ã5££ª¬Né%M/&©¾eÔô¨a†aOì«GÝ7Ã÷EѽE;uš¦qß;¿Ù†¡Ý"%ÜBÞe{ÔjÑ–²>f*¦ÖcådÔUšf§“2Õ6£¦G Ã0 {äÏ÷¨]}¶ýg³Ñ|QÑãƒþIŒz=êÕÓuçlýÃBe{ÔK“Qk;mNº:éÊÇ霋£¦G Ã0 {g_=êwdÔ­]}GÝdԕʺ”"2j„Æ$Û£¾fÔS/ÊQ·æúhìôé\У†a†½óø{ÔI’ì÷ûÁ]lùl6s×:–-u¯G½ GИd{Ô&£6vúÐʨÍ|ÔÅIO—f…:¥G Ã0 {b_=ê×%×E‘™”ãñ\®ä01ÏòÜZÛiaón«æúXع>ÌõШÔíQëtÚ$ÕnF­µZÞEfÍÄK;£F!„>©¢×£6Õ|ÔY–-—KñÏQÔúÁ³ä¦æóyLdt»ÙÌGÐh5Ø£Ȩ/åu!rwÍDzÔ0 ð'6uúñ5k&"„úræ£Þ\ŠÊÚi±Ö׌:Š´¾šj2j„BÞe{ÔcΨBÿˆL:oÍG]™ÑͨÏNMF½¢G Ã0 ûd_=j2j„P_¶Gm2jk§·5uúL!„Јô=j„Ð?"Û£6õþ¬L4c«G}˜ë£»f"#####ã§Æá55Bȇäƒ|“Q¯uF]ZSÝZ/Anç£ʨݔ†a†ßÈÍš‰i“Q_·“Q#„>¯[:VŽº±ÓÅÞfÔaÍÄ;óQ322222~j^3‘Œ!äCNZgÔ¦ò‘¹µZö4M3cªÅQ;uÚdÔ#J-`†á;=j›T*£– Šã8 ‚(ìÎGL”‚`ª]‰Ý5° y³J£Üïn·{}t„ÐÇtëQ«Œº6ét»G­3êô’¦×âGQVô¨ýŽnÚÍûÑxÖL”Ë‘7Q³Ëã5åâ—Ë¥á¾a¶[Ä™/‹ë-"„|©Û£>ßéQgÔô¨a†a?ÜéQj†ê2Ú&ÆOµ^¯v»Ýாm¶>¹þNjMFÐåô¨WÃ=j“QŸ¯_KLÏ=jFFFFFïc§G7ÛßQ¿nhå Ôv9:êäÌò~Çñ½ów¶Q#4B ÷¨ûõùj§ÓLÏõ±¢G Ã0 ûäVZÞ’ÒÔ¼½/£tõÙöŸÍFóEEwtŽ¿k}{=êÕ+=jûÜ¿½„Ðoë…µžºYÞ…ù¨Ç0væ£.åÝ©,‹į̈[»úŽšŒ¡ÑëyZµüÉÊJm§Õƒ5 Ã0ì;=j»râxzÔI’ì÷ûÁ]lùl6“{±? Ë–Ÿ‹ò¥çóQ›ŒúR)S}QŽš5####£÷ÑíQs®¹°(ŠŽÇcýl®WrØb±çÖÚN ›w[+2j„F¨çóQ7uz)YÓ£f>j†aØ+wæ£.të#Ù|ÔY–-—KñÀQÔúÁ³ä¦æóyLdì/(CFÐuëQ'¦G]v{ÔS•Q§:£–Çñ\äeµLV9=jFFFFFc§Gm·³f"Bèóêö¨³j0£>Š—ÖvZ—¢GM†aöȵÝ>ªŒ!ôèÖ£G]ÖÖNËèö¨MåC‡s®2êXeÔô¨}ù¨ív2j„Ðçåô¨7&£>(;-`3j5µµÓòp2jzÔ0 ðîô¨ív2j„Ðçe{ÔËd—µµÓǬjeÔ¦òa2êâšQwzÔF0 Ã0üîô¨ív2j„Ðçe{Ôñj#Žúx©ÄK[µéQ+;M†a Ó£FG¶Gm2jk§»õÐ\¶Gmþˆ$µ€a†ÿìQçdÔ!²=êVF­ÇÞ|Ô•™ñCµ>Ì|ÔiÓ£vó†a~?ƒõe\=j¹¢8ŽÃ ˆÂî|ÔÁD)&¡Ú•Ø]«ëUÅÉ/‹P)pGAÛvZyæÆTßæ£–ÿóJµY9Q[ê5éQÃ0 þØWúuÉN£èp8ÔÏÖLTny¹4Ü_ÀÅlIâ8ÕA–°œSŒú»¯!ôº:=jÕîÐqÔ­ŒZìt~ÝuwÍÄq¤0 Ãð¿À×uÚdÔÍöwgÔ&1~Eëõj·Û îêÛfë±ûçï§Ö5k‘#42uzÔÖNË(–:RõLõ9¯Oú!Û‹ªŽuFM†aöžzÔ¯/EѽnFÇËû©ÍœïeÔ®äCÄb>ñ2BÐöÐêQÏlF7£6vú¤“j9L¦G Ã0 {äÏ÷¨]}¶ýg³Ñ|QÑãïÆÈ½õêiÚJîz±XœÏ翽„ÐïÉö¨ãÕº¨nvZÆVF](G­“jQ¯èQÃ0 Ã>ÙWúukWßQ;[䄱.T¿x ¡ÏÈö¨“Õ¦(›,Z?2jý¸eÔô¨a†aO<þu’$ûý~p×[>›ÍäŽì²Åòb±È²±Ìd‚²²=j1ÉEÕuÔÑtÖ˨k›QÓ£†a†}±¯õë’‹‰¢èx<ÖÏæúp%‡‰m–çÖ…6ï¶2.—Ksס±Éö¨“õV¬òÕ9µÙØÊ¨éQÃ0 Þø+æ£Î²Ll°øç(êÎGýàY§Ói>ŸÁDFÛ;¶¸.÷»/!ôºœõ5£¶¦ZgÔíuÑʨéQÃ0 þØWš5B}ÝzÔMF}}¨ÿ¹I9ê[FM†a ûêQ#„P_ýu;£žÝɨ79=j†aØûêQ“Q#„úê÷¨í£V3ÿУ†a†ÇÈ_Ñ£Fý#zÖ£¾—QÓ£†a†}2=j„Ðxt·Gm2jzÔ0 Ãð(™5Bh<¢G Ã0 #Ó£FG=êœ5 Ã0j3§tLBµ+±»V!׫4Š“_,AÈñI’Ü[ß!äEÌG Ã0 #ûêQ¿.¹Âi‡úÙš‰â–—Ë¥áþÒ-f‹<×Y¢ívkGANzS”-ç\w2jzÔ0 ÃðhØWÚ$Ưh½^ív»Á]}Ûl=vÿüýÔº~¸Ž9Bèóê÷¨íƒ5 Ã0êPÏG½^¯Tì…lÚdÔǬԦZe¥5=j†ax„ì«GÍš‰¡¾:=j›QËH†a-ûêQ#„P_õË5=j†aØ'ûêQ“Q#„ú¢G Ã0 #E!ô¨×£¾ÚizÔ0 Ãð˜™5Bhj†aø™5Bh<²=ê8YeoÍÄ)=j†axŒL!4Ùu²Þå5…6zÔ0 Ãðhù+zÔrEq‡A…Ýù¨ƒ‰RLBµ+±»V!o¯Ò(GFQôöKGý‰lZLrQµõ£Œš5 Ã0ì“}õ¨_—\á4ЇCýlÍÄÓé´\. ÷él‹þx„Ðçe{Ô&£v³(¢G Ã0 ’}õ¨;‰ñ­×«Ýn7¸«o‰­ÇîŸßM­~~¶Ûíë×€úŒn=ê&£¶¦ZŒt4Ñ£†a†GȾzÔ¯çÃQÙ.GGŒZÞOã8¾w~»E>;,‹?º„Ðgäô¨·b•Å3Ÿµs>«?T팚5 Ã0<þ|:ÐÕgÛ6ÍÝÑ9>èŸÄ¨×£^=íQËÍÎçsó9¢ Bȯú=ê×2jzÔ0 ðOöÕ£~GFÝÚÕwÔz‹¸nó? ÿÑ5 „>#§GÝdÔ…“QOéQÃ0 Ãcäñ÷¨“$Ùï÷ƒ»XâÙl&wd–-u/!ÇT#4*Ñ£†a†¿‘}õ¨_—\LEÇã±~6ׇ+9l±XÈsë¦8m£i+ì4BcÓ@º G Ã0 ¿b>ê,Ë–Ë¥øç(êÎGýàY§Ói>ŸÁDÆÁeèQ#46ýY:§G Ã0 ‚}õ¨Y3!Ô×ݵ™Úö¨õºäfÌËŠ5 Ã0ì—}õ¨B¨¯ç=jqÔ•N§Sm3jzÔ0 ð/öÕ£&£FõÕïQ[S-Ž:T=êHeÔY©µo5=j†aØE!ô¨ß£¶ªR­“Q+/§‹ŒeQÖqB†aöÉô¨BãÑ­G-Žºl;j“Q‡‘8êô”ŸÎEz.ÄQçE¯Öô¨a†aL!4ÝzÔÉZuªËÒfl2êHÿÏjù©1Õ6£¦G Ã0 ûbzÔ¡ñÈö¨Õ¢-:£¶¦ºÉ¨CqÔišN—Tç\eÔÉš5 Ã0ì‘¿¢G-WÇqQØÚ®~ª]‰Ý5° ¹^¥Ñ]-Ñðçn!ôL¶G½LÖâ¨Y%^Ú<ÊJ9ê(ÔuzIÓË锋£.ÊŠ5 Ã0ì—}õ¨_—\á4ЇCýlÍÄÓé´\. ÷­²Ù‚…Fh̲=j“Q[;-ÖÚɨåOV&Ž:=]Nâ¾mFM†aöľzÔ&1~Eëõj·Û îêÛcë±ûç7©õ믋ú¼lºÉ¨Km§Ë[F}íQ_í´›QÓ£†a†}±¯õëY±¼Ú.GGŒZÞOã8¾w~³%Ò’'Îf³£Î½BãQ§GmÒévFmæúÐvZµ> zÔ0 ðwþ|Úí0ÛÄØ|QÑãƒþIŒz=êÕÓµU®»Ù|A¡Q©×£îgÔz>êsqÒSç¥YÁ|Ô0 ðwöÕ£~GFÝÚÕwÔ½-rÚù|þâe „> zÔzÍDm§Õ"/ÌG Ã0 €ÇߣN’d¿ßîz`Ëg³™Ü‘ýQX¶tŽG½X,^¼ „ÐÔíQ7vÚdÔQ“QŸ›%ÈÝ5éQÃ0 þØWúuÉÅDQt<ëgs}¸’ÃÄ-Ëskm§…Í»­<×4=ÄN'qœec™!!T÷{Ôýù¨#Q7vZƼ¬ä`zÔ0 ðGþŠù¨Å÷.—KñÏQÔúÁ³Ä9Ïçó ˜ÈhûÒâ®CýÍDã±Bã‘íQÇ«¡5mZU>º5=j†aØûêQó•@„P_nºÐk&ÚGk®Õ£.èQÃ0 Ã#a_=j„êËö¨•£®ºŽ:šÎÚs}î\ô¨a†a_ì«GMFêËö¨“µrÔçByi3>šzÅ|Ô0 ðOþŠ5Bè‘Ó£¾fÔÖTëŒúÑš‰ô¨a†a_L!4ٵͨÍ㤦íQŽ: C•Q³4U¦ZõmÍDzÔ0 ð'¦Gú=êvF=‹BQ‹NÅB«â‡Í¨éQÃ0 þ˜5BhÛþ³ÙhrcwtŽú'1êõ¨WO{Ôr¶$Quk¹ëõz©FhTz>õTgÔ—J™ê‹˜ê²(ë%=j†aØ+ûêQ¿#£níê;j½EŒ·Ý"¦z>Ÿ¿x¡¨5uÙZ3±¾fÔS“QËãØdÔKzÔ0 ðW:I’ý~?¸ë-ŸÍfrGöGaÙ"°X,Ì×kí¨åÇW¯!ô~õ{Ôöáö¨SQ+S}.òRõ*§G Ã0 ûc_=ê×%EÑñx¬ŸÍõáJ·,Ï­µ6ï¶f»¹kZM·õJeÔG6=êcc§åq)JqÔô¨a†aüóQgY¶\.Å?GQw>êÏjæžÈèv³ÅTÏf3qé÷¢o„/ÝzÔÉZµÊ¢/jTŽºR­kFmìtV䨢ZÆ*£¦G Ã0 ûb_=j¦°Cõe{Ôñj“+GmLµz4µêQ[;-•QÇ+zÔ0 ðGöÕ£F¡¾lZMßQêvÇ¥’ñx©ÊJ9ê[Fu3jzÔ0 ð/öÕ£&£Fõe{Ô&£>j;­MuÕîQGzÔ0 Ãðhø+zÔ¡D¶Gm3jñÒf¼fÔS3×Gytçú G Ã0 {ezÔ¡ñ¨Ý£®tFm¥Û£VSçiS}d>j†axL!4õ{Ô¦øÑɨOùõëŠîš‰ô¨a†a_L!4uzÔªA­'Ð;¶çú0óéS—Ul2jzÔ0 ð'þе\QÇaDaw>ê`¢“PíJì®UÈõ*öxûÜÏÝBè™:=j;µŒMF=Gm—}‘]EYÇ:£¦G Ã0 ûb_=ê×%W8¢ÃáP?[3ñt:-—KÃ}«Üß"ÇÛS!„Æ Î|ÔÚ3_G7£nu%•Q¯èQÃ0 Ã>ÙWÚ$Ưh½^ív»Á]}“l=vÿüýÔz±˜Ë]¿x¡Èö¨ÅQUD7«GәɨÏEcª/•&Ž:§G Ã0 ûc_=ê×ëQÙ.GGŒZÞOã8¾wþΖóùdFD¶G¬·ÊQkçlüs7£nLu^Š£ÞУ†a†=òç{Ôn‡Ù&Éæ‹ŠîèôObÔëQ¯žö¨­æsj„F'Û£¾eÔ©ÖõÔͨÍh3jzÔ0 ð/öÕ£~GFÝÚÕwÔΖ󉀡1Êö¨MF­ŸÁDF·›M@ÐhÕïQ¿–QÓ£†a†}²¯5k&"„úê÷¨Íãd2jzÔ0 Ãð(ÙW!„ú¢G Ã0 #ûêQ“Q#„ú¢G Ã0 #E!ôˆ5 Ã0üL!4Ñ£†a†¿‘éQ#„Æ#zÔ0 Ãð72=j„Ðx4УÎéQÃ0 Ãcç¯èQËÅqAvç£&JA0 Õ®ÄîX…\¯Ò(¨S…r² I’{ë›#„¼ˆ5 Ã0üì«Gýºä §Qt8êgk&žN§e³bñ³e±Xìv»Jk»Ý.Y<¡1‰5 Ã0üì«GmãW´^¯ÄîêÛfë±ûç7©uÇ„?XÇ!ôyÑ£†a†¿‘}õ¨/ î*Š¢{ÝŒŽ–÷Ó8Žïßl‘‡ƒÍ¨mÜÐÿ±w¶Þ­2[þ"±È‘‘±H$‹ŒŒ¬­DFb‘‘‘X$2™»aš)eH ïm2°Îó¬.ÖS>¦Ð{ßÃ>ûü2s€5Žã8¾DŽÚk£Ï&ÿ¬wê*v·ó¶‘­õvJŽz½^ë{!G 0+ÈQã8ŽãKtW9êWô¨²+ê{:Ë2ùK„ø~¿§G 0+æ¨ëæ(9jÇq|ž>ÿµ½RzR–o6y"ó­¸ì¹‘£˜7ä¨qÇñ%º«õtäf”R‡Ãá66×G9- C¹öÖ–ÓâúmEaúù©{ÔR¨3×À¬ Gã8Ž/Ñ1uUURúJý¬T>ê'WEç­dk²Ùrm’05ÀL!Gã8Ž/Ñ]å¨Y3lÈQã8ŽãKtW9jrÔ8ŽãøÝUŽš5Ø£ÆqÇ—è‹ÈQÀ?9jÇq|‰NŽ惣.ÈQã8Žã³wrÔ0ÈQã8ŽãKtrÔ0å¨ ]Q“£ÆqÇgé‹ÈQËÅqì{žòûóQ{«Ï[ùÍ¡ïù¥V!¿¯Ò˜eÙz­6›îhÀ| Gã8Ž/Ñ]娧#w¸V*ÏóÛØš‰EQ˜5íÅ_ôž²(v»Œs9Ÿ›•_(ªæ9jÇq|‰î*Gm:Æ£ìvÛ4MÙe³©±íñu×:ŠBÓÇ®ª* É·o€5Žã8¾Dw•£~¾€x¥Ô£µÂ{=jyŸÆqüh|½'6ò7½GÄ÷ýÉ· /‡5Žã8¾DŽÚk£Ï&ÿ¬wê*v·ó={•£ÞŽæ¨Ë²”Óôk7I’é­rxä¨qÇñ%º«õ+zÔ?Ùõ}Õ›Íf½^ï÷{|âmÀ Gã8Ž/Ñ矣N’$˲ÁCOÊr©™å‰Ì·â²§wŽ”Ö&%sàW9êæët5=jrÔ8Žã¸+w•£žŽÜŒRêp8ÜÆæúè"§…a(×ÞÚrZ\¿måZ)¤o÷ûTÍe†@¸ý&G­Ë颾’£ÆqÇû"森ª*Š"©Ÿ•êÏGý䪢(‚ ð¼U3KÞ=›-uµÒ2”lYe`nLÈQ7uùUNÿèQ“£Æä^û™"Ùú¾//8ùïŽ//ÍÝn·^+ßóÖëõLJü•³ròŒ½ýzÏ;ïÇßæ®rÔT³`3!G­šuui*êv[_®RQ“£Æ—åRNk?UUš~*ß—×â_AY–Ú¥ºÎ²l»Ý:|F³ÿkÏ ~ÿ8þçî*G `3ž£Vëëµ­¨Û¯cu9_nqBŽ_˜÷ºµŸŸŸqk—²Sjà0 Ïm·YDNŽÂPÜ\+'ø¾/•³ÙÙ_êóÁýuó’?Eí€2ìýߦ›Ÿ˜¦©\¥”’‘õù‡<_¯×^ÛåÎö{=ÎÃûÙï£6iù¼#mºÖ柞åªÓÐ8ƒ÷ù~Ï8þ6w•£¦G 6ã9j_IE},ë¢<å«:×ç{š5¾ïuk«ª” Y»ç­¤ÀÖž$ÉÇLJœ&Ûm’˜k·Û­\cêðÞøRK,E²éT›sd@ÙjÏÙn½ß[­v2àé$‡ÖJéóå~‡ƒìÌóüë§<¾¹pôëN×zÛÜÆgýu[{œÁûœÉÿv8>è‹ÈQÀ?ÂxŽZ©ö¬úX|ÕçËU÷¨ÉQã ò~ÆXÊÚ{GW ˯HÛmÖog©ŸÅ͵UUéN¯j àñe©T¥<‚@ŠR³_Î××ʰëûµ÷B·üîm{§kr3þ£û‘*bðuV¼»ý§mMËhrKö8ƒ÷ù~Ï8þ6'G óaq|ÎNŽæÃ„µ½^›u[TÿèQ“£Æ—ã¦[ÛÎõ‘JyOzüèK™½Ûíä[Ù&ܲ|+W~||è:¼7þ6I²ý^êm¾,K99ŠB}ŽÉ'“ÁþÑ¿»ÍóüÔv¹MÆûÑý<F³ÿ;G½ÝÊ_"ôàƒã ÞçLþ·ÃñA_DŽºnÿÓö=OþvÜ›Úü«ßJÌ¡UÈï«4Êóʧ½¹¬óöCÍÍ'š¬Ïo`rŽº‰|e-59j|‰Þ™Úkæ£.ŠÁ.±™[#lçÖ0×JÁ¬çúÐãÞø²óc·“ÿXÚu”þ£>§™C#Šä‡ÊûnŸÁ£Û7že{3ׇԽÏïçÑ3öö›.´Ü¼Öõàröùƒ÷ùþÎqü¥î*G=¹ÃµRRñÞÆÖL”?‘ä?@íöâ/fßþQнVÇÌ.-2‚þYð~&ä¨Û¹>t9]HE}&GÿkN®Çgè®rÔ¦c<Ên·MÓtð]6›:Ù¿×µî^«§ôÔ®ÿ>ñÞào™:uyn¾*=×óQãÿ–³ò ŽÏÐ]娟/ ÞE)e²=z=jyŸÆqühüÞžîµ:™Ùývâ½Àß2q>ê¶œ¾«fû½f"9jüßpVÄñúûsÔ_³åÜóÏz§þ bwÛ9ß³ÑX9ê픵¹vПÿDx)rÔmút1 ‘›59jÇqÜ•»ÊQ¿¢Gýã]Q?.›=j€y0ž£VJþ[mË鯢ú»GMŽÇqwäóÏQ'Iòhþ'eùf³‘'2ߊ˞G׆aP–¥vrÔ™š£þ*§/ÇNš5Žã8îÊ]娧S·3½‡ÛØ\]ôôrí­-§ÅõÛÖÀ\3dbŽúHŽÇqŸ“/b>ꪪ¤ÐmçÕìÏGý䪢(‚ ð¼•líez×fYÆ|ÔΙš£.ÏDznæú¨Îä¨qÇqçî*GÍš‰`3u>êûäÌGã8ŽÏÁ]å¨lÆsÔ~gÍ͍6k&’£ÆqÇ]¹«5=j°™£nfæ9ªãñÔ|•õwš5Žã8îÈ‘£€„ 9ê¶GÝ”ÓU¯GMŽÇqwåä¨`>LÎQWÇ6øñ£GMŽÇqwää¨`>L˜ºíQ—MwºñãÌ|Ô8Žã¸s'G óaò|Ô_åô±jçú`>jÇqÜ©/"G-wDZïyÊïÏGí­]u9ÝíQ“£ÆqÇ]¹«õôòU)e²=z]eyŸÆqühüÞ»#MÀ9ã9ê¶G}hÔzË|Ô8Žã¸sŽÚk£Ï&ÿ¬wê*v·óºVŽz;%Gm®µGûÈÄuSN7Kl¶õå%Ûš5Žã8îÎ]å¨_Ñ£þqÈ®¨éQÌž‰óQçE}(ë¼”íùt¾HEMŽÇqwèóÏQ'I’eÙà¡'eùf³‘'2ߊ˞ç×Ò£pÎhŽÚWëËõ*µ´Õz[Ÿ¿zÔä¨qÇqWî*G=¹¥Ôáp¸ÍõÑEN ÃP®½µå´¸~ÛèQÌѵç«Ëõ–«¬8eÇS^œ¾{Ôä¨qÇqG¾ˆù¨«ªŠ¢H*^¥úóQ?¹ª(Š jÇqܹ“£€ù0u>êò|¨Î‡v{jfÏÛ‘£ÆqÇ:9j˜Ó棾O—ûBäçóå&uMŽÇqwçä¨`>Œæ¨}Õô¨M9-RŸ¯±îQ“£ÆqÇù"rÔrGqûž§üþ|ÔÞªÁóV~s(1‡V!¿¯Ò(Ï›¦iwöi)ïõ|×2H’$×ëåÅÌ樥¨–Šº-§¿ŠêúÞ£&Gã8Ž»rW9êéÈ®•Êóü6¶f¢.Œµ?YÀE*ó0 »×Ê€Ý5Í ðfÆsÔŠ5Žã8>;w•£6ãQv»mš¦ƒ‡ì²ÙÔÉöø½®õ“YŽÀsÔƒs}£ÆqÇ]¹«õô%¿•R&ËÑ£WúÊû4ŽãGã÷ö<*›åoAL¼7ø[˜Çq_¢¿?GíµÑg“Ö;õ»ÛÎù;ÆVŽz;%Gm®3 ò,õDðWL˜ºY3QjéìøUT›5ÉQã8Žã®ÜUŽú=ê‡ìŠz¬G-³ˆ¢HÿÃ18a4G­{Ôûc%u&Ûâtª/RQ“£ÆqÇúüsÔI’dY6xèIY¾Ùlä‰Ì·â²çɵòÌaTÕ\¦7ø7™£n{Ô÷rZÄô¨ÉQã8Žã®ÜUŽz:r3J©î\æúè"§…a(×ÞÚrZ¼×|î^[…œð¨ocjŽú^N7=jrÔ8Žã¸k_Ä|ÔUUéù¢•êÏGýä*©“ƒ ð¼•líeº×vóØÚÿú `rÔÍ\yQçÅ)/e[“£ÆqÇ»«5k&€Íø|Ô~;µTÔe­'ÐûîQ“£Æqǹ«5€ÍÔù¨«Ëá¾ÈK;u³f"9jÇqÜ•»ÊQÓ£›ÑµîQK-­— —Òº>_c½f"9jÇqÜ‘/"G ÿ£9j)ª¥¢>ž¤œ¾èm}¹é59jÇqÜ•“£€ù0ž£VJJkÝî÷¨ÉQã8ŽãŽœ5ÌrÔ8Žãø5̇‰óQ·}œõŒí\ä¨qÇq—¾ˆµÜQǾç)¿?µ™GÚo%æÐÀ*ä÷UåyÓ4íÎG}<Ã0ôjrÔ8Žã¸Kw•£žŽÜŒRêp8ÜÆæúè"§…a(×ÞÚrZ\¿m ½¹>tÔDÆ'G àѵï«k3u³¼K³-Ïßk&’£ÆqÇù"森ª*Š¢6äÜŸúÉUR$Aày+ÙÚ Ê ÎG­ç»–_È_?Lb4G-Eµüzl– ¿´Ûs}¹é5ÉQã8Žã®ÜUŽš5Àf4Gí+%¥õ½œn¾¾{Ôä¨qÇqGî*G `31G}< ô¨ÉQã8Žã®ÜUŽš5ØLÌQÈQã8ŽãsòEä¨àaÂ|Ôçú Gã8Ž»rrÔ0¦ÎG}d>jÇq|FNŽæÃ„õ÷š‰Í¶³f"9jÇqÜ•“£€ù0š£ö|_*êý±’ŠºÙ§S}ïQ“£ÆqÇù"rÔrGqû÷ù¢»óQ{«Ï[ùÍ¡ÄX…ü¾J£ãøÑø½=½kå/º¿MÀ!rÔí\RK—uÞnÛ¹>˜ÇqwéïÏQ{môÙäŸõNýAÅî¶sþÃ×ÊQo§ä¨ÍµÆå7þË=j‡üf>êf‘—f…æ£ÆqÇ]»«õ+zÔ?Ùõãõn·=£cÀ«ÍQûªY3єӲ=™5ÉQã8ŽãŽ|þ9ê$I²,<ô¤,ßl6òDæ[qÙóèZÝå¶;çðfFsÔJ÷¨«KSQ˶éQߤ¢®ÉQã8ŽãîÜUŽz:r3J©Ã¡é!?Ÿë£‹œ†¡\{kËiqý¶5<º–5€CFsÔÝu³•ŠÚô¨ÉQã8ŽãŽ|óQWUEͼvJõç£~rUQAxÞJ¶ö‚2®%G à‰9ê¼ÉQ×:MMŽÇqwî®rÔ¬™6ã9꯹>N_è}ÍõÁ|Ô8Žã¸Kw•£°™0õºY3±]-1/š^˜Çqwî®rÔô¨ÀfÚ|Ô×ý¡Ü«ý¡Y9ñ{ÍDrÔ8Žã¸#_DŽþ&ä¨Õå"µ”Óe&Eõ±jR19jÇqÜ¥“£€ù0š£Ö=ê4/Û¢º’¢úTŸ¥¢&Gã8Ž;trÔ0&ä¨MºÒ=êúÞ£&Gã8Ž»rrÔ0&稿ÊirÔ8Žãø|9j¹£8Ž}ÏS~>j³Ê¡ßJÌ¡UÈï+!Êó¦iÚ_3ñ>ŽöW? 2m>j3×GÝëƒ5Žã8îÊ]娧#w¸V*ÏóÛØš‰EQDQ¤Ý^¨Åì‘Ê< Ã“0rÔõà|Ôu[N7Ûv>ê9jÇqÜ¡»ÊQ›Žñ(»Ý6MÓÁCvÙljc{ü^ç¹×£žx3ðR¦ÍG}kWK¬›•‹º]3qW“£ÆqÇݹ«õô"V5Dº êõ–å}Çñ£ñ{{º×ª_ö¬×ëÃá0ñÆàÏy˜£®¿rÔÍ'›UÈ¿Êi½fb¬{Ôä¨qÇqGþþµÉ*ëܲީ?¨ØÝvÎɰrÔÛ)9js­= ü6¢(*Ëò·ÂhŽZµ=ê{9Ýö¨ÉQã8Žã®ÝUŽú=ê‡ìŠúqº‹üý"‚‰÷ËÔõñ¤?œ(Ûï¹>ÈQã8ŽãŽ|þ9ê$I²,<ô¤,ßl6òDæ[qÙ3åZ©ÞÃ0œxoð·LËQ_¥–ÖSçu×L$Gã8Ž»rW9êéÈÍ(¥t¼ùù\]ä4)ŒåÚ[[N‹ë·­¡{­ ¨£&RN‹W©7Œæ¨¿ÖL<”ûCÙ®œXVfÍDrÔ8Žã¸#_Ä|ÔUUEQ$5°Rýù¨Ÿ\%Erž·’­½ L÷Z)¶õ|zA°éÞðN&ÌGݬ™˜æ…ù:Õç0NjrÔ8Žã¸;w•£fÍD°ù‘£¾ ä¨=ß—Šú3;ê/©¨«S-59jÇqÜ¡»ÊQØØ9ꢗ£öÕùrIïµ|™59jÇqÜ•»ÊQÓ£›ñµ×ö¨÷‡¦¨n·Õ©Žtš5Žã8îÈ‘£€„uÝïQ_.—Tjé¶œN;=jrÔ8Žã¸+'G óajÇqÜ¥“£€ù0£¾}õ¨¥¢>_öiÞ|µõ©=¯&Gã8Ž»srÔ0z9êÂÎQ¯¼¦¢þØgmQIE}:Ç óQã8Žã.}9j¹£8Ž}ÏS~>joÕày+¿9”˜C«ßWi”çMÓ´7—uY–íäÕÞz½Ö«ÉÀûy˜£–ŠúúݣΤ¢þ̲ýA*êúÞ£&Gã8Ž»rW9êéÈ®•Êóü6¶fbQQi·1{¤2×빘Czr½jyUUR´¿æQ`„ÁuÑÉQûºG½K›Š:Íónš5Žã8îÈ]å¨MÇx”Ýn›¦éà!»l6u²=~¯kݽ6Žã²dåq÷LÈQKE}Î?šŠ:ßçÙ¾éQÇä¨qÇq§î*Gý|ñ.ªYtø2x¨×£–÷©ÔÆÆïíé^+?bŸ¦ëõz³ÙPZ8d4G­{ÔÙ.Íí59jÇqÜ‘¿?GíµÑg“Ö;õ»ÛÎùž=ˆÆÊQo§ä¨Íµ]O’Dÿ*dŠjWLÌQçŸû¼éQò&G}‰ÉQã8ŽãNÝUŽú=ê‡ìŠúqZªqãRTA0ñÞàoé娋9êìcŸ§ySQç9jÇqܹÏ?G$I–eƒ‡ž”åæ“†ýÙÃG׆ahN–߆ùx#¼™‰óQçi–§MƒZ*ê¦G£ÆqÇ]º«õtäf”RzF»çs}t‘Ó¤N–kom9-®ß¶†îµr( ƒËå"ã||0W6€+~ä¨/ßK÷sÔ÷ÈGž—§ú';rÔ8Žã¸C_Ä|ÔUUEQ$5°Rýù¨Ÿ\%…q;ÅôJ¶v‘Ü»V*ðÍf³Vj¿ßÿé½À/è娧~ŽZ}õ¨›ÈÇ!/¤¢6=jrÔ8Žã¸+w•£¦ 6ýu'õqÓ9jÏ»^ÚŠº|ä‡ò»GMŽÇqwä½µéTϪG ÿ½õ±­¥Ýµï_.—ƒ”ÓYq8”RQ×ç‹TÔ59jÇqÜwsÔm“§Ðï#zÔð~†sÔu'GíIE}ͳã!oÊéüXšÔ9jÇqÜ¥÷rÔòíår©g–£€„~ŽºÛ£Ö9j_IE}8”‡Cu8žòã©>_uš5Žã8îÊû9j]WÓ£tsԵݣ^¯}_Ii}8V‡âÔ~Õ§ó…5Žã8îÖä¨Û6Õï#zÔð~~ä¨/÷õé;G­Ôúr½ËZjéCy>”u}¹’£ÆqÇÝzo>ê‹N}УØ9êc/G­Úuy>V—¶¢>7©í9jÇqÜ¡÷rÔfåÄYõ¨åŽâ8ö=Oùýù¨½Uƒç­üæPb ¬B~_¥Qž7MÓî|ÔÝqôöÕOƒ ÎGý#G½Þˆ4åtuÑÛúr‹·ä¨qÇq—ÞÍQ›¹>ÎsZ3Qng­Tžç·±5åæÍâöâ/fTæa>ZoQ1ãÀ›ÏQ«µˆ)§›ŠÚô¨ÉQã8Žã޼7µ¼ŠÚÔÇË{ÔÓûÀ»Ý6MÓÁCvÙlêd{ü^×úÑz‹AÈãO¼7ø[ú9ê“•£Ö=êÓUÝ{Ô59jÇqÜ÷rÔfÿ«{ÔÏ2Y޽>³¼Oã8~4~oÏ`š5€[º9êz8GÝö¨ïåô59jÇqÜ‘÷rÔfÿëzÔ_YåÕIJþ bwÛ98žqÈQo§ä¨Íµö€aÊïá·O…•£¾~÷¨MŽúÖì?üèQ“£ÆqÇ]z>êûþ%ö¨²+ê±uY–¦Å N°sÔf‘—9êÁ59jÇqÜ‘÷rÔfÿ|rÔI’dY6xèIY¾ÙlºÝfqÙóüZÔÎy£¾Z9ê9jÇq|>î*G=¹¥Ôáp¸ÍõÑEN“ Y®½µå´¸~Ûz×Ò ˜rÔÇŸ9ê#9jÇq|Nþþõ ªª(ФVª?õ“«Š¢‚ÀóV²µ”é]+ãËOùÓ»€_ó G}µsÔGrÔ8ŽãølÜUŽš5ÀæaŽú4œ£>žÈQã8ŽãîÝUŽÀÆÊQ_GsÔgrÔ8Žã¸kw•£¦G 69ê9jÇq|5ü#Ø9êãƒù¨ÉQã8ŽãóqrÔ0~;59jÇq|NŽæÃ£õ±Û£&Gã8ŽÏÌÉQÀ| Gã8Ž/Ñ‘£–;ŠãØ÷<å÷ç£öV ž·ò›C‰94° ù}•FyÞ4M»óQË/!‰cÏódùA=€Wó GÍ|Ô8Žãø¬ÝUŽz:r‡k¥ò<¿­™XEEÚíÅ_Ì©ÌÃ0ì^+W¥éçµEŠm3¼æ£ÆqÇ—è®rÔ¦c<Ên·•*wð]6›:Ù¿×µî^Û«ÌýÇ‹›ÀK!Gã8Ž/Ñ]娟/ ÞE)õ(†Ñ«„å}Çñ£ñ{{º×&I’eÙ­í~~&I<ñÞào!Gã8Ž/Ñߟ£öÚè³É?ëúƒŠÝmçü‡c+G½’£6×—«ÖëµG„5€+ÈQã8ŽãKtW9êWô¨²+ê§=êý~¯sÔY–™F7¼rÔ8Žãø}þ9jɰyR–o6y"ó­¸ìyt-9j€™@ŽÇq_¢»ÊQOGnF)u8ncs}t‘ÓÂ0”kom9-®ß¶†Þ\ŸŸŸ¦GEáKžÆ Gã8Ž/Ñ1uUURôJ ¬T>ê'WEç­dk/(ÓËQK•Î|ÔÎ!Gã8Ž/Ñ]å¨Y3lÈQã8ŽãKtW9jrÔ8ŽãøÝUŽš5Ø£ÆqÇ—è‹ÈQÀ?9jÇq|‰NŽæÃÄõW9ÝlÉQã8Žãî5̇©9êš5Žã8>#'G 󡛣>“£ÆqÇâ‹ÈQËÅqì{žòûóQ{«Ï[ùÍ¡ÄX…ü¾J£w•£žŽÜáZ©<Ïock&EE‘v{ñ³G*ó0 »×–e)…ºînAÐ[]ÞÆïsÔWrÔ8Žã¸sw•£6ãQv»mš¦ƒ‡ì²ÙÔÉöø½®u÷Z)°å— ]ªkV!p…•£¾‘£ÆqÇçï®rÔÏz´,x¯G-ïÓ8ŽßÛÓ½6SQ ¾ïO¼7ø[rÔõ³õ¹>ÈQã8ŽãŽüý9j¯>›ü³Þ©?¨ØÝvÎ÷ìA4VŽz;%Gm®5.?q·ÛÉïA.O’äÉO€—ò G}#Gã8ŽÏÙ]å¨_Ñ£þqÈ®¨÷¨omØ#6ò³²l/Û‰÷ËÃuMŽÇqŸ¯Ï?G$I–eƒ‡ž”å›ÍFžÈ|+.{¦\+¥µùä#¼™G9êâ«¢&Gã8ŽÏÑ]娧#7£”Ò“Ú=Ÿë£‹œ†¡\{kËiñÞ Ýk·Û­ÒúÌ ØTÕ\¦ ø× Gã8Ž/Ñ1µ”¸QI ¬T>ê'WEç­dk/(Ó½VÊo)¤eüöL¦Îp9jÇq|‰î*GÍš‰`CŽÇq_¢»ÊQØ£ÆqÇ—è®rÔô¨À†5Žã8¾D_DŽþÈQã8ŽãKtrÔ0ÈQã8ŽãKtrÔ0~䨯ä¨qÇñe89j˜¿ËQ7[rÔ8Žã¸{_DŽZî(Žcßó”ߟÚ[5xÞÊo%æÐÀ*äí*RÉGaè7xÝóó<_·ÿu’$æßxR–o6y"ó­¸ì1†¡ N aèfõ5€Sä¨osÔ'rÔ8Žã¸{w•£žŽÜŒRêp8ÜÆæú袧ïko÷Z¿meE‘~êÞÉÌõàœÿ’£¾£ÆqÇû"森ªJ ]©Ÿ•êÏGý䪢(‚ ð¼•lM6û+ÅÝÉrëýY–15€s~Ÿ£¾’£ÆqÇ»«5k&€ 9jÇq|‰î*G `CŽÇq_¢»ÊQÓ£rÔ8Žãø}9jøG Gã8Ž/ÑÉQÀ| Gã8Ž/ÑÉQÀ|˜”£¾‘£ÆqÇçåä¨`>£ÆqÇ—è‹ÈQËÅqì{žòûóQë9¥=oå7‡sh`òv•F©ä£0ô¼îùòKHÓtúJŽð ~•£.ÈQã8ŽãópW9êéÈ®•Òë>_3±©–£H»½ø‹Þ“Äñ±md‰Ë˜R¨›¡Â0|¾d ¼šßä¨oä¨qÇñ™¸«õônðn·MÓtð]›ÛßîZ߬š|ðx¿ËQ×7rÔ8ŽãøÜUŽzz7X)e²=zõ°¼OMÏùQº‹ü%" ‚ÿvWð ~‘£®ÉQã8Žãsñ÷稽6úlòÏz§þ bwÛ9ß³ÑX9êíhŽÚ O†aY–O΀7órÔgrÔ8Žã¸kw•£~EúÇ!»¢îì‘ã6PýŸï ^9jÇq|‰>ÿu’$Y– zRo6y"ó­¸ì1†aU <9j·ü6G}ïQ“£ÆqÇ]º«õtäf”R‡Ãá66×G9MÊf¹öv/¡õÛV¶Qé§¶¡G àrÔ8Žãø}óQWU%e°ÔÏJõç£~rUQAxÞJ¶&›ý•âîd¹»—£p 9jÇq|‰î*GÍš‰`CŽÇq_¢»ÊQØü&G}+ÈQã8ŽãópW9jzÔ`3=G]£ÆqÇgã‹ÈQÀ?9jÇq|‰NŽæÃ/rÔ59jÇq|.NŽæ9jÇq|‰NŽæ9jÇq|‰¾ˆµÜQǾç)¿?µžSÚóV~s(1‡V!oçš–J> C¿Á3ç7;Ûù®eo’$×ëåM?!Gã8Ž/Ñ]娧#w¸V*ÏóÛØš‰º0Ön/þ¢÷$q|lYâ2¦ê·v¡ófx3ÿ%G}!Gã8Ž;vW9êé«îvÛ4MÙe³©±íñí®õíÁ:æO7€—BŽÇq_¢»ÊQ?_@¼‹RÊd9zôJ_yŸêžóàøöùKDöÎÀÚ ï5Žã8¾DŽÚk£Ï&ÿ¬wê*v·óvŒ­õv4Gm§ð,ËÞiƒ;à=£ÆqÇ—è®rÔ¯èQÿ8dWÔ=2`ܪ»'HEQo'¼rÔ8Žãø}þ9ê$I²,<ô¤,ßl6òDæ[qÙc< êúñtòÌaôvÀ›!Gã8Ž/Ñ]娧#7£”êÎÅñh®.rš”Íríí^Bë·­l£(ÒOm(ŠBNxÔ €·AŽÇq_¢/b>ꪪô|ÑJõç£~r•ÔÉAxÞJ¶&›ý•âîd¹o?óØÚ_ü@0 9jÇq|‰î*GÍš‰`CŽÇq_¢»ÊQØ£ÆqÇ—è®rÔô¨À†5Žã8¾D_DŽþÈQã8ŽãKtrÔ0ÈQã8ŽãKtrÔ0ÈQã8ŽãKtrÔ0ÈQã8ŽãKôEä¨åŽâ8ö=Oùýù¨Í<Ò~s(1‡V!oWi”J> C¿Á3çÇ0 =Ï“½I’°Ô €+ÈQã8ŽãKtW9êéÈ®•Êóü6¶fbS-G‘v{ñ½'‰ãcÛÈ—1¥P¿µ ë/–Z:MS3¼rÔ8ŽãøÝUŽZwŒ§°Ûm¥Ê›ü³Þ©?¨ØÝvÎØ1¶rÔÛѵAž: ò,»?HxRÀÀ«™š£nki½%Gã8Ž;wW9êWô¨²+êÎ0nÕ½sdžeä¨\15G]]šµl«K}&Gã8Ž;öù稓$ɲlðГ²|³ÙÈ™oÅeñ0 «êáÓùä¨1ž£–Šúz+ÚZZ¾ÕÅô¨ÉQã r¯ý÷ÚvŽ)/Š";{ùÿxU•»Ýn½^Ëø²ýüø¸ÿ3ô»Ÿ±·_ï™ÃïÇÿÜ]娧#7£”:·±¹>ºÈiR6˵·{ ­ß¶²•?»ôSd@5‘ñÉQ8d¼Ÿý>joøÉ3Ö®µy­GƼÏÿð{Æñ·¹«5k&€ÍxŽºíQŽÕñx:§£Tßç‹TÔä¨ñey¯[[•¥´Ú½ÕJ líI’|||ˆÈVÜ\»Ýnå¢Nþc|©Š¥ N??M§Úœ#ƒHñ,ÿ½äMã:Ñû=oµk”CëµÒçËý‡æÌví†ç÷“67<òŒu§k½moCßjÓ?·Æ¼Ï™üo‡ãƒî*G `3ž£öÕår=«¦¨.š¢ú|ïQ“£ÆäýŒ±Ò÷Ž®–EQhW¾/…n{¼RÊ7×ê´l•RƒãËR©ÊÑ LÛ¹nS”úZyé7Ås‹üÄûµ'ãR“ìv•®Éïã?º}Ãö3ê¬xw[ß»è§ö6d óÝqïóÉïÇ»«5=j°™£öÛÏS”º¨–Šú»GMŽ_Ž÷2ÆUuïQ·c³_ŠLÝï:é’ÛºÖ³²Ê]—«ô§ŠšÞïý|“{4×¶rêyijòÍæpÈ'ÞÏ“g¬;]ë¯ûkþÛáç²ïs&ÿÛáø /"G ÿã9j_]/RQWmQ}:OÍ'ÉQãKs;G݆(:ã&zq2íhß7מÚì±éQ?ÿYò~÷}O»œ/ßöιº}×4™í{ùÑýLyƺә—kïÕÈðùƒ÷‰ãsvrÔ0&䨿zÔmQÝö¨krÔøòÜtkuzY ÈR~vŒ¥ÌÞív"²íæ–å[¹òó+ÌÜ»M²ý^ŠÞS›Ð–“£(Ôç4ùäÏÏÿµw®lªëPþ Gb‘•HìH$‹D"±#‘ÈZ$‰­¬¬¬E"+ç¬dµ!½A™=3Íìý~OOλ;½QÒduõkùçÓÉy°ëYñ’å¯ÇãQ³ÜQåñî;žÇŸÑÍ¿û¨7›w›6·ãw­ÚËwg ß w2>j„P8z꣞Zµñ{H8}ÎŽ^Ž5ü‹ø>õÄŽGíùý,qcl ·®ÌS;ÖGVÍô·/3w»ílé›·«Å¤¯—ÝÉNgQ$Ñ™~VܱüÕõ¡®ÇÓ÷ó]Z"}‰“uã‡ê0üå;sๅáQ5B( º8žÒãY§,ËñQÃÿ÷ù–a‘Ã÷Qßn·Ýn7Ÿ›;åù|¾ßï?±‘á?zî/©‰ŽÇã'vŠzUO}ÔÓ(ºID}NcT—9êåz“㣆ÿæ—a8@ËG=\‹Å›ûIËå"ö«yðëŠ}KJ½\.oVËÅB´!ô­z꣞LMD}8%QNi|N³ü&5>jøßa~y†ä±|Ô“ÉФqE.¢nÈĺú£K6ôuów»­þö“Ë-»Ì³|XYXç ¯ªÕõw«‘|L¹X,’$ѵŒsìíí!ô’j>ê[—Ú4QK8Ø2Í®·åÊä¨ñQÃ0 ÃcñX>êá6 é%çóyÇí¸z»Ý)wÛ­Î|ßï÷!ËͶš©™gû«¦oRºÕO'Zûyo—£–˜ÜíH¶æÿ!ôMò}Ô9êh6™NoE±?J8Ø2Mó«DÔø¨a†áùç}ÔnÌvÍëL}QÑ/}eöO£(Z,š†REÑTsËRÊ_uæl6kÇÞ²¯Óéô6ŸûrÙoY]ÖrKVÇ9iláÓ!4Pƒ|Ô·bJ5œÞŸ’,¿-Vø¨a†á1y,õçÔ$IV«UÇúO?èuÜé'‘¿n·ÛÕr™¦©?Ó_ä­Þ£–èýŒzI}ÔNïm8­9êÅ 5 Ã0<&‡ï£nÈ7`x9êÛÓõ‡õ]/ ÷WYEWo/ùaÞˆ¬û¨ø¨úv õQŸ%œNµÌ®eŽ5 Ã0<壮ív+¥†ÁRî÷ûÕjåþ¤ƒéI¹mù¨å£m6é2Ï/—KåÝv«¯(ÊL·äÛÛ›|ü;Ö‡†ßú2#c} ôz:u™£®Âi)S|Ô0 ÃðØþxÔrx$Û_núo6‹v»K,÷õ!ÑußX6TÖ×e;ëõzZþ T0˧ŸÏfK05B?®ãQK }°áô¡ÌQ›bÆG Ã0 Åcù¨ùÍD„P[C}ÔÆA]N©ÉQ¯ñQÃ0 Ã#òX>j„j«æ£.º|ÔSúr8^¤Ü/™y3Ñä¨ñQÃ0 ÃcñX>jrÔ¡¶ø¨§&¢>žeÚOR¦Y¾\­ðQÃ0 Ã#rø>j„п£ç>j“£¾$–޶ê†äh§Ó©²—£¾=ÍQXßõb±p•Utõö’ç „¾IO}Ôš£>ŸLvZËk•£ÆG Ã0 Åcù¨‡k»ÝJG©a°”ûý~µZ¹?é`zRn[>jùh›ÍFgº$ºîëC$3õ5FÙÎz½žN&²ñÓ餕O?ŸÍ‹…[ž5B?¦ç>êhZhŽútÐR¬Õ 5 Ã0<&‡?5BèßÑPuNŸMD]å¨ñQÃ0 Ã#ñX>j„jë¹ZsÔçØ„Ó¶¼^3‰¨s|Ô0 Ãðx¾!ôïh¨º §¥Ìë5 Ã0<&‡ï£Fý;ä£.lŽúŸíärÔø¨a†á±5B( ðQ›ˆZÃi[s—£ÆG Ã0 Äø¨Báhº¸/G‰¥mK†a—ñQ#„Âѵº>L8}²ežg«õ 5 Ã0<"ã£F…£¡>êËÉ$¨mérÔø¨a†á±5B( óQ'NŸLD}Ê]Ž5 Ã0<ã£F…£>êèfrÔgN›RBh‰¨s|Ô0 ÃðxŒ!ކù¨‹“‰¥ËIš­Õz†a‘ñQ#„ÂÑut“ˆ:¹œ.—S’H™›õ:ÇG Ã0 Çø¨Báh€:*ŠâxIÊ)I2Q㣆a†Gd|Ô¡pôÔG=µ9êc’/©–ùí¶\or|Ô0 ÃðxŒ!Žø¨%¢þ8^²c’Ƕ̮·Õzƒ†a‘ñQ#„ÂÑSõ4š™ˆ:¹º)¿Ëõ6ÇG Ã0 Çø¨Báè¹:Š$´ŽM,}Ó2»šˆ5 Ã0<"ã£F…£>j›£N ‰¥µ”À[sÔø¨a†á±5B(=õQO¦&¢Žm8­evýXnvø¨a†á5B( õQ›ìtaÊ´°9ê]ކañQ#„ÂѵŸ£.lŽºˆ5 Ã0<"ã£F…£á>j §¥t9j|Ô0 ÃðXŒ!ކû¨%–ÖÒø¨×ø¨a†á15B( ôQ»pÚDÔÕXø¨a†á±5B(=õQkŽúp1á´–)>j†axlÆG G}ÔNl™15 Ã0<6ã£F…£>êHsÔnºç¨ñQÃ0 Ã#1>j„P8è£>ø9êëÇ5 Ã0<*ã£F…£>ê½ÍNk™æ…DÔø¨a†á5B(Õ|Ô·^õáR¸ Úå¨ñQÃ0 Ãc1>j„P8è£váôÞú¨ø¨a†áQ5B(µ|ÔE·º´|eŽz…†a“ñQ#„Â>j†aø72>j„P8jú¨³uRÔrÔø¨a†áQ5B(½ê£>ࣆa†`|Ô¡pÔí£Î†ašñQ#„ÂÑë>êÂæ¨ñQÃ0 Ãc2>j„P8êñQmu;G†a‹ñQ#„ÂÑ@õ¡z-Q}ÔK|Ô0 Ãð¨Œ!ŽðQÃ0 ÿ‘ñQ#„ÂÑ@µÍQ—“ÍQ㣆a†Çd|Ô¡pô¢ú††añQ#„ÂÑ+>j?G†a“ñQ#„ÂÑ`õ­£ÆG Ã0 Åø¨Báh€ºž£NðQÃ0 Ãã3>j„P8裎­:Öõíc‰†a•ñQ#„ÂÑpµ†ÓRfø¨a†á±5B( ôQÇI!±ô15e^å¨ñQÃ0 Ãc1>j„P8z꣮"ê› ªï9j|Ô0 ÃðHŒ!Žú¨%.§¤°9ê]ކañQ#„‘ï£îÈQGåo&J ­–›£þXnðQÃ0 Ãc2>j„P8裮rÔ\Ž5 Ã0<ã£F…£!>j8)-ø¨a†á5B(µ|ÔÅcuŒ†a€ñQ#„ÂQ‡:ëðQÇ•ZÀø¨†a•ñQ#„ÂQÛG}îÚf§µÌ†a›ñQ#„ÂQ¯:+}Ôš£>Øìô¡ÌQ㣆a†Gf|Ô¡pÔç£>·~3ñ`~áÅÕ.G†a‹ñQ#„ÂÑudrÔ—›DÔZ¦.G†a‰ñQ#„ÂQºhø¨]8-evýXࣆa†Ge|Ô¡p4ÐG½—púr³¥ÉQKD†a‘ñQ#„ÂÑ@u™£¾{/G†a‹ñQ#„ÂÑ@µ—£¾Ù5>j†axLÆG GC}Ô6;}Hªõ 5 Ã0<&ã£F…#|Ô0 Ãðod|Ô¡pôš:ÁG Ã0 Áø¨Báèu†a„ñQ#„ÂãQÃ0 ÿ‘ñQ#„ÂÑ+>êûo&⣆a†Çe|Ô¡pôªÚä¨oø¨a†á‘5B( ôQ»pZÇúX⣆a†Ge|Ô¡pô’:ÆG Ã0 ‡Áø¨Báh Úf§oÎG½ÄG Ã0 Êø¨Báh :îë5 Ã0<ã£F…£W|Ôeš5 Ã0<:ã£F…£Á>ê>j†a8ÆG G|ÔõurÃG Ã0 Îø¨BáèuRå¨oK|Ô0 Ãð¨Œ!Žú¨cN«ñã–ᣆa†Çf|Ô¡p4ÐG'…ÄÒÇÔ”y•£ÆG Ã0 Åø¨Báè©ºŠ¨o.¨¾ç¨ñQÃ0 Ã#1>j„P8裖@ºœ’Âæ¨w²Š>k+Š›mÄBÉZÀ0 Ãÿßn7›Û¹4æ“£Fý¼ø¨mDÖòQØõÇrcrÔI’Çëõ*ÍÚÕš?ÔÃ0 ÃßÊÚïH$=QÃG½Ú§Ë=9j„ÐJZžÕ>ɽu§ºÊQÐõÕ´[éå|Žã8MS×¾QRRRRR~w)ýŽô>Æò!P=ƒ}IL¦Hz·ý){?e””””ß]®ÆòqI³ü¡º°o&ÖrÔÆõaÖ’6ílƒêøp Ã0 +k8-}Ÿv|IÒõ!íéãf[[Â0 ¯ö‰ §³|€ÚÕÎG­Ê²LÚ´Ëå’ˆl Ã0 ÃßÊÒïHç“׳Ó0 ã³ç£.Ú>j3Ї:O ÌQý–7 Ã0 Ã0 Å}Ôvè< ª ýÍÄî`†a†a8nŽõQù¨5G]þy™£.œ:œ;†a†a—9ês+Gí‚ê¬úÍÄ|컆a†a‡›>êÌù¨#“£¾Ü$–Ö2Õuw0 Ã0 Ã0·sÔ7/G}H N›2»~,ðQÃ0 Ã0 Ãp[>êÂ÷Qï/&A­ez-Æõ1þ] Ã0 Ã0 ‡Ã}Ô‡Ká‚jÍQÛk‘ùñx|{{›L&ÓétµZ¥I"3“$Y.—:S@Gã×%§VfÉ4 çΆ_.­½QYQSÿßÿÉL-]m×ueNc;:çÁ¾öïï3£h¿ß»ùrÈ–£(ÐÅN§Ób±ø±Ïÿ5,5PjQcþûû{»®ŽÈ¦S˜ÏåäJ8Ì…à_eZêÂrär]Ìg³8>tn3˲Ývûຓ­ÉÕÔ˜/›•ùʋśBûJOl¿ÖÞ¦ìtE©÷ëÇ8– ¶Zq™ù}_«Ýð·y:çóù´<=Ÿ1M7›, gl»ÙT¿ÃžËöÝ÷쫃ïÜæ¯`9ÃÛíVZK=?»Ý.ËÒïÞoglÓùýúë6j¯”Ÿ¸6e¿ykk‘ý6µ*vs&ˆ<²‹…‰¸äP'®#k¬+½œU9¥Òõ9Õ¶ÖEe­Û–µî³Çù7ócµ §÷eŽºÃG-§.Žcós/Rç7Óûg™œg©ÿåÌíVOûr¹†ÂŽÏŸÉ÷hã„Pî,`X9¹\¤]’æEÃHj¬Ôp]FZ ·¼HªýÂTl3§ê”ïÛ)çôìKê¿\ÒDË.&¨6ó¥w>K¯:JÃ¥ËK'+~8çþ-,5PºQ¾TZ©Níº:KE—zn¢Ü,“:oŽ6Ë&æðšËK³^¯åb‘nzf‚긽Œôær)=¸îäúu×r5ßœÿºÖùþ1Ü»°®mʑؓ|Ÿ/»F#³ÍGÕ͵{É·ÆvÎöTœÏ§ÌœŠSc›yu(LÚX%“–Jb°rÉú÷îXÎ×n·•{–Ñ¿ë?d9ÿÕMM¦?C/1Þwï·3¶éü~û¶cª‡©rŸ¿6ýú,Õ_¾ý¹~›­mÊQ5¶)W܈¸êÇyÐnÈ^VÒ—in§±Lê׺¬ªuÍëè…ãü»ù‰º´|eŽzõÌG-mÑdr-tY^ÝÅO[Y‘ÜKâ]ë3¥ÊÝŸ~Ñ:S¾¬ØÞæö^Uï¥ô2Ùf³¶É½©ËøÉ_5Ó¸\,ÜÛéxt7øšëÈí­œmW'ó¹I¸Œ‡ìbjwäîÈì’3»ä\ÓÝçþµ,·´Ë4² ¹W‡Û™±v¶Á_WfjºL$éÛÛÜÛNfÁìKZ<9ž/ÿŒð¿ÀR—$ê¸\În¾´¢¸ºê·®-•j68šN¥¹S–¦Õ±e»ª-°®åö%}·TcY¸êe¬2›7ËR—AõSöÞÎÔ¹Œ±?_BP·¯“¹^:®©Æu×>'²åívs>ŸÝ|sB¶[w]»u›ÇPõkímÊ©0¸=¿d»bc~žgí¾ONÅ©z,ÕwüÒÇÉ×Ñ·Œ;æF[ÔÙÿþ:–*˜¶²Á2ÇÔ·Šå>È-ÙòýJÔîÞ=õëŒ1:ëg;ë@×÷ÛhçýúæôôÚl×Éê(²é´û)ŒÄÏ®Oé<žöñËÉqOmÜeÕXFŽóý}×·ÍOçßÍ/ø¨ó'>jóÐm·³Is÷^eù¤Éjer©f榵ù‚ä”Õü»o¹RÊ–$Ë4àÉí¾l°aÖ’ª({Ñ[­uuw¿Y¯÷û÷ÜæÍý¬Ý¾\GRsäÀäúrÛŸÚ¾#¿Ï4K®Íê{³¤Y}­Û”¾Å-¹¶•öy€5KeÐP{?s¥9jS±[wèy+SÔÞ—´?æ6­¼£Oe§Ê~ŽÚfò]Ç6Ÿ,É=õëŒ1ƒÛøóÛ߯Ïò=J”۹ͧ×f»Næö‰ÃýÛ¬oSŸÝ<ø,Õ¡ÖæOï÷)æ3Vñ|m™{­{Ø'<οžÿÜGíîPä”Ϊ“¯O ÔW£63ùL/„VvB·£Ùwû©wßæÁ·eiy¤EµB˜êºþ.ü»ZMMg¶-Òùæù…ÄÞu_™{¨áOTmÓ6å°@õЭû<À¿M«R^ Y ß]©¡;ïÐóg>jÝ‹—=(ïô6y¢>ê•ï¬áüÀ¿…µZC£i<¥KÕøÁÕUi?ˇw¶…̽µì¼Ì] ^o«-°)Môtêöu¹œ¯ö‰~å:_:q‰\k©JÔ'ב\M.‹h—‘ á"¯Ü]æöÙâz½2)š$‘Oá®—ÎmöíômmðËù,'çÚ•Ýmø`¥ÙïlóåòtIãöñd^¼ç¶/›Ó¥±¼=Æ­Ù»ýÔíeüí7|æ~¶Ð?~Û‘Õú»_ÊRµŒ›7Šä¬º'&rþ%x–³'çMk¸Ìi/ïü´cŒûmÄ6yÿ÷ÛX÷ím~9ŸÛóóצ_'›ßf—?y·ÛîíGîü,.âjŸ“ûÒÞ5^[fâ-S¯uŸ8οžú¨ý>jÇ©½és^#}w@gúwjúа²L4·ãßétf Êä^½ÁŸtyçª/÷¿ª˜ù‰½*åë~›ÏµÕ²Mw²Ù¬%®žÏçjêÓm–ëÚI·i—ÜÜ— æÎþ*.oÛf×[—…x°/SM0S^&çV_FïIõ½'“µ¶n¥Îü[Xk`4K,]¶öþ^»:Ñö­l![õ¶“[av³þ/ošeµ9ºGÙÑÆ“šöSËr¦å£½¤éÖ´vß6û¨s›íT'•œ³mÃÛÙ݆ZöØö æ6“õ´¦›+?o¾ßKÖ—ŸV™"]¨Ì7Ö—éÌ>ÉjÚƒ/M2ÔÉ/`ë3—hH››5Qq¦¶Ûæeô›eú޹ú<;ÏÏÀw üØFçw¿—•ÿó×fç‡ôÝ}›õs"ŸÑe›û’û)9òX#®úñÜsÔÖ^ú4êËt>ùäqþ<ÈGýÂxÔO|Ôr˦·u2 š61E•6ñý«Ó*óœz’ÆÛÖ¹ËÀdYÏqÛ’K˸ùvf¹MkÿËúŽóè­ÂüU,Û{Ï[Ïom«¤AÓÇ(yUϵOì[ÞϜϾPéôõ®ó~ÛØõÞ ?hK$•Êé¿•¯ݽ÷uý§-|ö—ÞŸúÙ-3€4caL¤ýãêmeM£5–‘` íí”Uª@½c›}Ô×¼ ?ζ'ªÍï÷Qg]ZcüëyÇÁží¹3Ð6–É»®eßp«I§öº>êÎl¡¿Ìßá£n°FƒŠoó¹{CÐDÚû½¾‰yµÍ²œ1uñùç§c Øï½<þ~]Ý>?Œsú®Í—üÉ'ëVíÜ—F\çµË±_Ÿø¨›Ùï?ñ{ÿÝüçãQÛ·Kb}õÕy¤bËa^2µ7wúMéØ}¡ÖÅ·•mU6鼑1™æ)O–Iy÷Qo+µÚäì¢ZôÕéuå÷“¿JK¨3#½$í¢DÈš±q²YÒíêgïÍëæ’ÁÜÁ_ÅÒNJ[­osK…:ì÷«ÊÖ9 òÁ¼–¥}¢1×½Ý_\í^^ÇúHÜX•_ÔƒË×E¥³úä|€áœ+8dvY#“ÔN㪎Ý}ÔÚW–-dùþH_Ïq³ne¨4dÕþø8õªqVÍl¯t€û@м¯gŸ$®,˜ÖØÞ«ž«'‰ím>öQçe±Ô¤]ŸßñTÔvleŽº¾ÍÕjY=ñ¯Í?š·áÞïbT½dzï%ëÇfNÅ|® ÂÆ`êËÜG]07ïåHDϳ…ïï‹ßŸ-”s↭HJ›n™1–¢†üç6ó/!¥:áýóÓŽ1:÷ÛÛt~¿ ö†<ýäµÙ÷Äáý}·h=q;w=1ï‡Ú}œ‡ƒ¹û0CN%‰ÞŒ´—Éjµ.¯Õºó_à'>êÖo&Úk‘¹T97Ò¦:KskM·n·ÚxÔ §Mß8R»ô=ÜÌØ-Óëí.»Ö±>e­0-Žƒï©Ûí :ÓyÕä¯vÿ¼dLS*G>µ>ð¸ÖÃ.iG±ï„sg!ŸÏ'­c‘©Òµñ¨¬kš/[7¤ôëFß[äïvN·©a˜¸öþ…üúöœØ#o¶7µõwqÞ¡ÓGmv·ZµÇLþulbZ{¤]}µP—¹\.îM+å¤z\(Á€6ž::‡CgŒÑY?;c›Îï7¯·óoÏžÎ<½6;ýÉÚ¹cVٜ̼s_íˆËž[Fz}öÑxÔ©ú?¹T—ÕÛ /ç?Â/ù¨—}Ô_Â}M0 Ãp˜,Mù<=ŒãaÇÄð÷ñ=Guù¨[9êïŽðÃ0 ‡Ìjdò3Éá cÀßÇ­u‡úpÏQ?úKøñ 0 Ãp°<ŸÏË•Ã8n01ü}üÌG}¨G Ã0 Ã0 Ãÿ&wç¨+õáÇ}Ô0 Ã0 Ã0ü»ø‰ºæúøx?œ$¨^¬·Ëõv±ÚŽÉ«íÂðFàmµ‘i¾\¿-72ÉL[ê’f’Ï(ÓZËm«¼ÿÕ,i—·k™u7«µãú_KvåN÷Rß×»_®Gb¯¬Ž°úËòÊ´6år}çÕZ>þrµÑRÏÆò~N()))G+—5.[*i¸–eƒvŸ´•óú‚ªG£}†aø¯áÃñÜã£Ö±>®6œ6å)½¥y‘^‹ìú!]+¾6ø«Ê®íß÷[˜ƒ1“Õ-Étº&–uff“O'ÓUËÂ+oM6“YÞ®eÖÕ-Üò{YþµÜòÕãÛGµ¯®=~-»£}ÂE~Ÿs?6ÿÓé‰J³kš_åì $–uŽL™YÀ”fºÖøI©g,÷Ïå½Ìã_RÞëUUÇnO8¯ñ¿[ŽÿÝ}ÙuÑQzŸ4µ ”+½¾@—/[ÂŽá«Ëë7ókå€>¥¯|Ü¿ôô5CøËë‹ÜQÖ?oÿ9/Ëb$¾ì®oÍòsuàÕz5ˆûêðàøªÆ®Ìn²z>j=o6—ˆúO©ŽõarÔqoYÔ8qsþŒ»·«ŽÇùûóU¦÷S&Ó6NwG3é?÷ç\–9¦r#P¦ßå~áñd?~q2«ÜŽÉÕN¹”ñ%/ÿ©óË¿ÊÁ(ߎi!¥ìÈL™™Î÷_Ìñ¶Ÿ7¹£Ôûš!\}e}ÜQÖ?],§èœíO©LïÇD¦]œ(ìOf:Ø?ÎfŠÏ™)/™@9]îåñ’×ÊKUÊ 4g¯*«édÏm°åéø'÷õüm¥©'W†qgé×+å{}ûñ2~ÀçrΣeîœyËgî:ò¯/ÿŠë^æáÑë×ã©Æ]GCÊھʚ`¦ƒ=‡ÓXI enJ™SµáÚšÁµÆ×ï-“oæ'e£ÇÉú¸ðå­]~¢¯iòWL§Ÿª~í9÷oÖ}Òûäç{y½—ÉÏpþ§|ùn®—ÍZ×Y—¾vê«oùU©óÙ•Iþq<š_g[­ÖQ4“;úþ@úI©SøÑö5œ–˜ùý”Ë´;f2Ù Ú€‰¨OÒ^eá2ÄÍjÁí‹át+k êÂi{e™Þ4z×K¸~z5Y¯~qƒ*L£9*?ÝM?—Lípz_AµL±TW¥NwnuôCëÓ`>}1WÞ÷ñåŸã® ­7$~µ¬Âü3Aï¹Æ]j››Ëw-Ó¬ó?Pܺîî¥ü ºZ?úî>s=~îÚ¹˜ßUVátùym,mµ Z›;mú4ïá‚Éæt•_-o³•®Ö§´8óû”>~úvð0¸Í_T6¦{/œw„ÖÝ6¥ ¤;ë^£võÄ$¿÷êÕ‹Ü3 ©ómn^;×¼–h~C|»›F³ø(͕Յ†»ÊcúœãÖüã‹§·££d›W&ý úN¿›æÔæ¨mêØeŒŸ4>­ Z³ÓN7³ÓÉí>¥… §½ÞË*·š¸Žæ®Þô=âOd²îO÷(G},Ãi—£îÌN×éZ÷ÝN¦Ó ¬üŽŒî_nKŸÌNw—~ .SÕæŸ{–¯g§û2ÕÊK3xîÊQëë¹s¦úèžS ÏQ7,Ÿ h—aeªk}M«ßy%Sý¤|%á3|ú\¦úA(å>csê (;ý¥YëŸÎT×§ž˜äK§`2Õ~™Ý>$„ŽfóÕjµ{߯כÿ&™“]Í~ŸÕµû!»ƒêÛã@ºT«ñãjuª®XsÔ©;iߵ߀ø§×Pç^h}íÌT׃êê²mžÞAMÖ+Aòà ã5,~8=fÐJ7]oZþLY¦Ð²>}´æÜ§?8ž¢¡Lîhïg©pçÍ/o噬•ºVñˆûˇZðów•}uésüj½ ·üì5øS׿OÔ1ï“¶§Æ:§¿”ÃšŠ€¹>=>·¿±ò¹¾–Ç©cOÇãIÂæh6—pz»Ý¾¿¿ïã“ü'Aõj½Y,WWÁ4íá?˜&Óh¢¥7ÉÆMé-iiGì-ïK꺶œzùÛŒÚuìkÀ~²l|FóéôŒµÊ¨,Ë3@é—íºÃÃùÕzE9 ô[-¯«úÿ¬Þ[f-ç””””_X. §M‚z¿ÿ)ͼ£ endstream endobj 25 0 obj <> stream xœì-˜£¼×ÆW®ü˱#GVŽYYY[YYY‹D"±H$‹D"±H$²o¾sNNB§ó±Ï[®\÷žMC …2?©ëº(Šëõúú¶{Û½¿ }ÆÏø?ã_ï˜òòúÆô…ëÛ‹­ÊâÖ‹u_^u'ºfCü •öˆ¾¾ÙWu1.»ñgÖ}Æßgù ï÷?ã-eóõù[âR8O×ë%/²ªÊêšátUUyžË?^eU/7¾<õGtIŽÝ²Dã»tþtü”îÛÊgõŒÎúÙ~­.\EY¸ÎL—ÉêmÂ5²èz¤²LóíŽ8Ô'Òų?v?åþ#ÅåöŸŽ¿ZÿÅ}þ]þ_Æ¿]o8åïÚ-!Žþ41[N§ÓíÑKQí°¼œÛág–ãþƒiQV°ßŽçË9˯e)qúåõ•Eì£g^¦‹ˆÿŒÎ¨fõƒ©¡1Ñ!§·ßV&÷Óí×´›x`”—ѯ­Ñ‘é"µutðk³M£²ÀøîR÷³P'è0Ó®ÌÞ:}Júó ÆÓ—ÅdO~•:Å{D#ˆ¼vÌU7•íÈJÑ EÓgu—×ByÜçÍñÿöת˪Ž+Y «òfd¥h§¼™ŠvÎÛÊ×âë²Ndo²[¹]^Ú‘•‚«øoÓ‹½bûÀö¤ÍÄ.ñ½eõ¢A!Ûw|•²› öÖú¹ìf®ì Ô Å xuqZú4Ô>=~¨_÷ü¿8žK7NQºn`»\o•‰G§©m?€z—Np…øº¡xoqbi0H7>„^=vñ6«Ç½êÆŠ5ÃéÑi^kLÕP-qš—’½$Ѻg%¯G Õ“†êé.œž²zTP]K€·P]¨68Í÷¤aåRÔŒ«9Z Æ– ÌZªKÕS¡ºwp:ÏHËÇ£õ§±ü«qZÅ3€êûp}†Võ¶l  ZÆÕ ІÕÆ.B«‚¶EâÄ-Þ(fÇt âZÇñ:úÆQÙÓçãډǻâq1œ ãÅ[`{M'¦°mcqíÕx ãvô@µ[ˆûí§y±@;Ο‰ ÀY^¢VeJˆ}…ÿæ¸,œ¨‡1íX×F§ºZs®+® ŒE¼Dâ55½éþ+¸Ý¹ïœ¨ÛqfŒ4ÌKYU/o»ãù–··{ãÐé}¸S=Ý¥¡uíõ[€Ð{\« {Š;Õ¸>äKG@ÚÆAži¬±ÓEëfpã„°ÙçpjNñ ”Iš–ŽzK3(mT»í¯jï¯øF tC´õÝ8¬|¼˜þ½Çqõ&(Ô2´.;14NOŽGsZjÀ£ÎuÑBÓr’äÚ£[  œVÀÏÝ鲕8-‹ kíW ¨.ØÝA;hœž$NWž±.6¦¥·qjó“åöu±ë¯êVDwuÝ‘® !œÆZqýˆîô3Þå6';½÷­[“~>Óç£Þ˦q§ÝXkC∶÷ëÒj³ÆÔ£žG½Õ;Õœ¨‹bѾ÷çœjíQŸŽÜ£®+Æ´LEOZg©†5W‹ˆ+5§yçªg³-¹õ©ï‚¨»‰ÿiî„Sý¶Û½ï»ÝŽÑõèsn7¹Ó~W”x–mÒÛÇ]nwW½h®"zÀ£ö:Õ~çy ÄGZ«fã<[$ž« “D ™0vÔÀ°ÄÝ;ÝEØg¨8­ÙΛY‰2Zme _U±Ò~2ÈÝ [u·ñÇá’¥w€ko[zxìàq¡78öH!%€Ý8šðkEãÛJ訑ۜ‰ÞøTÿ(»-;».ÆiÇ£îGœ®B8=ùp:C§z8ÝP=HÂÏ*ÓyuÎJ¦¬\Zg<¤oAB5ð¨{íQëbcìT{0;”(B;%.7¶Oêç JIâÄäóùlp×#{² Õa­¢ñW¨ÝºÆ{ˆß8Õ!Mr’?¾¾þ+·ÿhü ׯw”f‹Z¨Ž:Õ\ë`¼@ZÐÑòeœvÍsÐçl^Í‹òx<3†çz8Ñ8/*Ø^ÆŒºQ3”ª’•” ŽÁLEYj¡UY\¯¦ÿârå5—«Ù®¬™A™´š7Ƕ>uÝáýÝxÔLûi)ÊŠA5+2߃A ÂÅN5cOâVeZt5˜a½§D×õ 5Áiñålûuöö/?Ãeª‰âã®Îs³Sã›—ø- Ïøï”8]6ƒÈúèµG-ÝiëQçªk™ìÑkÜeΊÂã²3:-±V¶­GÙ¡JŸ®ä NÔø¹ÎM颿8•ªÏŒ«…Y- š;ÕG|ÅJ§|X<¦e°uM´"qEêSt­ý-1®h½•áöð¸¢õI.÷¶‚>‡‘¼/­5Ôä²óBIñ䇇٫~gÛ–)ÖWéº6ž<}zúÿ2 mwµ4“j';ÕÔµNv°©SíæTËÍVÌv Qšé|»ÉÑ?XŸ=FÁ îtJ•uïƒj‰m›eŠ´_|=»j·kZœö™;)³‡ ÝçHqÝ)ü“œnŽöZ…·ŒŠÁc[½µ´$ÐÊÃd¸5ièx¬ày¬%Òð2eõR‹+‹Á¤Þq CJÚ{œFŠRQW{Ñe«rh+UDG¥ºeiÚÛ÷«ßÈ↠Üx"âAM¯›Ð&ØOƒ°ÜDÈWÑ­ÆÚ‰(Dqô^|¸>¹¨蟠²<µì§m…U~ŒªVßþ `ž,r«[$Òƒ¨áðÙ@œîôɪ%NwªyÂs¡=ê­8]º8 ?4N×½´Í:gÌ\6Üšf8}-ÎY!T 5‡êZØÔXQ%~ ¨69Õ¤A5jÀƼ¾Ä„êïîç3Ûõ—{]÷àv½8MÑ:S,ߤqgÛ¿–ÑqÎ6ÊâÖ½a3^ËÛÃà)-w;áë®õè‰ÝÏ„Æsªa¼âTóx±µ'jÐÝzéQ3îÆ¹—eb:õæ¿ îTìªSÏPêšqº#mkDÍ—ö9Ns~Ø]×yŸgƒÖ!ÏF­cžML ®“Ö¹ÈóóÅAhØç k3åv-Õèy[d:¶í~÷=j¦‹ j“Díà4ƒÃ¢jвÎËZh…⊢TeÝH“ß]âNÏ@½€Mµ÷:Õ³³]û´©Jw7?LFM½ÕHÓqÞOq¨Zkœæ<ÜæN9̢Иê Ú– $°YhÓkâ2 ÝIÔa4Ò«¢ðf0¨)!ÇÁ¶ Bô;LS,<ÐeÛ@|ÒªpKõŒëàO*—RÂR Ø…mÕ‚Ó Q;®>÷¾°íKð¶Áÿä¸ý ~~òƇ7ê¢5=RàX¨žÉ¶àû¥@nºöMŠÐâÓ¦ÇÅ*¼å H<Êú˜¸OÓªsø Ÿô¨ fkœ.N—Ru©ºEh,6Å@µp¡…Ý «€ëž°ÿ~†›Õ?ôo3€£ŒVõ]Þ"yßǕҜŠ'ÆSÛ+‡Ù-˜!6«¸“m°Y›~ o:ã?>úòSÕGG$6‹sL(ŒmM¯9šÁäHÛã[÷ò\…ÇZ'{t&ÚxŹRÕ ¨–¹Ê§5TÇ:Õ¼>÷zÔ¥0¨mRJÕæœ¨«K^jœÎea5—¢ÊDî‡\u)‹A -ÖÓYÖæU Õ³]×ÚÕÁBô­%Žú[Áþ®xåc«’rój3ú4§%œ‹?x c¯_MGüXmo‡$¥Rùä^—›€´iß+”QoÞ…E'ô_;­ã¸;½îT?ȵ¾¹~¯«u§}®µÇöÄ•5#±Œ]Ÿt²Èxœüõј1ÒåšM¶Ï±Ó/§E, he½ÔP½TZŒ¨Ç¶í³k—]{PQF©9&­ÐüÌ¡}Ñ ‹YyÕ¬5j5}xsl댨÷oÄ£^€G=¹5#jÖât:À²ËÇÇÇûû;S†Ó]×Ýø0×µ'º+þüùó÷ïß?Žì|›Ì°ù®S½Em<íN¨ž-Z·9Ûî¾/=}½gû“užgµÖÖp?S—±ýϺnô)pzÌü¡A5/Ò‚îùY§Y¸ÊRk¯vÊm–ð ãªSi§mR‡¤ÁÏ?Zþ'^ Õ¥ø»oPÂ'…Lº}O½GØü¬k\GŒ¾µÁ'ÐáqgÚ@-taëš}–7::´’rĶ_e{ˆÖŽ“oœ[tõ«àÑO˜â+jãÞ¼€ŸÁ´ÇÛuúô¢»{ `p@µ=—Ìg¢Îs>X… Û‰¬3gŠZ‡ ÚÞœŽ¨IùÈõXö¨N7#gévBÀÌþ¾ …1UÕž£ø„GóhÊg`àQ3¢ÎŠúÊø™Aµ°©O—\Ò5#êk¡?„ɯ=jÕúF`,»ÉIüð^j‘°!Ç‹E¸(šö$†ëÖ´>ºîÖmÅ×]qÔ@N?“Um”; Qg[ b6Ša²A0i¤ (`€Áƒ‹ÓAuÕ`Ø×éR¿(–;8íäSÀ®~T)6'"4uª=½æTÃØ¯£u¤£®µox=GÍÀ¨lõ¯Ø,Pe8_2ÇIv”5P›¾R=ôm?ž.×aœLÖŽç-‹…­­ï vÃx}ýßPWõþ½Þ0möÍþ½Ú>:®ïLûÃGxÃ^¨Žóãñ 7!·Âjú½iù.ãž÷ð.zûhYÿRÅV½Ý¾È?^þçxÔ·hu^VãùÂXZªÕ¬Û1¦Œ¨]œ†¯Y§ã¬·`ìzÔ‹…ä.ÿó'oZ“¼kŒÐº PÇyl³?|ÉêQÕ°—ÚrÏë®­pª'ëW‡:Û]uø³gHbjH³@€´ø‘b?U˜,DÞ¯´¾4Dh[ÚÁheTðÌjÁ(h°ÇúÒ2¥“ÿ}gªÆpjäQ ¤Þ5¸ÝèÝOؽõ N2ÆrôKê9½ŽëKn^HºÁl¼oAïš$o¤÷¹ÐpÿÍQnŒ/-ŽcÙŠÒhm¤ «kÐ ý#ºíSSz¬µ;Æ£5ö®ñ˜ÏAGºWZ™Ø¼Šq¤—ôj¼âQ‹OIœÕŒ¨‹J'~0œæ5ϦÎË«xDQZî2ñÃøÒ§Ížc¨®û:ÕªôËzüˆÒ„á%Ð{¸­ðvýë>öý6*êGk ²è¸4p³‡u§ªIPqº„£”8®5oëNϤ™ÊRJUßë3ƒDƒÓ¹B³öv]ÐC©ýjPcQ„µá1®«¨;ŠkoéΣÖ2H–Dm@š:ÕÅûÞxÔ¨p6.Z =SÏYzÑЖD­¦ë¥íÕ³Õ­5Q3|Öõc{ÕUì(Ç@zòÇ‚¨ëÁ¶€jÐý0b¨Ö©øü' ÕÊ6.4ÂiÒi‰sL›¾4±VTëW]œ®ªSíIç°‰5D&µ–‹¬µû£‘J-³©-T3œÁ&~È?ä§Aâ‡çáD‰Ö›¿Ò©nq¤Dócñfwz{~uä=FÔb¶,¼}h½„ÐÚÒCÌ‹öçT› Q´ð™ç ÛÞÖ—³…ÊyEEÑ`¬nB±S]œ†¾´YWŒ9ÉèZ¶ú%Èô ±<‚ÐÕ].tJ\?Òµ^Gî”NÒÀÛuªa}¢S xÔ§53•,:ÏsH‡u¶fÏFA%®gÊÚÑÅzÔ`-†è××—¾ªêýGÅÈö 0"ñC«LÕè6ñC*Üâáx†k§´ïE(åCôßè-2í ãQÏf46•G½{gDMÇ“µ„çëuŸeEñ~:dÈõúq¹ð¬¶?‚¨+::Ç¼Ì iÿþýËÈYÔS}øsà¨,@úÐ csø{d§%ñ¨yû–‡ƒè‡£5[qšƒ mFל~y†6‡áæÊé|€f5'jö‡®ÞsØf5c)À¸–-Ç¡<üeh=à]_%0³öÝ•× ¢%F™4r`úGhOœt¤‡›W[° fGjþ#:â‡Õ0wž´¼µ*€Ù õ\ᇫlpzT¥Ec¹«³¦—ÛdR 5iz-TwªAúDèÚ:Æ¡ÑK¾ÑµÓ]k÷vÞ Ô´~³k Se¨Sí×$ú¤‰G6¶œàÆ®êzÖÛñ|eŒ¤ëIKÙ:î½±QîQ¿½2¢nNÇút`ÚœíYi{:tçcw:ôçc> JE¹lˆû³ê¡½u¢çVk£¶{ì«ê]µÆiG½{gšÌ(=ê÷÷éÜN»,{;ŸYÙϯœ¥ùÔí‹É£F™Ï7;­sªç©å>oßrËYþÁ^åãØE´”i!=åƒ{ÔîióÑ<„ =é g֫ά¯æâU…èÇc>^G#rMë]‹LéAüwP^·ÍCÇ£ö¨¹•δïu–µè](êL¥”ðaôĨÔÎX­Fë Ž7é1¥MBÈäq­UlGœ8ä3øhÎY¥‰îøÏÎOðÚÓ&y׃‚¢ØXÄ=×ÈC‹ÄñöŽòAQ¹Gûƒß»Ü· ôG04Ž=UƒŽï¡'Çñ¹Áà6å9£+ºuo^´ã<“q¿ããQWø ñŽ­8öÚÈ$àv <àéùýj€~%1㓘i_ìÕè¡ÅCµ-§Õƒbf˜J!†ÂÀ;i šº»Íñ‰æ Ê T{ö?2n¶ï=ÂOžYr¬íQ³ ' ²“{ÐT%x\K‹Ó—¢¥ÆPÍ耎t¡ò¢Mv4|€Ñ¨|/½~ê¼OÖ¬de óÃ…-†Ñ3ck·ƒV¹çjp¿Ì¢u+ vßÜŽ.Z+oÐzM«È«ÀINÀoºCº‡jœ˜(í'ÁCµ\Q½]wÈçPûs§‘kÝx]ëÑu­Û@N5t§Ím¸ ª]Ðz@î´Æi‰yÉxÂÒ\äB[¿ZÈ¡dPwº3þÅ‘ÃE:N5˜»ÓÁiýýÒk r"$=ÑÒˆž ã~XÚ3ÅŒz˜Œ^æs¤nöCµ^S½1®a¿¥Ò±½©7Þ8æQó2.7þu^0âÆ­hãщ1¢fà$êÁ°f)±¯0nËÞ߆¶m¯VºL)+½VV†ü2d—Q+ãd¢¡ú³\Kö0ÈÞt·ÙVžñ\î²ÌŽGG=»yÔ‹r§mµú|>ûÓép:)=Ÿçóþr9\¯GÖàr¹@:Õ]þWGÍn‘…_}»Í=ežYÍbžG-Ûü‘¹Ö“ȵO"Î2]D÷ üêÑxÔ7ëBÏ‚u壋zšiM7b:˜¾6¯JºƒïõWÝó_ÑÏh^=èx¹£˜-±“-²]•‰Özߨ†8?KÔÔäBg=gâh§ oÁdâp>ÄF+šwÏ3çoJk ÍxF<2ýw|qf[ÿ°†ðcãP{£^ˆ¥p Õ& TÖŠo ð ‚ÓôïõB{¼Îd(Þ™ ¡»‹Õ­'S{££I&¯Õô=n{ß¼‡ôîn¯§\w˜^ðYä쿯gwÿÝóm}fL|ܴϰ€iw$⨮üP}á×’«T›ÄNÃ1¥!NË_.x:´ Oât•—ªE1‰¦36µŠÕÀzb‚rôxcŸÌ´§]€ñ š·–ì1û등9¥„Ñú1M t¼·`vt8Má9Òä›p:žòa‘)ŽÓPUߣ… ¤›Iá4€j„ÓöÑE5¬k  âôàÀ°>ñ yÎDHª‹†&5á‡ajHeÁ ¦Gwá ëøíñ9ØIüÞàþxæP=Œp>è´Ø¯—¬˜Åàl«-5ßÍã86ÍØ6L§–L'³2wJçO«é;­®í›¦*ÊÃî{¹ÓÌ£xÔr¸é¸zÆúÀS‡/zvGüHSØ'™]ªoüj8M9hïÌ´Èç÷÷‰¦5'*O-3)¹éAÍAy3‘wªå†7 Õ®6.`[m¬: USÀVLq(•Âgˆ=¤äžúÊ‹ßÝHꈮáJCÚ„&³ö@þä¬kZúo+ HÕ8ÚB(õÞÔ z÷X$Ü -p»Ñ#ûãíÍÝ7ߟûAõâöàë'Ò¦F²ã[9çXàãîÅisŽÙÛ1Râ<„(ÈYyÔgþô_}¶P?4NrÞC5QKM&jÑi§›¢ªYÉ9NWYQf\+iVóiþtá@ zRS“k´Ę­3Пj‹ÖNþªEkàTƒ‚w< ÷ZAí1Ñ:Çn|q‹}ûÞV­UœÏK‹ýn¶-át‘ÚIb¨/!:=z¥[Êgý`ÚF&X:«GX´6P­‡³Óñ„R>쬠ƒ:« Wv(¼9†Óµñ¨ñÈð de;é(ûµrƒ޳mGÛ†@þqMãû­IÕ…ÄV›5m@Ìp‚! » .ÙþxâåpRÁÝñáxú¨øôörx}9¼½î¹ªXéÛëq§ôÈõM©,oo6†åÍ‹~¸DŸ|‹R__λ·ütäóKšQ>&íQGó¨Ëºa »!†!®MÛ–UÑIíÉ©¾ÍÂNž=cS§£uŠ.>´^Ì$ætªtÞaI,ÔçÆDxÔJN+í°¶F l‹'p}/¶¥'?€Ù? ¡ø­uR!"J ì!º».zïö„[ÿ&À'€ÕEPô9ß ”ëC:º5 ‰{ë„bÏùf¼Ï¨ÈóÍ-tà~†Ú}ot.¥wÏe£N)±oë@µ¤è³ãQW\MR‡ÉêôüÑo­S>Q×u^UEňºd…A5GkVªF (†•ƃàáéZô41x²˜ø£ÂwZâ h®Á´/˜C ½ ªë@¼ Ø9ºÃ'Üø´} à4ÔNõÏ·ƒB5vÝ+ Ò@Ç[EÚ@wZ=ZhšCµ¢k…Ó­ýÕCO¹2“Ç ­·Äé=*k&H²‰öûŠIˆ"Xn½n2ß"ê¹B#–€Ïj[ªÆñQ9›×¡º!±¡Uš‡ÂéÑBµ,Œ =äö[tqæ4¡±[n_‹)¡;’GÝKQÕYÎÿvĵ(kiÕ:êáN‹ªCÓ‹Çc/oÁé ¯@5¨We ƒ4Ù[{Ô¤§0NÈQ1„Ÿ?{¡>EPQBÎ̈7w*lG Äã0*,wÎrkhšrkó˜±6‚Ç.î†÷3é¼Ú¶oà|ó{­CÇ=ŠÓÎ4+îÈת»ð”Cµô®U¢2|H ÿ$=š<êÂ"·} чÓE& šuY‹üêÖ¯Cl0Bœs/¨ÖŠÐÚ´™°§7‰‘ýÌ(¤1ªŸš’Ö«2ADG¸Ðï)ñýùº-½£ø¯xƒÕ¸ÙFá-@ñÒ×i—yõ‰»I{Ô£Ài«*@µš~EOÂR´àÌlì/)€j{«ØªIˆ*;»èÁé¼qŸ2(ý^÷lo üÒEG·=t¶*ßþ›âÚ‰‡`\¯Çþ¤plo3kOìцê艻* ÇÙê0;5mTeQ5æ)39€ƒŽZ?Š÷¦ÒhG¥U‹¾ÝjŒçt]èxÔ‹ÂE'§:¼ á´ãTGîDRîüØ/r¯–5ÿ9ª#‰G Ø1wZ{Ô­'€ÖSصިmH,¾¨«)¾MD …bô'c%FEôO¹å›Ô)q|ýÌÑùïiª·°­Î·ÆwC‡Ï‚ú P?®"ÚjíQwÒ£FYÊÆ¶C{iœnmžåÔ"åCát^yYh¨‰•šL\?ŠóEq±P­Ð=GxïÛŸPú°¹±P‚ÍaœŽÃsŠRN‡äx¾oRâGátuí:ƒ0»§üzRŠÛÒñ™•Û,‹Åi, &(HƒµÃŽôv­7Æß§=Ønê·ÅkïýÕeå—2x-ªÕµKÅÞü g£¦ÀÑËaú}$'@5\צ<ù0ØŽÉcÏC Õž9FƒýX,Ç7‰§¢½µ#Þt¾(áäŸÓƃ‰áù£]hY?˜WgpË&ãmH¼ZZ7ö9ÉÛâ{´Uê@5Ak Ã×㘶$FyÔ3qž}±×© ·[zñ¯t›Ã.td?ãï1ñ†Â).TϪi¡˜=Y€q;%AQKb?ðæùÒHéÁoHAy(ÞP¨«ùD÷þá ž+ò™‡Ž×£âÄãþÛ”œoÞßAV޵ÔJ Àb§}1µÖ«Í¦nN«1@Gmý:ÏDá£UjZ3ÊG•™”¢¸rå3‰«‡ëÎþDÆ`8½8b!Mö?²¦€—ÉV{#×:!î6¶ÿÖx޶™Üý'q§<â½ ÕXCXhƒ ͶÙjǧ ¡ù7‘NpžPõ{% <6î~VJÕ ÑÝQÜCeÖ¥7ž=4¿&,Vìüm:¬Ä[¯Ýf7¾_GTŒ AÆ@±cÜ:ñˆê½r’N<Á¸óÀ³V‚»bPˆ‡ëLãøX÷;Õ°„r¯¹Íg;Säà‰öauŠžé¼%º‚ñâÃæUw: ~<¸K¾÷Ä÷`ðW(…j¥Þ¬é_«¿AWÏIç7ÿ9“ø«„ú£o†¼ã;_åɪ{ Õ5*µÊ ¾òú^zÔzL0SŠtD±ë@ÎÒ(§Ï!r_º¼æŒ¨KQ„G]6dæD Õj«ÆÓëà8ئÀ‰žm®ò=J1û—Æ~ ÆqB?Rg¼ P1ç³E¯”©ÏÙvÁxŸ3bÝ_:¼#/™±âñm…·ŸÎßž|*Òu§ÆBuèóù—´ñÆpô$µi‹Óeò¨C®råÁ¢z|¹öþÝø Jê5a± †P=±#äÄ4ö})ÿ¶¶ç‰Î= ç–)¯ßü®ß#án?˜|nöêýÛâæ“ñè­_‚mFÓ)’7œ Ý’8AurE8öán¨~³vPGÒ½é0(7¯ž8Öj0zŠÆaRˆÃmÑxÅažÛñ¢×KçóŸ#âùGó Ú’øŽÒ t±5át/{Åž£>‡»|fxÑŽÇû#Õl<„ÝmË`†ÏâœuÎ9–tž˜"gç)É$)§Uõ…xÔjôíT“2j³Å‰‘¢35L_+§G¼5+ÎÒõEÄ×¢‘ãø©Q¯Á¨HÕ¨-NÏÁ6icéËpB‰›@ý?Ai ªkº¶ÕƒÓl ÿº‡!y J×¾wÍ@ÞKÒµ^W=×XüÞ·ô¯qñ«PöwÅè/‘ŠA›€t›¢>À¶<ày˜[ ÒîÀ¡4ŒoQ­{ Õ™½Ú¨…áÅçôØQ†Ôo;_µ˜õ{¹ýÛ:Ã8>Æ ¯L$NTùåúÈ$þVê“uŒÆ®ÎÑø.eIŽ—ÏÇ[•ôƒöÍ;”bÒ€Ï8zNFαع¡gÎâeœ‡‰]-e™tÀ.§“TYl¸´öʯ˜á¥uðǺ¥\KmEöÉtý½Uµ¢‡Yîä8©™Åî>?ɹºõ;EcÏ¡¡ø¿ªäý>ê³ _CBJ¿ ÒÏôZ÷§ú¹W§ÿtüƒ:ûã…Æócâe[½l9x+L˜ÆŽm‹W׃‰M³‹hyáAªÚM˜ž÷¼sUdlöaÏ÷ŠížTµ·zÏͤçsH7w~q¼õ½ÿ§õôŒŸñ3þgc¡eU3TîÕÙœœ¡G s‰ÙYû™-Ó$dzÆÏøKc¥ólëõVã`ûвµýöeyT?Ë’5¥Í£uI‰—Å©_@=Šáªÿúå–P»ÝVc´nBýÖ>?ÓÏz¿`ñœ?ú?±6óð»~CßMY5ô݇׊ù·.÷]ŸC/¢}ÒöÓ§ÿ6=ãgìO ’ô8[Þ´G-ó@ŒG͈šžO}êï×yc›P{ÔfžWסêÜ8Ôò{—%P³ÚÐ?èK(þ9]¶b|Ã(Þ–`ŸáÖÛ|ÅòÛ¥Ÿ!Qúù¤¯ŸÑôs;ð­ }_BËz‹øºÎ¤´I¸¦Í¯uŸ¿Þ>õ©ÿ–Ï'ñCxÔï ª™õ¡‹ñ¨ã_‡güŒ¿$– ¨Ÿi=‰§PûÐêó÷- ŒCp 5À!xÊnmŒ†Ñx Åpù)|}.Ÿ_B·6ñsãW!zôû•þ]†Ëˆ~ùÌõvëõ<ð7‚ϯø[öŒÿ»1ô¨GäQ¿1„æ(}zÔOý¯jèkl/—躺Éz›9úÄ_ýše!:Ó8ð¡øwèò ¼§ý,ÿHýJûGÅdÿÓëç„÷žô¹ý‚ã»é< Ïžs~ö/¡ú/Z6\[`û„ëáL4~ÍLº–þ‚ëÿSŸú({ÔúiwH¢v¾ÏøÿX,—@ Û{”.¡ú­m¾wY1jC`¡õÉNÚërî.)ñsI_>ó¹ÅQèvÆ¿×S¾S¡ï ù.Ã6p }ß×’~]¥×çøµ=TþFLú[óŒŸqZ|õèxÔ;îQóõ©ÿ =žÖ<êÉõ¨ã_“güŒ¿?V*—x=i3ÁúÐj“²î÷.KB k({êӷߤËÖxYã%¡>Øæ3K¶–Gm+¾]¨¤Í†ÏóEúÐ÷‚~à7.ó°MBü»–Ð56¥MÊ59ÿ†¿;Ïø;ñjµ™ø`“Gýò?¾¼ˆd,ë÷û½lcj¢Î¶dìÙ«—ÿÉøããÃìçÝÛ†ár¹ìv;Ö-ÓìzÆ1þ™xcó™|³Ò÷n>ŸÄö? Óô[œêƒmähª÷İÛ|ײâ­íƒqŠ‹ø»Áþ©ÑÏœ×M_¶µŽ/)ß_z ¡ë~âúã¹¶l½îm¬ÿWþ<õ©)êäQÆ£ÞIúF=jøÕÅh½m^^^RúIŒYo)íå^mÚÏH̰œAµŒÇa¨ëšvâgò™í>*¦û@?ŸG}Vß+•Kz û‰/¡6)ëþܲ$ÔݳÃvØü º<*^–OÆË'âìÃ>‡IÓÏUøq¨‡Öý]ËÖk]¼>ùZ=ůÏÏøÿ¾øèz‡Æ£–ºÍ£¸¦ÞàÙ8އÃ!ñ~¿£oD#Ž7ƒ^†¾¯¯¯eY—U¶‡ÎöÛ}{{c{_e€-ßSö¦æ€‹.· ?“¢(Ø®²ž«ªrö?Ïs³ŸŒÞßßßÙŠLÙûºãó#Ó?Ýg?Ùî½¾¾È{Šû¶þêüÐú™´Ù\/—@œÒæK—ek‚ùhû…®ûi—ò©ß« q蘮œ°~þ}Kü;¿ï´}à;þ$ }n½¾…Ú<õ©ÿ4˜G çL”õ–ñ¨Cn§©7®òù|fÉbáñžûwâˆã}:þ1p¿^¯¼Mû¸+«²ü?ö®\U&ñÆ­F#Ñj4©F£ÑJ4©F£ÑJ$©ÄF¿ww`]Ø]ÅãÏ™÷ñ™ûÞqöY†9Ã‡Ž®ŽTk=(4-×V[NŽ^mÖkteƒ Ðý‡Ï E‘î?\îãñH“ƒûúß±oï?ú |»ÝFhýŽãõ^J‚Á ^ØõØJÿ8wÐ{ŸVûeÙ¡­äùù_”}cí¿Uq…É…Gï³y/øÖCŸM—²÷­Ã§å}®)Ì™7¸?šbÔ3F½|8º%.ŒHožçA0¹ï¾ÀŽëo'“1EY!íùƒ¹ÁI’ÀáÄf³ÙÞˆOÆcŠ·Óí¶œ\÷ 0æglG‰eÚv¥izwÏÇH×ßÞXÒ·ãñø‘><]ª!üZ‹â} ÑOﳩþsSï坯ù.{SÞÐ[¿ŸŸÍßÁÉ|¿1·–ôÙØú¾°ãç¹Ó÷|ôû¢}Mð¬C­],Y²$YË£fu£N«ðõ yÔv”^ÜHyu#‰;#Æ-yÔfv„|¨Üàãñ¸X,¶Û­®ŸFDÒnËÉÍH»9?vÿÕC‘+øÕ³Ùôt:Þ×ç–<êöþ;ûùøþ>/%¡ƒ¾°õ67ÑWÿ|œ*ëå}Ñö²CE_-ÏoЇo“üNL=ñëÏð£Êà‘µ«ÃÚxcÍ´ä;\˜3¿›ßÊ£¦õåyyÔ“ÉDÇ`ï–-yÔº~x¡v4¸}w‹^’¶Ä'Š´Ì‰“;ÝlÝc·"êûý~ro”¸%º½ÿ×u–é6Kç)öˆÞ¶–½Í6B´pas_ï‡sO½·_DÔlj¸ÞÅ}²¯ÏÞ7wŒ±ËxE? u|íçWî;»œãý}kÎo¬™Öµ†%Ë•ò¨U¤úÙyÔ§ÓI¦w«¿ÁÛó¨)Ù™G=NeîDŸ¶4GÏáÐRu–¦Q- ²Y¯×Ûíäx<®VK»-“ÛsbrôŸæÇì?”‡Ãê—>í]ýoÉ£vößì' •ÚM Õ}×›ðÂÒ—’Лõ´Û›èkóù8›¼‹#gEM‡¾K4òâ«,»‹ÇîÆïÁÒÛ6&Î=y_›šþÜ´1¥Ãæì¶,õwʳQϹ«M_î•v»–Mßú}cq”m•ž¶„Í[ee/ZìmüÆ~œ³=Ö ß:cð¾ë^+Ÿuž9ó_àFŒšœês3FíÍ£.˜ÿ— û}¡²>f³ÙËû#Œÿ;ÌÏмè¬ï+;•CÙ´^΄Gߥ¬ÃÆw)7eëí@aê}‘ÃÞ·>úÚßÀ¹ƒ¾¯Ïþ‘²}õ£ýX?\Ö÷[j×[¿m×ï¶øüºQö¹kˆoê»î½ÏzΜù¯òŨs¸ÓåÛÆ1ê^yÔ"´ìRöîlK·8l[7ÇØñíÝy™à1’¯SI’äú?ø?——²ËåÕvAí: ¦MˆS«ýPîÊÐnç}už;è}ܶï!ŠºEMÏn+ÒkÖé<[uVÖèÕÿ.óÐ{&­¶|¸ï˜Þ…¡~Û}Ïîç]ÑÔß·nXÒ·.ÝX»Z“Á˜3ÿƒœvÏK+wÒ£>÷Í£fÎü«xÑÅþÙ©ááØûl„/’Ö÷¹Þ˱0xáæE«}—d§(·É» oÙ¡léCß²]ô}Çå9^>Þá87êñý–Úø¿ó»Î…î«D—ó½ƒý3Ö+æÌ™—œbÔI.¤G­äù,=êÆ~Ô©‘GmgÉ’¥CŠx­N!\ö®Dz/l{výó¿‡s}_Ÿý3ÊvÑÿ-<á7ßã<õIOÙ.ëRßõŠ%K–7e¸\‚¢Ó¥SíË£.yÔEÁœ9ó®ü9T=Ÿ#…É…pë;ðîmÝU'šý1õ>ûV½£žVî˜_ºð‡ÇØcúŽ¥Ëo£çïä¡ßáü÷ä3Ö ³NæÌ™ûùb¹Ò1j%ñ?+F­?8FÍ’å R Ľõ Ñw‘ì§ g0ž¾¿ÃžüÎsÐâ¾3ñ¾õá‘õ„%K–eÇ<êÔŒQŵæÌ¿ˆò¡èɽeÍѽ«O.K0í…UöîëׯìÁ»ØØc¹Á»ÔÓw™ó‡æ³ç¸ºÈS’-¢d²N‚M(Éœ9sæ7ùb›`õxЗÐ{}¤Æ^×u¡ó¨9FÍòË%áò ±èÑ “ ñË\tá&†²g<ÒϾóóéèü[:¥.Žð¨£}ºÙ§RÆémÞEv©gX·Û$ޥ΄9sæ ŽucºI±†ˆ¼ˆr?ê·µŒN7cÔ†û1Xxî+Psc Îs•« jËüüvg¨õz²,Ó©|@÷“%—oðÎ9¯ñ÷“âÍêéÛ–ÍE›wà=ú)ÄË' n\©_×±0gΜyŽÕkHÛµò_ïL”’Þ™˜1êÒ©6cÔ…rôµ›AÀ„–ÃsEÀ 䮄à÷'G©zOàNënHr>_Ôw榬ë1(‡S}K “ÓrcpaóÎÒW§·~í}hç¿YÏ_ÃPsØWï± 6éö»‹3 F+¶ùG.Ñz•LÓtµZ%I⾩¿¾3QI!= ŠQãªlfƒèuQEóÌ÷t¹\~=WÍ“cº‹ÓÆê›w\>xê|ˆw@§>?0W}K½Ǩ ÆÝh‰QÇq¼’XRâäj¹Äÿ¡7-Í<ê\ǨeõĈQ‹kŒºŠæI·$Ër– ™çðËJ§‘çª.1#öíMÚ‹ŽTͽo¸Ó„å*F}¾Æ¨;îÂÃí»rDö­g¨vYþÉyÔÌ™3¿›ûò¨á-À—’ÓIÁåu)êe®òWÉÞ“G}Q'ê±Ä¤Ì£–!îÄr„2ÃQùãγùä¹2ØÄŒQ_ίêO‘;Ü{;FýóóÓnÿr49KAOr<ã¬ÌUgèÛ4MÃ0F“Iù•.uR§³YÛn·õõárWŒú>)â]êìmCË¥m_}Ѧ·ÊŠö²>›VûÜWgkŸ‡ª¿‡ý]óé«¿ûoÀŽQã|ßl6Aà\ƒŒ¢÷õâסÏwHœ×8Áéqƒñ>h‰Q' ty% ù€iyG5U+]“:ÔjñCOAÂOX,°´¬^µ”]ûFÜgi+aß«9;òiÏô«ÕJ¯óëõ~T¯V|½½Ö6òT•k'V%]ëì‰>^p,qõ®¿=‹ùœ|ÑÐøÁ~¿ß7ªŠ6›–qÙM¨Ez•D47õôÓ©×\º»–¾VgQPRý/Sïv;Õ©|ǾnKs]çt:uöávõ@¼SýÈGêñ•ªo,‡•k;z6›eYµ–'Ô~ÅV¨Râ#`?ê”'ŽË{tì—ûÀœ9óvÞ’G]úu½¨¶VÐú1êjë<3F]w„®~ éºÀ?Œã˜V3Óæµ¼ê¡×~𭝻d7ÚÊóÌŽ|Úöp„’¤ôa¬Rt–}Ç¥z;Øü8Ç>ø<ÛyÔyÞ´©+Má^ÎËRv:ápâz„Šï¶[ܸéþk®kT§{\hוG]ÅxÕ™VF¡G=è‰zÌw XÔãNgõ‡!x¼Ô s®Å¨­:µ3â¸ÏÂèöÄQj³YKÏ¿ÒÃ=ب[‡ý½yÔ¯’â͸¯o}ûïµ=yß±ô­_tèsϹê;Ÿ}ë´íí5NXú+ ¸Ø8UÇò„ëÀ5Î&yš«gùé4p?ÊèÇ4pÒ‘Õ¢¸>ßI‰ÿšF[´MãñH·xEæv@0™èæ°Òè†5.ô–8HÊçl—ìXuçÎ`0l´ïõÑEúó¨¯ûQSâG!.fµrvR%J Ç¢©ˆÓ2¥ì®Ë|>ÃWÊ«L Ëår¬@þ’´L`9W–sm‰u#&Ú…CE–q¼“ñÞŸÈívkö¡ÞŸkÑZ$±rîãX÷sEPÎfÓÓéÔ—³? ™ÉÍ=š‘O{®à—éúµÄÌ 3© ºVú$ÁÊ ½5´'[5j3êîëlàqѬbŠ4ÇØížS= =ú£¦&+!=¦–qt «,OP¢rtÏœçºÔ·fŒúFOè‡D.ëz½vÎå¿v5N.y¥Øí²¬™_M'l¡þHNzœ›òùý<ÇW(HöXÖàTò<‹‚n~A UñBT¹°"`Õ: y®QÊ¿7Î_¬™¾:q•¤\¯x·Ã2«æ$¯¡s˜a¹@—„‚êXÛ¸ÊVÞ Èœù{r#Ú·6Þà¡~gb#F­Þ™¨_AžZyÔµ¸_jD&W®ïN]ñËøa´Ùlj/¢4ݬ×àdJx5X+(Ç,SΊR€ÚÂâ†Eõï÷q–1I,,ðŠ3åoËâeŵovì> 9–»Ýnªîçjµ„’V¼Æ¸œýi´…ÕÒŽ|ÚsEË5愼_­_.Ãí6Òu½—SÔu9?×X®§oèùZ½ÜcQúD’`]…ChϬ͚7ÁTÀ_†DßH%MªÂ(ÈÇ‚”ëõª£6ëT1êFµ=‡æ¸èÞg’²)ïD\Ç¥pqQ“Sêébä—ŒQgŽ¿&hZ{Ö*,Im–gŽæÚa7¢aÅÕå6bSff5Ž—ã²íͶ(/TZÛèƒaÿ«yÔËb ›RŠžü‘²CÕóìqýæ\=£þ.Ò¹×–/\\‚Éd6›ª³0Â×(+gã¸ÐœÖê¬~«‹âÏsó|§¼ÊÌ¿gQG×ö£î”}ubÅÚ BÏ@‘? ¹Ç”ŽÕ¬Ñ1ç¸t+ ÉacÔ<êK£.#ÕUõ5îªçʪåQÃÅ’9®•S o‡8¾"=$8•Å"PfAu’%E)i ¡¬cø?º]åhM”òhöÇæ2¿×©¯œ®2¨÷­Š¸ºûÓl«–Øà+@XÂEÄ:¿“qu©Ç,•>ª²—ƒ*G½ÒÎdj¸ú-}3Æ›T‘vÅGŽyhÜUÇeLÇ•Ëøy}¼€î:€šÉÒ7Ï™vbóFŒ:kŒëšG-H¡þmTõpÌ'õ„¼hè1½ä|:ÇENµ'º0ΰBG€Íh³æ*Ú\T|däê„›²ëlè…Š†åyfëu[p (î´ C ^™}Ðög£6ó¨•üpþ[cWͻ؈’]ì»´;èXzðÎcé1‡Í>ëq5Œ<ûQãÛÓñ¸X̱ؒ~d¬3£*ÏY‘¢ÁU `Ò'—1a:‹G#="y¾Æùn´këáîn£h^åQËó·²·ë¤Å_¨¿^ae 7›®°Â*†ëþ;Nq¥è8.çü0gÎÜÊ£î»Þ £.Êlj3F]*U¤º±uVÂnH#3ö꾦F4rä*Û¸¯'=Z‡G…e6VO¢¥J¹¤……þîïì‰ÙCù€Ûb¡ÒbGZ¯[1ûf~k÷§!;äQ7%F1ŸÏu \¥Rdq¼S>!úH£ž£n̪Ý7s¼>î© ‘ú ˜ÐÔeUŒ:“‰7”®bÔ®ú³Z¢…µ×‡'9§Q.+ºõðôŸþŠë‹LÚ÷«:Rvµ<„( aDÌ(“æ¸ÁIé¼E±^­à ×dKÃÒS§°î”采óTöIþb3ß’l0˜íòA$_ôémó¨?ŠÙ8xñ@Ù_ã#ýÿÍ1ÚyÔ8+õÅ.S ÌÊ5•ßê|cÚWÖ>75_†áñp(Ô©]:®*çYæc¤™Gí<¯kyÔyŽ…¢¼#®Ÿ¿Î:£h£žO‰èQ#²G·å㓲cäQwWs­`Μ¹âvõá°'ÿ™Üf%÷’‡+'öeŒš²;ìµhĨí<êÌíú¬¾{C™GÉ¿£©p¢Ô¯UujæQgéjUæ£Ãe¯Ê×¥ ¡œó¬’ôˆœTVg£Y}׎ñxt:i;­WyÔ«LåQë¾éq9ûÓh+wíGmÏ*ǽ 9«2½ÝR[ÐC—³éTÛc€×Q“«Ÿ¥pçd.¿oæx}ÜyŒ®ýTOÒ=«jSzNGNÝr©Kéäêµ1ŸÖü8ò¨ui߲눺pȇé®g{ÝëãÚt¿-%ÒøwDqþ5á,šhQ£8Xtq,ª½>èM»”]§ôØÑ¨¾µYªÚ h©íÓò¨YÚ²xB=CÕù¦ÒŽQãdGJP£ä@}B¡mü;x8cÔøvZítZe}оs{¯áBc?jçyí«“’ë°Ô¼ÌˆÆ:£÷úл9.ƒá„£&7ÔN±#É §Š(g[ÆÁÂ应ò!eVÔó¨‹ëÉö^-QSŸÕmµ+…Þë#)õi çv¢Ðæá[µ•ÐmpAuª Ë]/0ª“‘£½>(eÂá4£èjKŠmņFÕβŸ¤W[[œãröÇjˈ|úç PûQOFr`5#ÌèÒH=3®íQËE’ô”tM[:ûfŽ×ÇÍcdí×ÝÜëƒìQÿXí.E$QÁj|{ÝëÃó× £ÎûQ߈–kŽáS—ªë¤ñ£”﬚ÏÖÇ$Ë(nnäQ¿ƒcð¸<êâ æÌ¿•·äQ3gΜy;×1j½¶ÄñNúÒ¥#} út*åIÊ“ô·áŒ–1ê•£>{cÔ¹/šQCîz_ßJÊç+ý›àÊ£Î_Ò“Æ_Nvuyº}$çTqéÊ‘>‘#-=jâpµU(»¨bÔ* ]n ×È£ÎÊõ¹PuîÈ V`^ÁŽ|öš+ ÇjÕ7׼퉯èý×ñj—`()êyÔÁœùWs;š9sæÌ;ò¨¾×G!cÔñA§|TiÚµ®øA¥[ˬŠQË·WNu÷½>(¶È²&]‘Ïî5L§SJ\y‹± -s;F})cÔ/9Rö_ZêûhÉyÔ,ÿ ä5ƒÁ¸µyÔð¨÷uwºŠQW‘j•G-!ÌuÞ/ºP1XF˜;òÉsE(”§×È£~aì¿&”§’Qó‰¢WµŒQ/W…¸ôQ*ØxQ‹ÛY…õè/æÌ™(/^U~’|‡¹eÎ|@N—ž<Ëì_;ŨŸwÁ=©¿Ïþ~Yƒñ ,ZŨ)"}©åQŸí3šœjŸÔðåT5&E•Ü1MB»—(k{àÌg"ÚWÇe¾…„™4æÎ—ÊÎI.cÔa#F]6ª$sæÌ™3gþt.þXµÜëC‡[(ó¨«ß<Ëàøáz~QWdÕ5Åz ÷•¶•Ó¸™G­·³pFqQŠò¨³4uZ64ºÏ³ÙL»Í {!œÓ±ƒ 3pt©½¬~úÒ›G]Í­3Ú.~³uΣþ›.º£ž£1nQ/Ç$Ãç ¤ŒQ‡+ΣfΜ9sæ/äâ3ó¨;¢‘«|©ö£«M§×ë•=NÓ”ö²˜Ô÷£Æ%ÆÚÓ_iýڵׇ®Íäpãi7 òØ/í1ê*Ò‹#2Ni7iÛ>Ë2JYAcU¹ìÞ¤Ì w–5öú˜·ïõáì¨öúX{}ÈÖǮփ€Zç½>þ&Í£fõxRzÔ‡SJN5Ìæ¡Üõ‘ó¨™3gΜù«¸øê:FÍyÔÌ™3gÎü•ü«ó¨}¡’[ !÷ß™øg1Tõ©ŒQyÔã Ö,ŠQ¤ÌTu#F]6ª$sæÌ™3gþt.¾:šÁ`¼CæQg5Zè(sæÌ™3gþL.1êJ¯.ÚUO>·˜ ˜…«5çQ3 ã…_Gm¾w›€ëöb>KŒV«¥0Þ{ˆÃp¤¶ª6÷£îŽ–—žÛ–æ›Ä q¼Ó5ÐfοzyÍÍF»®;x3oÅðyÔUŒz i!}lò´Åù6bÔ¯ŽT0gΜ9ó¿ÆÅWçQ;Þ™†¸l“·ßïõëü0jý®|‹ëú²zgbwØoBl±´ëŸÍfÝk÷ ~FßÐîóÌx<1z€Ð;_È©V1ê çQ3 ã…_G=ºVµßÐmWyòu§Ó©éÌo\ïL¤÷*B‰R¤ÄôRè{±Xèx,,7›M–ef+拼ut-¢]üR¿ržƒRŽL¥n( ¯ ™–ÇãÁû=‰??qc Ù|û¤¯-ü–泊 ¬îÿÕÒ>êÁxQöÂ/UüR œG™1ꩊQ—Ÿ2F½ÚœGÍœ9sæÌ_ÇÅWçQ·'*À¡…HÞ£ð¤y¬V+rqµ‡_JJx¿ðÀá"bº´G ¿ú‹Š~A@J|K.7ºMÉdÙxŸ ümT¥=|ÝstŒòC µ=”iš6”¨œRÎù¦²Û–&àîêü³õmQ¬^ÄŒQ;ÛBýäÞï¶[ÝmiŸn=ˆ£õeõ—Æ×`°<ꬣ–yÔö¨)R]ˆK£æjyÔ%Ç/UQ8ÝÙÆ2ŸË½>0!ºÿÚrZ»q¸ÖÏ{}|%Þšöú8»ò¨1êÇ¢ »Ý?ãÿþûOÝeïM›8–_ýû÷¿| DüW4Y–áL ýʬ5£Î²æýúFq!‹§FqyWBâ[ªðM¢1Ì™3gÎÜäßGÍ肎ûQw‡Êúè”]ð~Ô_Š_ʣΟ’G;kô¤Ès8Àø%›_áŽ_¡­(ŠàZÊ£nÃ÷‰°wÔ\\kö‡Ízµ¢â¥ÆU!ƒÁ`0ÞßGÍè•в~¼ ²Ýn;¾ó‘ß™ø­ø†<ê<ß«¿O9mò<ûùù)*—Ø´A‘4•wäu[e«šUñFýð´)² . S“JÍ{Dc˜3gΜ¹É¿;šñ›€74›É “Ùlf¾¼†ñ1Xõëö£¦4 Rv`·Ûá¶DåŽýüû÷?û$IŠzØÙA7j¦²”㑦²8xßÈspÒJ“WƒÁ`¼¾;šÁ`¼çQ·ìGý„ø ƒñÍ𭽜GÍ`0Ç€yÔ§yÔÃïGM»çU¹Íå}ÞÙ£±W|]ÚjƉZHQDo×eÛ5kM ö·,Tøœ4úƦ¯>ø ãéHÔ˜‰‹ïÙþâ,¿ÅÂÉÿßÚûÝyÔö›qÝžÏç£ÑW±åri¾÷3†!}eîGݾ—ž;-íÝ¡ãx§kè¸WÆ  —×Ül´û躃÷úøV ™GUûQwÏ£6—¸!8`ŠXçóø«>ƒÁx"°n“Ë¿íå_TÊÑîÄ—Åá2?\fûËx'õ?Û« ¾]Ÿ.ûLºÙŒÇá[{1j­ÿŽÉ7a&K»þÙlÖ½†Ñq?êgô­àý¨¿”Gm®9wæQg}ò¨ù‰ ¯>ø ã‰Ê‹Þ^fñe²“žóQ)¡'‰ÏX¹ÙÒ»6ô0[9jý(|k/ŨO_šG=ºVÕ^âz½6ÓÎMà*O¾®ùÖB`ãzg"½WJ”"%¦—BßðÞu<–›ÍÆÜböæË»ut-êw&êw&IR½3ñªÔ …áµ!Óòx<Ø£s¿3ñç'ŽcŒ¤!7Þ2élK¾3q&ß™ˆ²ºÿWKcø¨ãEÙ ¿3ñKá‹QwÍ£¾Æ¨åF';ºhË£Öšø;üÕŸÁ`< »´Œ—žswŽs²“5,6FWøÖÞÝ냜jŸÔ°ó¨+½Œ¶ÁD_}:¦4hGemÜtàM—˜,1ÏÚ;¥oÉ‹ɲŒ|{3J\5P§ÙÎê4•hHƒÉ¤´TÅ[rÂÇã1|FŸÍy³|_[¦rd _+'•%ê×ñy|‹>øºÇøP4bÔEÏ<ꢸ±×GÚ£~y”øµüÕŸÁ` ,t”®ñç;dVÈŒÔ5Ÿåbt…oíýÄ<êîh‰QÑÅ[çQ·Ä¨ •ë;#R:=pSiXþ¯Jÿ˜ßê°íjµÊT$Ùv_Ñ+Ø õÙlFÑfê½!b>‡òa¦VH–e9×ÅoöÙykа´Û2 ÌR¶e㸼${œñT –GíÛ:ç0!ºÿÚrZ»q¸ÖÏ{}|%ê1j¹¶ÜŸGqu?þêƒÏ`0†–8Ú£#D§µÄgu,CßËã%¯ççÀ·ö¶æQ?Ñ£f'ê}Ðq?êîPYR£ ÞúKñü<ês‡w&¾EÄø÷ù«>ƒÁ»Tæ<Ï÷ÃD§‘êõéº[u_âT*íð­½Vu©ÿŽ5£ TBËúñzt‚Êv»íøÎG~gâ·¢ŠQWëÌyÔ¡ÞúÜ7F­ÕøSüÕŸÁ` ‰©JÏØ§ƒE§íÝ?VÇ2 ~}ãö2ŽÄ4*fQ¶Øf‹( ¥L!C)Ó0J–Ût¹MÖÛÓz{ÜìN)›í!Ú£’u¾Ý·»ÃnŒ÷Xñ“4ËáƒâƒëþsL¨ãÃøl÷E?–²9œ@âã)>&û>é1ÉŽivÊò$+²BdÅ9/ÎR ŒQææBÊB…ú…¸Î@_/Ä·ö~w5ã7oh6“&³ÙÌ|y ãâ÷ò¨ ΣnòW|ƒ1$æ=.NÛõGÇl½9ülÎ:j 9ŠÎæÛåûšË6ÿªÖÀ‡ïPóÚ­¡±Þ5ù£>#5pôŸI%'QDâòWó¨ ÆK0du.Ô‡ó¨9šÁøs€£K¯Ftº!“ýV„aº^$‡Ýq¿;ì¶ûÝ.ÞívÛxU2Ò2Ž¢½ú6ÑaW›£”òs"¹4$4ËènN‹(m²i”O£tGå¯n ßdóMŠl›DÊ( ‰o®š…²™GìÕ'•µmPalò@Ié «š+YTm•rð™qû0Rþ¿ ËGâŸáçÿ«ôÏ…ó¨ Æ/â=ö£~‹ˆñïóW|ƒ1’BzqÓø‰Ñiͳí u²^dÉ)=³ä¨äwòêsHŽûô¸OqzŒO‡>Çx‹ÏAÉý.Rw[’ø\8šÁ`ü"žGÍûQ·ðW|ƒ1޹ô¨½'±£‘ô¨OÑ2M¤ó™)ɼÁ/œGÍ`0~ƒåQ7bÔœGÝ¿úà3ŒÁ°K¥G½:>7:]òU(–Ë}´ØGá~³(?š¯KyØ„Zî7áÕ& c”ÝB†ñv¹ÛJCîß­v»e)ãõ>^ö>ÇäF~¤f³W_íw«}¼‚1,•T¼ü¬ëŸ ä>†ÜHoö²žòs€Ü­!»õ1^w›c,?§ýöúQ¡éä;ù9ÜB'‰Ïé(߯á[{?"} Ãplímî5=–_-õWöÛõÈqÁd:âšþK`0þ†Ë£&wZÀsvæQ§œGÍyÔ Æ÷b£^n¸Mž£NÓä¼\Ëe6Ÿ¥ói 9›J¾˜]ùLê“ùLs§ªa–µhæ†fnØ,æl:Ôc·µhµ™uokzy¿<êî@‚ê-*pþqÅÕ;76=Æ(‹qûm‰¤IÔ Md «(f³Ù‰jã 2:÷çQœGÍyÔ Æ7#ToKŒÓ§çQ'ûH„¡X/ãõ¢ülÂxJi|v›… A+)ùFZ _“ܯHΫJ.¥<@ÊÏ츜ÂÙqµ8.¦'Åô* ¾Q6⫊kÙÐÈzŒ:Ëš•>¼jPj/ËΤTe#Û¥>œ¤œŸæÓd1+åbš(͉x¥¿|rõz½Ún·Î¯l·YûØæ[ùJ2^,æ:ަiÇm“ F/põ ù«>ƒÁ 3µõ)zŒ:ß®Îa˜K·yqØïއ˜¥S^Þ/º;&“‰öhĨqiø/F=›Mõ+E@Æãñ°½e0—¡ó¨SΣîÃ_}ð Æ`ï¤GåîéyÔë¥X„É&ÜEáñ°S$K‡¼¼SõH¥>ëügRRpÉ”†½÷}ÖVõêfu’$0à õþk;”Í`0Ç yÔ‚9º{Œºö•íQW8ÕÓé4‚Ýn‡Êê&ƒÁ¸‚ó¨_È_}ð Æ0€¯ûŸz!øs£ÓçKGç0,Va:ŸîãíËãÀï,/ŸœG½\.ã8v~e§vhÀg6_„ MîµÎa0bà.a¸úàJ{¹µ×‡ ˜Íçs”½(wœöÊCY8Ò—ÊÇÆë7ÆÀ`ü1lkY÷çQ+wÚÌ£¾\wϫŨ¯yÔf[ŸÝC…›Í°i³Z­ ÄêÏ÷㲟a¸^¯a éÓèúuÍp­±|AO^´i#‹¯V²øj%‹«½C©ÂinõÿÕŸÁ` ƒc.cÔôz—çæQ¯—2åc³;i¼AøåÅô¨ëk¯£.õ¨M9p%Âh2iîGÝR ƒšÍf£ÑÜ%¯ÊÍÆå Ž4ª‚ä·Ì0OB-:~?êku1|ŒÚXóÿþ™ÜÝ7¢Äp¹Ó*’Lî·­±«Å]}uõ¨+`u¢È6ŠK¯›4ªÂ¬Ò´ãÕŸÁ` ƒÿÙ»Z0e™(ºqã­F#Ñj4©D"‘:‘H¤F+‘H¤‰ÆyÏÌ…qt]ÅÅŸ{ž}æ›ï2 ƒìÂñ¼‡{My—ÇfùÀ­ÖWŒ:÷Ö©ðŸA~æVÞ¨Q?Q3›e0Þñ¶Ò¨oðQk:Ý÷QõFMnÛÉvS?I|—·ã ÀžçfƒZï´!¤¡ÄjL#8ë¶4ãûó“ÁCï^þû÷ï[C9:² c¾¿¾Hß(uÿÅÖc ÿ§õO}ñ Æ8ˆöJ£Ùc}Ôû]ª^K ¼|µLSñ :ð3·’õн·«Q·ñ§Ò¨ Æk¡Ñ¨­{ί|ÔÕõ>ê‡iÔX’ã8E„V»%ZjŠœÐ]©N#Ðî˜ÜDŠ¢À"¶jmQô“ÈeL}ñ Æ8På]b]Þå¡Õ“P½–©D“+ÀÏßÊçóQ³FÍ`¼1F÷Qç|Ô‡s>êÛÕi¬ü6˲N¼1]” DnѬ»´M*R#ýùA¡;js©JeÑÇÍóL9CÊRO¨#™Žü´~S+¶ÀŽpäå"ó¸þÒå]"‹8ÖmBò2õ)NjóÍc*Ô®Wo¬wÛ$ }"R-©²Ô§8E>yŒ´s}œÞ{‡s}<™šÁ`¼Nr}Œà£ÎGMJuO£>§\‹$IV«U'MÁ÷ý­&üxØ‘¤ìy%ýÃÐoßL4‘ã‹„®ëê›mMËåòQuh&ŒcQ¾êHÂxÂAL}ñ Æ8 ò.¸Ý=6×Gèôk‰©ð'W€Ÿ¿•ïå£f}›ÁxrŒê£>5ê®úL®û|ÔÝŒv:N/E²MéïÈGm¥¶sò³Éîšù“$Æ<í¦ !ʧ‡H#‰ke}ŠèËÂŽùëŸúâ3Œqðm—wyŒ·ÙÚ÷j×ÍüušD“»”Ÿ¿•ì£f0ˆ±|ÔÙEuñà\ À$;¿¦¾ø cTE§çÉ#ÕéƒÄW{õZ¢ïeëeK§×Ÿ¹•ïå£fšÁxrŒç£nè4~䩺x¤ú¥ûS_|ƒ1²J1êeúØj‰ûMLÕ·þêàçoå§ú¨³,[,®ëN½ßW$Š"i­ÿræíAÌfÍ.˜ê ä=ãï0–:;ç£>jÔãû¨_S_|ƒ1¶¥bÔÞö±u‘FªZbäoB÷àçoå§ú¨ç³Ù+’É@WL“Öú/T‡<CÂ+ý:Õ¸+d0.c,un|ÔUÇG}xœúÕûS_|ƒ1â¬)ïòPu)Týñ"òÒ8xøù[ù‘>jPÊÙ?…™þÖ$Vã|=ÏGõ<×ðm|ëÕj>Ÿï¶[£îÚʰé›Ý1›Ù[A‹9½¯döŠÂs"ˆ­8„ã8E"bJ@p-Â0ì¯ßÊGÚGÇת:äl©_nêì‹ÿÅ„l•aü%FóQ·å]ÎÖL,ÿÂGýZ˜úâ3Œî£ŽóÇjÔu¤u.<ýZâô ðó·òS}Ô†ˆ‚mâ1Mý(ŠÀo¥&¢BD ‚ IT*È8Ží½ÌT¦vJ»cäÍ ­Bt°iÙÒfD0xo¥óhÙûÐ73€®+î¬ß=IÔEKsÒâ‰Æƒ,—ËξR»G|Ï»òCc0îÇX>ê–NKÛG­Jè 1ízj•xÚþÔŸÁ`Œw«õ&¬ºTýñ}èî6O¡?+ßÎGM¤ú\k`«Íeû Y,FÝu ¤/Ø4ûwI£¶GÍyp¤ã,:ú3Ö`/@ i;§fæï¬¿³xŒY´‹·a‘ðã’°Ë|>ïf0„‘|Ô2+”FMü¹Õ¨5£®(¨ø¶Ò¨A¿ÙGÝbê‹Ï`0FÀr£uV=V£>ø~ízû`=¹öû*­üTõ9µYÛ!š¶?à²Fý̾†µž9`~öðe°%ÿƒöæÙ9·Þ?P¥Ò ÷zI³Î¾Ã`<#ú¨ó®zž~c±¦Ã0˜}Ôì£f0Þ ³¸)ïò85n›*užçm‚ÕäÚï«´ò#}ÔÒb§6½”v[£¾è£¶%îþVy¢Qw=ÒRi²s£Í󼿒º·~³ÕÖ¨—Ë%ÉzöH›™c0kÔŒ¿Ä¸>j[£V®ƒ4t:+0Jº'õ§‹ÔS_|ƒ1¨¼ËC«%æû­Ò¨oy“k¿¯ÒJöQ[ mEä©¡5Vä0 âXù¨…6%Þl6àÏØjf06ì"σ èÏoú¶ÚŒ”Š; ëõzpÍ?ú¨ihÍâó9¾QÎ=3reYJØGÍøcŒè£ÆOaù¨gs¥Qïó \šZ sý€}Ôì£f0Þ¸á}™ò.óQçÛXiÔ¡—DÞäÚï«´òí|ÔWbPmÆùú¾æ ÂŒG<U®õz>Ÿ#bÔ]<ôø|ìú»jÔR`“ëÃÁç±0;%ˆ “ëcpýV®µ‘Ê19Žâ8‹­îз$\P¬ôpÎõÁøcŒâ£®µM–éâ¨Q/ð‹¿WtºÔ¤ºTŒÚ *öQ·˜úâ3Œ{¡Ê»Äº¼Ë#5ê" Uy—ÈK“prí÷UZù©>êÛpC9•_¡ó~b&õ(¨85ãÏ1šºêú¨I£Þe%¸4ý€RƒQ³š}Ô ÆÛüë«-ïò8u•¸{–‘·IÅäÚï«´òS}Ô·Á~ûotàw]÷ò—S3qpÍDÆßc5¾îŸÕ¨ó#£ñ^{~Å>êS_|ƒq/â\™¨ÃÝc5ê:öžWcù˜^~þV~ªú6ê–Nš'6>ê–QïˆQ÷4j[øÀþÔŸÁ`Ü‹p¯4ê8{¬Z •:/Þ3h¿¯ÒJK£îÜ{5êò]|Ô cØuy‡:'º²5jëãÔG}¢QŸhåö§¾ø ã^¨ò.±ÜÕ¨eäžD1þûþÔŸÁ`Ü‹åFiÔûò±>jzµëî"÷´ßWi¥fÔƒ÷ÞŽFmâ¬Q3Œ›Aµ}ϹÅG­5ꢯQ«*äµ.§hòQ8Ì©úYã¿ïO}ñ ƽ˜'My—ÇjÔ¾{ð¼-kÔ¿Ö¨‡ï½xøÈF£.í8kÔ ãfŒâ£Æ·}K£VÓ.MÍDå)k]ÿe fâä*ñ´ý©/>ƒÁ¸ß±Ò¨§N£¯ÄˆMPÇþ†}Ô죾v>g]9üšù|]ÍçëyÞ|6[ô6Ù0)8²,sœÅz½¾!+ˆyí‘Sp0Þ#ú¨R]7õÂQuѼ±¨Z ó욉6ÅøïûS_|ƒqj©ꦼËÃ4ê¢,³ÈÍ׫DøÏ ý¾J+?ÕG=X3QêÒ-TúDêOÆi+°€ââ‰~.{3âô´''2|CV³Ð »"ƒñNÅG]ku¡ét£Q;Î|á £…k’¯Á¨^p¢QO®OÛŸøÚ3ŒûWŠQ;ɳ|à'ÛïŠp¯–i>ƒöû*­üTµ©~ا¾V]ï¦þøe˜R†Zê>*Þ´•²LcNÏuÄ z°Z­¨º"·÷•\Êñ¾ÏG}Ô¨Kͱu«¾Ü‚QWì£n1õÅg0wa¯õzóX:Ûm*o•¯Wi=ƒöû*­üTõ93xžGýÅbqÎæaã‰Û³™ùÁ“D}ÎhMq–À÷÷ºÔ8øÃÒqú+Á„f Æ;a$µíô¨ÍlùªŽ›ò E¿¿{Ÿ„K-¦ f0^ãù¨ãGyÔ¨—’4êºi[š}ÔMê‹Ï`0îLŒöÕ¨óTì“pº“«¾¯ÕJöQŸ§ÍWjÔö }z<¸DÂ÷}GeП[É•|žÁx-Œã£>ét£Q;§µ&Õ}úN¥·(Š ¾¾¾úc°BÜ1°iµZ…:Á¯S ’çùr¹D-úöüI’ànðýýv‹Ó,ËÎîSèÝ1·ôõzrôA}öQ3ï`§uœ=ÖG'aíy`Ô“«¾¯ÕJöQŸ…¤7MÓ§2o#Ú³™ùíV[£ÆãP¢ë¶Znædšñ®ËGݾ–¨Hµl}ÔRkÔæGkÔш>jpW×u‰wÆ€Ù !p^QÒ«ÊzØÉß÷ÂP½)†úöü˜–n¶8GÜ ÚÝOÖàyf÷ýÁÈåõOzå ƽð4£ÞÎõµJFíM®ú¾V+ÙG}^ Æs ´Ory1ׇòQ·ÇôQÓëhéF©I8ùjð6#W«•1:²šñ®×GMÌYZ>ê²¾¤QâI&Ÿ§`©ulÜŽ”؃xž«ohiLgbÔ¸í”ú(ãêݳJiÝÝChBÉ25!û¨Œ·Ær£õ®¼Ey¾¾_ _iÔkÔ·hÔï䣾ƒÊpxZQ¦ŽÅâl>j“ëCù¨M®×Êõþ0Ÿ«D`xz’v„Ïvé8ëõZr®Æûb,ucùòQ«Ÿƒ­Qì£î«ÇþI£Fûýý¸Îßó,Ä„Ñ7ÿäGc:ó`fÄqªè›Ýñ]_±;Ç¥¾­–*çþÔŸÁ`Ü…Eª &ÖÕ¨+¡4ê¶¼ËôÚï«´òS}ÔãÂ䣜šñÆÉG­i³­Q;'uù5õÏù¨Éíyž­NyŽ?g|}nH/‚*^öl©£(RƒÛ89·!~BÈ{öQ3ï ÐiüTõc}ÔU¤4ê4ò&W}_«•Ÿê£ºfb8Öl\3‘ñÆÑG]þ¹ú58Ïsú‡'-&E”»1iä9úƒó·òõ1ÞpøÖ4‚€1]mäòúM‚ýØG8‘—ˆt r0j'‘ˆIO¦>ZaE„ŽÜ<¦Ž”F _Dþi*'RŸ‚V„Çøî£f0“`,µ¡Ó]µÅ´ÿÒGíºn–eEQ„aAÕ¾lHÒ¨=×%gˆy‘ÐÌ£ïve©éårYRd¿£±"Õ»U:‚~ÙDö§û¨Œ7Fu_B.’ªÓM?Pu"X£¾E£þ@5ƒÁ˜ãú¨ËÚöQ·u»éAù¨;>jR¤ã8žéÔöÄu7 ñ¨4j¥ ècŒJ§2ì¨ÇL‰õhðiD¹ïò6{žI¾×°šÁxW¨ä±\¦tP7­fÔiìßé+þ´V²šÁ`ü!ÆôQ·?²ñQ«„“Ši7i@Z£G÷QÛ}PY­9çãúÓ^zƒqv¥Ò¨×¸fÙC}ÔµïQÇŸ\õ}­V²šÁ`ü!ÆõQ“F-µF=_8¥Q7t­Ò¨ý°*º2møü‡õ§½ô ã¨ä±ôÃ}úÕ¨}¿vÝ4 'W}_«Å5:wï­«ÖGݼœ^±FÍ`0îÄú5hseù¨gsÒ¨kªº.+­Qû'õä*ñ´ýi/=ƒÁ¸á¦ø2 wµçÕUý 5nj~ßK’prÕ÷µZù©>j“/Úgê®×s…Yøvêizh6›aÛ¹¬Ô—q9ëõå‘&âºëþôµySµ´¼ËJ”½/å¾¾:ŸI$uõÇY`ßþgø#ÌÑ9ŸÉÇBlFðQ«­F R-[ZJiè4Z ÃàÊäú ÿ4Šñß÷'½ò ã.xq¦JéÁó³Mò :Ï2¥Qû^šŠÉUß×j¥ÉõÑ»÷Z>êÒŽ¿‡FÝ'®¾çá±M4o³Ù˜’…•®Ý€ˆ¼X9ñ2.TfüqäåHg+(‡ëº£¬¤“sÛT]¿þkBÿèœsûcqÔ¨K[£¾ÑG]Y>êF£.k%S딌º¯Ql;í¥g0÷Àó/!S?©]/Ãù¨³ýViÔbÔ“«¾¯ÕJËGÝi}ÔF½y}ú'6h¸Ÿ)#Þžò«Õ #—Ë¥Mæ£0í4<\jò©Ê#ê ö¢ >^’¾Á`{A£¦57Š´®§68Þ¬Ü,R \ØG7…¥cS’ö5óÐVSÒ³êBÝÚWr]ÈO…í£¶5ê_ù¨ÏhÔ‹¹>ʺÔm£Q³ºíO{é Æ=X‰â[Èm(@ÊÈF½ß* Œ,qrÕ÷µZù©>êËújQëÕŠúàÀçlA=ÆÓ¤š‚B0pðR|V¦:9ˆ%âR«ßŽÎñ%5¥$Êe“¡B^T¤­çg5j¬Ç¨ëfyh—Kç¾XI’¨_´fÍ60'±âÁõØ»› |ß}»tŽŽ ÍRŸƒ±|Ô¤NWº#m5kÔ¬Q3ïGT_BîET»nórâ|ÔÙ&Vuè=ƒêûZ­¼M£~b5‘ês­Á0Îz½^÷ äezé8 ¼ã|¦Žáê8¢¡Ù¶l+ºöš‡}ÔÿþÍç³sþä> öÂz:YÚéQwö2ë1»£Ü}ðèçŽÅxoº¯QÿÚGݾ–Xéßwã£.ºõQ{GuÕàYã¿ïO{é Æ=˜‹Ã·»88¨7¥QgitðüJ• Ÿ^õ}­V²ú8kOªMä‚FOÃ÷ýåÒ™iPp*Ã`¤TBï= ?w"?jÔaš/²ñ*ûàêà×÷µ¿_\¿þËsâèøp.ýܱï±|Ô•6Q5jËG­Hµz9‘5jÖ¨Œ÷ÁL¨|ÔiÔž«Œ:åþèJuž„ A`ÔÏ ú¾V+ÙGmA™=Öë<Ïí haš¦ƒ“€K§iBÊ¥Q;×kÔ}=ùGõ¹Ùžçšçær¹L’„sy_ó­á¢FÝuz›9íV[£>9úºÎõgb4õ9º>”:×ZlaµÝŸöÒ3Œ» ¼*ô7©¨U¥ÌóGhÔ…ÖÀKÖ¨oÓ¨ÏÜ{?ÍGÇ·ëºý‡NY ~xžKMÑ Ú\‹Å”€ÒʹÕöQ›tƒ*.ö"u‘çƒ#;³æÕjehsg¼~IpM<4˜¾aI—÷5o_¢=ë£n?ÛAu÷Î>êÏÄ(>ꃭQ£^8s­Që7R­5ê|ÔÛN|í Æ­È‹2Øl…®cX…ŠQïÓø!ù¨ã v½R°FÍ>êkÑñ*#§Ù0yžS.‹Åi>j<âÁÓ›L<Êõaf³û¾ïS6 bìò²FÝ*½¥®}LÙ¤ûã‹¢ z˜s®'WË[,èËÂà¾V®õå\ƒë1¹>\+ׇ:ú|èèŽCGç\Ÿ‰q|ԲѨ+K£^hº:V~945û¨Ûþ´—žÁ`ÜŒÍ~ÿ-dÅ oµðÁ¨³$z„F] ÿày…`ú&ú#}ÔŒßÂä£%ç£þTŒâ£>õQ£vãú0¤º>S3ñcÛ‰¯=ƒÁ¸ñfFí‡b·MJ¡4ê\ðQW ë*ß5kÔì£f< ÚÜ2`¹ \3ñc1ŽZiÑCª%iÔ:aΑikŒºbuÛŸöÒ3Œ›§[0ê0 oE¬4êJ3êñ3~DJ£ÎX£¾M£>sï}o5ƒÁ˜£ù¨5s®muëú0¤ü bš5jãÕÅšQ‡Ñn›d±Ò¨«Ð„ZeºvÝ=û¨ÙGÍ`0žcù¨F]5jUe‰86µJ£Š}ÔmÚKÏ`0nF”ìÀ¨£(Rä-ñu¡pÿ!u 4ê-kÔ·iÔì£f0…±|Ô6s––º²6±FÍ5ƒñ„z31 ÃÝ6I‰Qãç>jEÔ]wûÏ ú¾V+ÙGÍ`0þ#ú¨ëAZ6ObÚ`Ôû¨Ûþ„×Á`Ü?ÎÁ¨c‚¼¥i:}ð}ÅÓFר}E×7±ÿ ªïkµ’}Ô ã1ŽÚrwÔ§>êÚÖ¨%kÔ¬Q3ïW(F-¢`·M6›˜Š¼Ty1²º–ª£ë¦qð ªïkµòS}ÔvÝmÎt½^Ïf³ù|îë¯~fÎÚó<Úd磾犞_3ÒD\wÝsÌ¡=ûGË»¬DÙûR^è+A…l¤®Ìâ8 ýY]{Rý£s®Åx>ê#s–§>êÚbÚ죶ûS^xƒqÖq©5j"ºiz ¾Ù&W£.«Z«ß^šDÏ ú¾V+?ÕGÝ'®`Ñxn4„T$Eê§°©Õ‚Mx®ûmÍÄëѯ„xýÈË‘ÎVP³ò;WÒÉGm*’_ÿ5¡ô’óQ*FôQÛÌù:µ­”ØŸøÚ3Œ[ሊ5)¢µðj·-ò2ž:ÏseÏö½M*žAõ}­V5êî½wH£.z“OýËu/f?±Á~…î>ð”_­V¹\.m2 ÕL¤ºŠb/ âã%騶—j&þ³j;¶UûãÍÊÍòÇɲì¾VÍD÷rÍDSe’æ¡­¦f¢gÕL8ºµ¯äš‰Ÿ ËG]Úõï}Ôg4jy´O|ÔÄàËòcÛ ¯;ƒÁ¸ qPŒº1c¤•ðteÃ`\:Ûït0êøTß×j¥ñQ÷Úõ[kÔ6’$1B48ð9›GDñ´©¦  ¼Ÿ•©Ü b‰8:àÕŽ®C!5¥$Êe“¡B^T¤­òßg5j¬,½³<´Ë¥sa_¬§L'nÖls+\½»9‘@kþúèÛ¥3ptLh–ÊøœÑ¨oñQŸhÔ§>êúÄG]œú¨oWz‹¢ƒàëë«?õøMôUñ<Ïq[@-úÓ‹çÇßvÿþþF‹3EäëêèyŽ»Æà }DÔ„:‚xžç?®âkÏ`0nÅ¿–Q“"ZÄJ£.£`\õ~›*{v ’Q?ƒêûZ­Ô®Á{¯¥QvüÉ}ÔDªÏµç<À3-¡:ÎÂè9WÚ$ ½Ä¾}Þq>SÇpu|ΆfÛ °­èÚköQÿû7ŸÏÎù“û4¸³Ú ëA¿¿»vzÔ½ÌzÌîhw<ú¹c1Þb“•úÔõqªQKËG]žú¨ïÐxÁ]]׿íoE«$nŒ_xD|Ï×MD@Âѯôëa¨Hx:bfÀî8MlJÓ&ˆôâ™ ÃPí~Œ&Â5ƒñ–øŽ%ušF¤ˆf‰Ò¨U‘—Q5êý&VuèO®÷¾b+/hÔõ‡jÔ …¶ù‚FOÃ÷ýåÒ™iPp*Ã`¤DàõÞús'ò£F§ªQ’eãUöÁÕÁ‡/ïk¿¸~ý—çÄÑñá\8ú¹c1ÞöQ×§ÂË#|Ô­F=8¦ÀÝ¿óèã×;o•dű«ªéίuº˜Ï­£Ǩݳ }ü™£oEJ´s½×åõO|í ÆMÀ­ìKÈ™8h3†VD•‘£)ò2žzŸDº£7¹ÞûŠ­dõ¹-Ù-Ä3np ¸tš&¤üXµs½FÝדôQŸ›ày®yn.—Ë$Ih1—÷5ß.jÔ]§·™sÑnµ5ê“£©ë¬Q&FóQ[o ÊF£ÖŒºë£ËSuié·õI=C ¬ýïïosó¤~'ÒŸû"ŽsE_ÿ#Õ?üïjµÊ4‘VÇ-éC+i º-Iô°5ísëŸðº3Œ›;æw,çQeÑ4Q• U^ŽÖá6J›£Ž¼ÉõÞWl¥Öï½Uë£.›{ø›kÔ*ׇÞ´Pù¨[‹oY ~xÆIMÑ Žk% QZ9ÃÀmµIg1¨âb/òQy>8²1kÆÖÐæÎxý’àšx,iVècI—÷5o_¢=ë£n?ÛAu÷Î>êÏÄÈ>êÖÝ¡}Ôê Z=z45õ}ÔÔ/Š×øZMt·lAÔ·éÎ¥Òî&Ž qs HKÈËsýÇõO{é ÆmØî÷_B:QiÑ$ j_?ê²ÑGÇu±F}³F=|ï}Qõ•èx•e›z®“N‡a`«ÇyžS.‹Åi>j<â1ØÑ/"a“‰‡C¹>Ìlv4ž²ac——5êVéÅUX.—”Mº?`²¬`ιž\-oÑ8Ã÷µr}¬/çú\ÉõáZ¹>ÔÑçCGw::çúøLŒç£>’jIuÏõ€~WC>ê{”êËj0þ‰Üâ&çJ[&KFE­6#Þš4ºóà~Û™¿´[ÓHuÉFòÃúMÚ¢ØG8‘'ˆ4ýŽå**DˆÈW­êHiÔ[¡d<'‘ˆIj >ZaE®S£ÊG}<–*+“¶úñ­ :>êþ½·ï£.ßH£fü&õ((9õ§ÂhÔå=>ê¾F­ûJ£n«“µß¸>í£Ö¯îU20еzWºÒz‘ð$âºöœí‹J‘Æw^±5jL"DD;èS$ŠN"ì£f0ÞqºýÒr[­„bÔy裗®UR>Ö¨oÖ¨‡ï½ïí£füÚÜ2`¹ \3ñc1–º÷fâÒ¼™hH5þ×õî£&÷—I§3ãe¤$ƒ ëÜAŽQ’—Ë&Ù]¡Õf[=¦ä{Ø\ÓvDWÚjÒñµ‡È#ì£f0Þ"Ù|ÇÒ‹ö¶k·J|­'‡#ú¨+¡4ê"f5û¨Y£f0žãú¨–zvÔ¨ÔâK›fÔ#û¨í>¨,¾Ž;çãú_{ƒq¢DiÔØÛŠhžz”;zDu)–ž±F}»F=|ï}o5ƒÁ˜£ø¨¥¥QÓ?uhšÞL<œjÔA5¶úuû^wƒq3Âx÷ËPlmEt¿ñe¨rG¨QךQïcÎG}£F}îÞË>jƒ1:ÆòQÛO©}ÔF£¦ŸV£ʱ}Ô¯ÛŸøÚ3Œ›Ä*×G$6¶"š¦ž t=ðµ±*'ŠQïX£¾]£¾÷²šÁ`ŒŽñ}ÔzZ¥QÏmúÐhÔ^èåx>ê×íOxÝ ÆÍðEö-¤©­ˆ&i Cïàùu^ŒV9Q3ê-kÔì£fšÁxzü‰ZžjÔôQ¿VâkÏ`0n‚'ò/!cÑÕEe¬_NÜnÇòQ×¾½ÓØŸ\ï}ÅV~ªÚ®»MÀ™ºëõ\aàkšU÷°ÔYªf:Uµúz\(zÞiW'¤ibf dÎ*^óãA¯?»ëÁÉ@ÞôQ·ù¨5©®F]±ºíOxÝ ÆÍX‹â[ÈDÄ]TªÊ‰~žˆÑ4j_7ß$áäzï+¶òS}Ô5=mbq›ÍÆ”óÃ)›Z-ØŠçºßÖL¼ýJˆFöç_­V×Ï0"®ÌAýˆµá¸7|ÎŒçÇã|Ôƒù¨ÙGm÷'¾ö ã&,Eõ%d‹®FRúèpu Þçyµï¥I8¹ÞûŠ­üTõì'Yµ_¡»<å‰ëRÂX†j&R]E±ññ’ôíº®Ñc12Š¢¢(ì£Ø…¼ºŽ#â¸ø_´¦äb–e:8³ƒæ@žw<=r·ÛöÏn¸Nâ¿išâ,p‚tÊvõÉsÇÂïÒzµÂ.Ø×¬ÿ8Ò:}̃óž’‹*¾)FôQ7Þ=íù|Ôì£>ö'¼î ãf,Dý­uÔÑEëPEÃQ4ê<Ï•Fí{›4ž\ï}ÅV~ªú²Q„ ú`çlA=ÆÓ¼”‚ª hƒ"â³2Œ¼q©ÕoÇq(ˆ­D¹±l2WÐÈò´ž «j«U†á›•caäAkÆ#ˆ?ŠN“Ó49Ú‘óþH »Æb=‚´zs"¶F=x,ÌOô>‰c³~3Ò>}úêA}Ýoÿ¥€ñ6xœz~Ìõ!KîãòQ¿VâkÏ`0nÂ\´FvtÑrÔ`”@ïnu¶ß):PŒzr½÷[ùv>j"ÕçZƒ¾Úg½^¯ûdò2 Qtœnx›ÓHѰSÚJ,ZjnOæ[%n䀺wt'û~2"°£söª‘z÷ žðù|ÒY³ý¹õIþ¹cÙÁ™uú&¸hGb~£Ïc+ÖpnyŒÅ(>êƒbÎZ޶j&‚T£›•uVÔÔVÎG}ÒŸøÚ3Œ›ðOH¥Q+3Ɖ.ší‚ƒâÀþ(õ~›ªÌ!¡÷ zï+¶ò‚ºÑ¨w*hÅß[£®•È3Æ yQ£®´×w¹tfdàvÐùo¦=zïöV#ÛAPh%¹O_±*ŒÁÑW«©Í´$!ÈözàñK…Zkd¤G®Íî?®yð«AgdÿXö{¯þÈÎu™Ä=Îx(FñQWõ!¯d^ Õªi•Z}G“›¬ÚfÕ&+ÑU}’ëÃÖ šzû¬þÔŸÁ`Ü‚oŨ›4êè¢Û}X»®²j÷û¨÷›XiÔ¡÷ zï+¶ÒhÔ½{ï€F­ãoì£Vf½gNÓtp°Ù4MHù±4jçzºŸÎÂl].—´žN¼¯®ƒlw0ØßÚé ÐõÑÝÝÝ:ÓïhÔý©Ì™¢5ëiÏ‘5ê÷ƒØ´>êæžs‹º¬»¼Þ‡]qØ—J]!5 Û\Ñiõƒ[Wuh}Ôê;2û¨§¾ø ã×ÀÃŒzF½éæúH÷å»Sž¶û5ê$:x~±F}»F}îÞ[}˜oãµ°Q¸Ù€ñ;‹NÐæ X,æød(ÅœQSmµñ'ê½ÆH\äy$&97oöU\ìBç›e™ã,Lü*ø^`(+D_ 4ÖîÁ‘6”ºýƒÁø5Š¢£žGU_M³¤ÖeU‘—»}ÔybªJ°F}‡F}æÞ‹§O£QãrZq­Q?¯úJØ*H5m"§™+ | Û`•‹Ó|ÔxÄÏçsGçôÀ&‡r}˜Ùì¾ïû˜Öfζ©Ãðd9¤Óë”ëÃ|•ïh3x8Æ ŽëØ?P»ûlé8ý Øò\®uÓÇo¦"9}ðX*×ÇZåúÀbÖoF.O¾8çç\o ËGÝ|U¿ÁGMõîD£vH£&ušHµÒ¨9µÕŸúâ3Œ_c—©‚‰KQ 袻ô T½z·¿_£.båÊ.kÔ·kÔçî½ïí£f\ƒ+óQ_íú¸Ê]q>ê7Å(>êŽF-Û|Ô‡ƒL÷ŠQkº(ÊZW!g5û¨ŒWE²Ý‚Q¯¢bHM+¡4ê<Žï÷Q—"À³`š}ÔŒ@ZÂûç1•8ޝ¬ùÈ5ßöQkº RMuÉ>ê¶?õÅg0¿F¼QŒÚù .Z%Tä%º_£.#•Ý:gúúܽ·zk5ã/6´Z)‡Éjµ²‹×0>õQ×ä£Öt:55û¨ÛþÔŸÁ`ü"QŒÚ‹öƒºh‘(º ýû}ÔUä㎙ŬQ³šÁ`¼þÈGÝhÔu«Q}Ô¥¥|Z;õÅg0¿F”ìÀ¨±ÔEs]ˆ¼üû5ê:Tõž5ê»5ê~k|ÔeC§›8kÔ ãf<ÐG½hs}ìÛ\ì£>íO}ñ ƯÆŠQ‡ÑvPÝ¥:×~îöQƒQ×®»‹ýgÐ{_±•ì£f0ˆ¿ÉG­Š¼P>jËGÍõÔŸÁ`üA¼£b;¨‹nR¿ö=U䥪ïÕ¨1çmbÿôÞWlåyºï£.Y£f0÷¡§Qç£Ö5µ)ò¢óQ‡ì£6ý©/>ƒÁø5|‘iFê¢I¢ê†«"/Ey—º–JèvÝM<ƒÞûŠ­üTu¿ò Ît½^Ïf³ù|îãëžU÷gíym²óQ_sEÏGö³C§ibf¸2WÆ( â5?ôú³»œëã]1’Zö}ÔøÁ¯Ì®¨·Ø”×۬¦­™È>jÖ¨Œ—„`ÔIœ ê¢é&ª#ïàùõnF]VŨ}/MÂgÐ{_±•Ÿê£î“@°h<·B×u)ŽS6µZ° Ïõò$_™„™Föç_­V×Ï0"®ÌGýˆµUœúM1Žº:ì|Ô `Ôˆ©Æ05˜}Ômê‹Ï`0~•ÐŒZÄçÔÑ:V9:Ê$¹ÇGe91êM*žAï}ÅV~ªzö“¬jXb†qŽÁSž¸®]µˆ†j&R]E±ññ’ô önôXŒŒ¢ÈN1‡ñvñn£®ãˆ¦f¢©9˜eY[3ñ4ò¼ã쑻ݶvÃ5ÿýKÓg¤SîT™<–ª™¸R5±¯Yÿq¤uú˜ç‹}%×L|SŒæ£.ê}yâ£VµT›´k ¨j©õ_ø¨ü!뿃øƒÅÿ~‘"Ïñçöýý}{†$Žñ‚atß@¤³;"¹Þ}´è›MH‘Ëëœúâ3Œ_c%Tò4çÔÑ*Qþç" ïѨ³ÝN1êЛ\é}ÝV~ªú²QA9“Z¸s6 ˆSEo âa ŠˆÏÊ0RðFÄÑÁ³Òq b+Qn,›Ì4Ÿ3f6GÁcS†oVŽ…‘?­ ¬ &§irµ#çý‘6@wÿÄ>z,iõæDlzðX˜Ÿè=hƒY¿iŸ>}õ >Žî{Þà'Ïx]<ÒGÝ0jCª1Ì ¢¿ñQã—œ¾ ã÷_ W4øt þ¦ðW1hÑ·çÁŸ9NÜ|è&@$܃]pº? ?a5ƒñfXhFÄá9u´L½Úõª0¸ÇG½Kc0ê*ô&Wz_·•oç£&R}®5èû¨Û¸R[gaôœ+- †(bß>· ¼M‰i$>gÃNi+±htðð%no«ÄíP÷Žîbß.Ô[ì,ÍH½ûOø|>é¬ÙþÜú$ÿܱìàÌ:}\´#1¿Ñç±k8·<Æ‹bÄ|Ôû®zi5‘êê ½€4ê‡û¨éˬi)ñ1‚ßç<Ï´¶œ¡?8ΛH£îlÅ_\®•m´èF.¯sê‹Ï`0~¹8(: Ï©£Yª4j•©ã:J£ŽüÉ•Þ×må{ù¨¯ÇD5㣾 Q“×w¹tfdàvÐù¼ÞûŸ½ÕȶAZIîÓW¬ cpôÕjEj3-I²½^#xT˜é(ÔZ##=rmvÿq̓_ :#ûDzØ{õGv®Ë$îqÆCñÈ|ԎŨզ²–J£þ5°çyßßß‹ùìqr€ Ž¿Ð,ÛÓ˜²E_Á.[£v/õîßøšœeYy¢ZÔÇVåÊÓûê>û¨Œwè41ê³êèÆ«=WñaåÕ½ÕG‡ªöbÄõ}5û¨´\œ{p Ølš&¤üXµs½FÝOga¶*§eQ˜$ƒ*1dÃh¼ƒÁþÖÎHgh€Ö¨îîæèÖ‰˜~G£îOeέYÿàH{~|Œ¬Q¿ÆÌGÝõQ+:«:5õ_ø¨AhÉ>¿Û&Ž¿b|/ÆmáHzÛñýy0˜¾#yN»“jÝèDÈIǶ):kÔ ÆÛá;–ÿÄ!M£sêhšzJ W–7kÔE(×GÌ5û¨á\úÐ`}ÊGÝ:xñDÄó‘lÀàxèG‹µúw[J1g¸í£6þäA½×‰é¡ÙÙŠIð5oöU\ìB βÌq&Hæç<Ï eÅèK‚ÆÚ=8Ò†òQ·×â²F½Z­ŒüuîXt¦ö+–ƒ#íùÙGý–ø5ý4uqªQ·¤tÜ–ÌÏD¡ús¢ˆ6i(µ9Ï2eÒš»Ÿ¨ÍVd>Ÿá´dÁ$Lj^Àåuš—¬c áGž6"âäKȹ¨7©Ømù" ¢Pµ¤‹¢'~)F]í÷àÆ‘P{é6&šú'Eº?¦ŒT5ó\xfæÁc¡OqÓ#º×âéÓõQëöM4j+C©¦”z®“N‡a`«Ç |®ëâáµ8ÍGG<;úÝ|l2ñp(ׇ™ÍîƒÆcZ›9Û¦ÓåJL¯CR®ó5¼£Íàá˜÷ q.ýµ»Ï–ŽÓÏ€-Ïåú8ñQ7}üV`*’Ó¥r}¬U®| fýfäòä‹Ãq~Îõñ–8ú¨Û¯ê7ç£VÖŽžš4jÝ^ðQÛ*Á8}ü2ã×[±V-2—úeC,¼´4j|K"Âèðø"a3¼ßÓK‹¤H÷"j Éàí‹•çu"?¬sê‹Ï`0~‡¼¨¾c¹ˆªÖZ0 Žn6±J ‡;@º¹Y£®#¥Qç1çú¸_£¸÷6>êñQ7ñ÷Ш×àÊ|Ô×C»>®²FWœúMÑjÔÍý¤Õ¨¯÷Q7õ¹|ÔSiÔøÞ½Z­(ýV[iA˜âc¤$7Éî'×Ú²­c069ún™5ýo…8ú1/$v"—×9õÅg0¿ÃvŸ} ¹ŒJRAϵU¢4j•@ïVu(N¾‹ÙG}{+/hÔÆGýŽ5ãhCKxÿ<Æ ’peÍG®™ø®8§Q‘úÄGÝjÔÚG½3>êTÜ÷îO}ñ Æïlvß±\Gùeu´ÚhZ'л1ãG ÊÄìöQå£>¹÷žñQ—¬Q3~ °¡ÕJ9LV«•]¼†ñ8õQ+^ý;uù¤>ê—h§¾ø ãwéöKH/Ê.«£YêQÅÃ[ÔiüÔRíîyiìO®ô¾n+?ÕGÍ`0&Á>jÏòQ—‡¬Ш©ýKõKô§¾ø ãwˆ’íw,ýhÿƒFªè|_™ ~¯Qçy¥öõ½Mr6£·WkÔ÷^öQ3ŒÑñ®>ê—h§¾ø ãwãÝ—¡Ø^VGÓÔ«@¯(oðQgÛ-• §Œ"Ï ÷¾b+ÙGÍ`0þãú¨÷ì£f5ƒñ¾ðÅþ;–‘Ø\VGÓMPGÞÁóë}v‹F ì[Ë»L¯÷¾b+ÙGÍ`0þ죞°úâ3ŒßÁ‹ó/!c‘^VG7¡è¹^™lnðQ—q€}+á=ƒÒûº­|qu©S¼Î{™¢í\ÓsµÉ7›úÕMdœoÇâ/xˆ;ç*'2Œë1r>jöQÿ¦?õÅg0¿ÃZßB&"þQ#­ï@ ô~¯Q"½¯J7½Òûº­|>õõP‹i«¨€ ã‰k2w’ãyêº.õûÕMÌ|½^ÛûbNDj Ì`*¶0ŒÛ0¡º²4êÒR >§?õÅg0¿ÃJ”_B¦±øQ#-S¥QS½® ½ÝÒM]ÕƒJuQ¤.“%¬Q£Q÷ï½}5áy4ê0 L±Îú´Ùðd»*_9lï»Z­ò¼9Yt®Ì¥Ì`0ÎÁhÔÍ=‡}ÔØŸúâ3žüOŒ…¨¿£Ž~ÔHóM“@¯£?Ë4UþjÏ;DÑ F½;™‡ulc÷”Þ×måóù¨¯Çb±0^Ž:5ÍžçQÿ‚FÝßw>ŸÛuFð¿w¬—Á`Là£.N}Ô}õàsúS_|Æ“OùõZ®V2I¦^ c3qørÿgïjÁ”e¢è7­F#‘j4©D"‘J$©F"‘J$©D"‘÷Ì\GþV]]]½÷á›ï¾Ãü\fPg3î÷©\@O€êª>±ÐE™ÛArp‹ƒØd¼NÒ1ƒ]:véì kÎ+0½7m%¢žüîU:j<ôüÇqÔk)}VúgÊ$¤žjåg÷³é¨KtԪÜ#Û%Æ:jÖQ³½Š9Ž€Ó¦)RÞ{ë%íËo%Gí|Ë‘#«qmµ€^ÇBû¾uÈŠÔ«iSEg¨²ÎÒª±ìøè%öþxü^­ÍéõÄwoõò:êË9ê³ScD=›·ÌQ³±ÝÕXGýDÿÙ“ÏöJ†Güá @u PíºÏˆmh½€Ó+¯ŽÂï9ê(²kO?ª$ëXè¢;wêÜ®‘Ú’ÁÎ ]Gúa}°jß ½ÃÓ9Þ¿ž¶YGmÛöÜúci‡2Ó4õ°á#g®î~¿Ë²Œ|ÖQ³±ýÜ~GG-SÖQ³ŽšmÞŽG¤}_@kbªgª7|ÌÝ´µâ6~»¼¬ jïäÆ+ßqÔ´€že•QܱÐAØXVœZUì±Õ„âláG'Žºi+ÇF™*´¢ˆwKüh5âÙn·xÒ¶ß­õ¡-ßAt à4üD>m•ñZll³{é¨Óyµúq"ë¨þ³'ÿ/Yó}‘?nx\Eãñ‡Ï†çµ;³ýƒ+Äâ3~ˆÛRt@ñm,ÉÄÒy¦WÄ—q¤Æ3{ž`¡«ª¶Äêi`Ç–»fHá2ñe(átžU‚µ¶YxxŽ÷¯§í+é¨o°<Ït·ÛázÔ µpQ»Ýn½^!o(3¨E¯GÍÆv/»§Žº¸:›å¨YGÍ:ê[ wÎ&͈ÄtGCkÛ¨]mxGè #àÔn'<4%´èºÿýþ_1 æ3e'm”Ÿfí¶‘_I» Ä”ž¾ÀÇ%Œ3\ÑÁÏ/äH«Hbf¹€^C‹ûvÚaèDS$bŸñƲ˸ÛW1 ŽTæèñ*wä¨'¾{Ÿ¥£æ=ÙØÞØXGýDÿÙ“ÿ+ê6¯.åœzͨ]ù÷ æ3Pí.À¶»š¡e/»½© ¨ílÃ[¥xΚfåúnÒ=zISYŽÕ7ý>c릅ٯrûÒÿä ”òµ ¨Z#9€Öù•xðÒ’\÷Ê?1ÞëPÌ~õ<ÒÛ ñ¾à¥r¤y$uÔ¶U×Mm t]Çv~´"! ¾YË5« ÇW«|Ôg’—`zÿnÚ.騇{&’½GÍÆÆö·ì÷Ö£.¦tÔ]§§¯»òŸ=ùKöØ’ôló—ÄŸ´~Ö–ó=cPy/>Rªë݃æ9ěЍ§×Onió(ªw×%+¹~ô‚ ;¸» ¦‘¡Ô·íÌ ÖÐ¥õíl$¾½Ápi˜uÏ3ãcÊ ù«½ ¥Õ~HŒ®wGŠ·Ñu Ù¨F ¥ŒdÛß9èâ—ß”¹a*îFÀáË8Òôh5žU'‡Qc FºÎ(ØÑÙ(pJ)üGYyÝHÔG‡§³»ï‘¶:G}þÝÛë¨Ï9êòá:jæ¨ÙØÞØÎ8êò>:êv¼µ8Õ°Žzà?bB ‚Э­äͨ‡â¯íw 3”2À¡u ÖWÏ=" t’Ã$Eî.XŽH<4B¹é¸Ê´’üg xÔF¶/Iƒ×£’ÀÛ¤d@aÀïB+˜7=+DMuë&ÝuQÎñØ]—}lü´1¦kÇÇÇêºK‚%,ŠŠFØE~U0W5Sô¸Qª`X¨&h° ýP‘Zp’qoå’‘–³£¯ÕŒ« ¤M#|œ|Ï`~q6*D<8ð‰¸–$Ÿ4˪À.eJ£ƒØýð 1óáP'NÛ:ÿœFûÚg37J½n•£Ï«|Ü£žûîý:j66¶¿e#uy¥Žºœ×Q DupZqÔ¤£>_ë£ïúÓüGL(ñxÛè û]n ‰³íŽ 3ÜÔ^gÀHÄšÞ{ „ߎO¬õàE=Ì5Γ`~ ®›W“ 9hýôÔ²¢ø%2ä¿‘Žw¥Êdµˆ–FÉ”C­ÚIû\‡âTrA„iÞtp:n꺩*±²W”5[¿^y ½ƒxÀ„õ÷M!ª/Y!=ôÊ ÌK^APlÛ¿O–cðñéŠ>O1±ÖB,]ˆY%©ì@æñ€”FyW}÷Ýoq–º@•ÉÖ’þ@kpbù3 DÜŽÆ)Åó«Y£˜È>îƒR.F\È‘#û…‰å–SÇN;I¸×ËDÞ¡NíÂq+°­Æ²Ê#¯òqoŽzê»w¨£îó_™£f~›íÅ­ã¨Õ÷Ly¥ŽzŽ£:ê­Ò)Š DÍ:êÞ1¡xlz,\ɵ¤UWÑK$K xW~‡pÖ·j˜¾¤ xo¼C\U $F&À*¡-ä;fÃòµ¤=‰‰E-óxjÕô ¥Zk¸qÝcþV㟑‰q@ä8I7>«ž¹άê!+KrÓ—A;n:û‚CÁHun$-àt%Sü?ÍbË7ýJ±ÄÔT<ƒQ,Ç9.Î. /ôÚ‚3K¿V&sÙ­w§® àW=¸.”Äàì¤ÊEi›Uz8žu:ô»îo¤…_˜îg­Û©µ^§˜Uj„"H´ÌïÄMfŽ›lüšÎâÔm/›­Ø‚\ÌÅQlA~GEn³ØK޾X€:qÂÀÖËÄÇ ‰v¹ë Û²jÏJ;Èýï_O[ÖQ³±±ý¢=PG½5ðˆZI¤Ð øÍ:êÞĄް{ìG?O€¤åbżîÀÉ€iÄÔ·ï)J Ø«t¹J¥LZè93éáÖFòáDB"ø¹òH¶×=‰òQqi`ÄÁFù°MܺۨS¡œøçs†œÔÅ €€ë\„8Mýû%µÄ@ª@tïÅâ0Ý49v\Ç ±ÓµLÉom»ÞíÓ(ä¬étƒ3À¥Ô¦~O^x]J…N—©1ºø'"Ì ­äXáêÐn TQïJ µèµe×_²7õ–æÇ^Öß·¦îL\šø9g*î¢Rû{A]Vë ¤*H`;tŽk¿Qª¡p5Ýqè\È‘ÆqØdŽÜÒEq¸—‰<«*ìæèÔ¡]¥vä[¯Àî¾GÚ¾—Žš9j6¶·ê¨7[ ê$+Ó¬J²*Í+<ß,Çeµò1¡ŠÍ‹%Øp‰$!²bFêž«,z†viDyê¼™Ÿ†Õ²)ú+<ÊÛ½®ØI–ØK:H¿Mš­ËkêK\­d*äNº$Fù²ñÉ6þ§7‹ªþ&æK|ƒqØæ‚ødÁü[`sU”'vºgªÀls'0aÝPxx l¬WÂÕNzZgc:žºÁŒ`ÞI¾îJ¹–:ORßÝòƒ| ÙmßûJê@) !É 2é?í+/jÇCP]â=%IJdzdëU4þÀá—ãj4+æËoŽ¡}9S¦‡*¶ëÌÉ#+Š&wZ ÒpŸ§V™Y‰k þìî{¤í§ê¨³,Ûn·‡ÃáÙ\gr%~¯Õâ_^y{ÒÖ뮊'Ö£ÕðÙþ‚=PG½ˆ:MË$í@5BÇQ³Žº|”ŽzÀãÅR¹Ð¨¹ÎÀ§âœpˆ«Ü«?µÏ°‚€¸Jù@të>O¬êö-ô«~ö=ߨXÇ(aøé7‘ j ¡o|Æâ"+@P @”tÅBÖ ×£·F+=^Øûå)½k ă#–áUSÀiÛ®ÏÙéZ¥– ©›è¨·†ð¼ìt½ÄN¯Gšs–ÍñXû¾XGÂvš0ªKÁå#˜tÀèþbŠ8ñ–qZ E»s¾eÈ¿OqÕIJëf4¾/˜jñ–‚‘­…Ç¥¸n²òê‘Lüʵ(Ú¯žî­|.c¢ÄçÅ+z`|)S„û 4ÃhžÙN£ÄÚ¥3 YAýŽzê»÷½uÔ›õú/‚IÇqJI4©øv‡œ3Â1Åj—I6¶ß±ê¨7ÁQ'y’âÈJÁQÛ/¤£Î³Ì4Íÿþ!…¯— ƒñ}}!Åe"çß¹!'ÏsÃ0P)üÉœå1¡ß2x¥ÄuAÖ 9À¶5y®.ùDºŽWZ5zÛ«X¸ñŽU‘WµäW—Û<ù(ž¦u’Ôi&ük˜ÉZÆm„ýúÕþD:©Í¾Ý¯›&ËY%#j7H]¯‚::6¸Š\¨–bBÁB‡Ñ€>ùqD]ïvu–êVrEAóرîx™íÛoDá(êHZZk‚ÖC&? ¯Ûùâe\ Hhí´üq›5 >­­!^ªZ£<:_\;pµí:N¢î^¼*" ¼Ê9Qµ÷ŠƒWn¼zã7†WY^á……_~ãø)­&ý ,§Ëiû‘:j@ÊõJØZþk"«q½–e£ZÖAám|Ïìw;<æ’8Vì®Î +_UGkª:Îâù¸Ýn¶Û­¾Ý¹çºh™8‹.ð@‰µ$æÂuÝqüªwÚ>Rï=îÅîkÓ0²,ÔÅ?Ñ KeØ~Ó¨£Þl¤Ò¤H’‚˜êGý:j|?¸ò¥¯Æðõ2øØâz‹¢ˆÂÏJ"êA;¢º+D,HáWe—SªœïbxÄ„ž˜º ÒJ²»a¾ÄÙ.0®‰dY1ã9é´kɈF–¶ƒùšÂìà,ņ˜ãXä,ü-£Jôâ¿<3Eœa¤îŸ:‘÷›b§Ç>¾¢(·ËËžÏØrÿìe“òƒãq%~–¼ûÊéõÔwï{ë¨ÚTÛ‹{ž|ÛJ êûeâÙ†bÑû ôZª)åã¡FÕi3è¬ïûppÊìa3rБf’U]2øªexb*įzC1¡H©M ž`<ðˆišƒº­TØmçY¶?mÔQoð™J’FÃó Ç œÈu׉/>FYšÓl‹_UGqéúYûž—±ý ì+§—¤íÛé¨ TÏ¥Êt¶Y=h¤³cwíVeWŒSëÕG­—TœódIÃØøgÄ / >½Ñè¦ÚÄ?e¶}ðºi üª‡Œ ³±=Ȭ£nR)ùH³ˆúÕtÔ___ÀÌš?,|\*|ù§¤þ¹Ûí²,­zŒMF ö0ç»1¡·ð{ä÷$0°*Š<öü  à 9A À0ÎÄÀHÄØ82è %蕵cñüŠãÆê÷ž±ÐŠÝ…/°P!pN)R ÙH 2òÑ;b \ŠMøÈA¾cŸÆÓ–óâiW8uèâhŽn}tšØ9K#» QÞ>kGÍ‘>_¢e[¶ìtùb{§ÄŸ@Ìfó‚ËGêÕ©+S9"?§4¨èÀ(aC1tTKÌ) '®He­H.(y-Ž?“¶Ÿª£žc›¥¢KÇ–9jÔSuj)9!~¶,KÿIyóz œë½;"¥4འi=¨» Ûƒì‘:j¹ÖG§Ó¬z5õË TÏsñUùEž;ŽM9„“¯(SN©å,Çðˆ =™Wú'–Øêyfs>ùçel±Ñ›ØÀ"ëV [:»>Úu`×¾-6ev%lól@©ï«'²ú.µd¿¨Úu¬Õ…“J`†.=òѵ Çêbpdã2"6yuˆÖ“¾#Ô][²_[ö+Ó¼K«SØv=ð3±.qÈÖÎb›H«éyÑÓ0¹h ãh½ëñd§s9≬*”c‹qÃuaHS»Êúò™äÀª|eõñ¬ìQ<¶ý*:–þe¯ëÔŽ¥®KöE.ç/ÝLYzËDû˘3-æÓ\¸ƒ9šŸQ™lX¦@™ÌCæ¨ÿLÚ~¤ŽºÕЩ/'©]Cç¨uÔ:Å=>ÛžqÔCt+4ɹ‘æùÄŸqÔ£øÕY£6M“¤§zI™£0sÔl¿i_ºJåÒyI^©õ¨“×ÐQ㳦Dð'ËëùÈ!FZŠF„êß$9ÉHr)#ù.† hÍ~˜ÓZ‡Ö¶®NqxVë[íÑn»ÍqäýAÿLåÛmìˆb8àN[¹mí¶•#Óy¿ù•;q”䜢ͦršÚi„/SQËé΢zÞÇŸ µÅ?eصÔ%Z CéV…S”v\üjçU&Ò°Ü‹Cœ²ÜÎ3»ÌœR 1·J&síÞŠÜÎ µŽÕáXâÂN3'ϼ"ñ{ Ž£×j)Ž*òÊÈKN~´óÈ*"« †¯ñ²Ð„v R·MÝ&uÛÔk+úJ +*öA½ ^…ðöÈÉ[ÞÅVË*• Q«Êº[TíÃj‡4.¨•FâŠð†‚ƧIºmâIGl¨]ÄVz<äÇCï³ä&‡,}Q­Jö‚’ÕÑiŽNÙ8ŠðG‡$ÚãˆÃ]îÓ`_‡Ü·*Oâå¥c³½6ð«À-ŽNšXä°8àŠ‚r•û8ßg©•Çvu´Åòt"BU±‡ëXL  VPìÝÊtKÓ/waЇ$““…Àp!±¼®XŒ|W+³“ÜB_˜_¿2EwÅ>É µ‚ð>ŸSÎù…œ–uÔCëyiªñRRd×uh¸|ß×!ññx~ÆYÕ‚’aKZÉ·¯|]G­J¶ûý~2æouÔ'RüV>µiÍ=Ur§IJXGÍöË6ÐQ—Wê¨ËïÖ£ˆZlï"RÚ3±8稟hxYÆg_ü´ÐqàNÑ/ñ…€áAqÔøÀ¹Ô~Ø8Îacccc{5«F:j²7ᨧØf\¯mÛ@žx~áO™b­ý~³Ù G±»xÒm¥a|ôÆÕ'9êV`µÖ‡ÊžG`ú’ º©µ>&ã×ÖúØ+ª£ÃØÆÒ![L¢aÎk}°ý²8ê;ê¨%G]ÔÝFäúž‰¯¡£¦ÅîV«Óbw:{ŒO(-¬'xìê,ã@Ì6ü¯¯/Z|eò\ËÉóg]ûì³Ï>û >јɛê¨o³¶S¹ÊÊóß'ŽM­G}ãõ¨Ù~ߨ£Þn¹-‚Õ¯ÆQ³±±±±} )õ[rÔ·™þë¿»[]ׇÃaù¥@í™xã=Ù~ß­£Îz8Gí¼Žš}öÙgŸýôß[G}›=Ž£Þívô·ÝµÏÆö"öpuöº:j6666¶´÷ÖQ³±±=Å®£Î&Öúî™È)§œrÊ)§¿•Në¨?›£fccû¡é¨åqÿõ¨åäj=ê)ŽZg ØgŸ}öÙgÿ~¿gbÒsÔ]>sÔlll7[ÏQwß'Š£¾XG]Îê¨7ã=gÖ£æ”SN9å”ÓßJ§÷LdŽší6à¨Ë{ê¨7@ÔI’¨¢Ö8ê¤ç¨_ˆµ`Ÿ}öÙgÿüŽZ1ÕïÁQëë9ËÃWHðDvGßͰ”{(lÖëíè”nj Ž,Ë c»ßïoXDýì‘—à`{c;×Q‹ýX¯ÓQ— :jÉQ'E’tªnXGÍ)§œrÊésS]GMBDz½G=¹gb+¯—¶>iåó×èw`Á“Oô¹Õ›‘OÃE,Y{Óª *’Rî€vmu6¶?a×QOsÔ¬£fŸ}öÙgÿ9þ@G]‰ªëò]tÔj÷Ã1ôÕöõvôíÚçLme(©îãMgi•i´iŠâ<Øív´»"m,®×my+C¶÷µꨉ£ÎºŸ%&YÅ:jN9å”SNŸžtÔeŸÿÞ5Z°,‹|< çdº¡<¡b½5Õ>°q†pªÍYÛ¦?C?˜†1Ž ª0ØØÞÉ©£–uÖÁé$—k}8¬£fŸ}öÙgÿ™þ™Ž¤$¡çÑ‹sÔªçRes:jWKÀöœI¥G­Z´O¼Y+u#ðÇÕÇ ¼•Ìöda6¶¿nÔQËõ¨ûí]x=jN9å”SN_!¬G]ãéT×Õ»sÔº]ÈQë-ŒáñäYŒ§mÛ†P~næ"¹ϳ±ý-{ Žˆº•H[Àiq°Žš}öÙgŸý§ûµÚ9ñÅ9ê mAG­  7Š¢o›R¿FÔ[Síoû³:Gmšf†×u¶\µÉ5Û»Ú#uÔ’£Òôµ@ÔÖQgiz8ìñ  ç#L¹ÚÏù–u(ò|\×÷}|ÀMÃÀý<’T„!~¬¡çãíc¿Ûm6cmYVžçãºAˆ0ä×Ñ}Fc¿<Öähr4Œ»ŽÆ+İ™ž‘ …±<#ѽfäÏÞŸBú(k!NT¿×ýù 1\ûA`âvB”kÄy¸°¯»ßŸy–î>×ߟGÇ ë¨?g­ÝpáøVÁ“¼]\ëCè¨û~'uÔôóF¤ôÆV‚ðRþÒ_\ªän·C&ù¬£f{W{ Žºç¨¶ã¼×Q?l=j|HñAÆçš¾oU>¾iP ayøfÔÅõâ›ßÉr±Mã‡ñdYН© ðéAù#ŠBô‚ü  ã¬.S<¾‹__€p?‰A†1._BŽÆnPa¨Ñ0Mã'1”ò½Ý" ÿb3rÊxN.Îfä¬n7#£›‘¾?ñ@ÄAÂàü$†R»?_!†k?#Ãs]ç)Œ'ÜŸ{\ÆsîOš”~4œŸÄPj÷ç£c¬G]IÕGùò:ê m’½‰'ãœ[Z­õÑNé¨ÕZm­à¼ÜàË _¡˜MzyÁ ›†¹ky­¶÷µê¨ ÁQ'’£Æg€·(üµm[¾ç}S²(ðÙäã3®ø\Ö#øñ†åÑ÷ç+Ä0w" Õ{Ùß!7§¯2#·ÞŸb|Ôh”;Å0ÐQ«ü÷à¨ïkj=ê»XÉëQ³½¯=ZGKK8£¨j êÛtÔv˜­ì^;•Á»p–eËí pþíòMÓPVWÓÍă/mÇ®V«j½ÂWyÎò‘m6-Œév’³0º|ää=;4`«F~Qçv“¯šlUvö¬Œ6³íhaœò Ã(Naäó1ˆÓm·__íjUã+w*æíf“‹0CQvfÅ_+Ü) ÅÕ1”§9Ë×f¤(¦æúìþtÓ•™¬ŒØöžyfžZI=÷¾÷çápð}Á£^Àˆ^ꚉiĆ—yÅM÷çc(½cj‰áÇ^œMŽÛÍŸ‘Ãa Ɉ,Ä ¸ß<ŽS?É‚,?N–¹ùþÆ\ ÂŽyîgŽX’Þã27ߟCìÄw£1{-y“gm†#¯óÉ2ßÞŸ?þ7ÐQ«ü÷à¨ïkrÏD÷^­ñž‰lolÖQ“äÇ_“u# _¯£Šþg%êp¢l\f ”»ØÆn·sž±Ôˆ“w-E8ýõ¯þú×¥øç¨ŒäO–ø"t´aù$u ¿á.«"§Ûü_“ý£T‚êñhœø«éÑ(gF#¾t4œþ÷Oøçôh̳g?pú_üï_òO¤ñ?üóÚhFDÇé‘’[‹38ýo›üÛÆt8ÞsîO§·[:c›Mq}7ߟ¼éÈŏ֯v»ÐPô)ˆx‹NÆÒýy‡âlë£ïØð“­Õׯ@S29#…c»–«’‘gvFœö’ÌåP}Çûól4 c¡…¸‡Ó8¼4§xõ›ïO„4~ÉhMAp:mSª›üÚûóç1¨t°µÊgŽšíf{¤ŽZ¬G­à4£¾NG½²¤c§"Å?Çezªgº<÷ûý°k\‚ïa|'{òïžsñvZÂéîÏ a™ ÂÄæôµä" !œla˜&ÂØÐ£ç®E²Óÿè úk\f™qÊO£1Ñ~<ékì´†¨ÛÕj\¦GÅð–¦c•\CÙÏHØÍȰL?#[ß÷—îOó§q¬Œx\æîÏØ0¢xvJû}óý)QÏ#FTŽÆt 031·ZF<¡ý^¸?ïCN¨FjøñU1,ߟ‡ý½K½wÞT§Ç3N%–N=J­Çen¾?1ž¼Ùû“€´Jý©ûðæûSÂ_¼?u8MéU÷ç]bPþ@G­ò™£fcc»Ù½5)¨;Žºê8ꎚlÁ—ˆút¢”±¬ýžeÜÂÞíÌDÛk®/b?æÊÔëÕG½ú—±„8ÓŸl?•{³&} ×K¯*se¡uŽºÍWã2òÏ Á8¿ìFcG£±<þ:4.=‰¨õ24‹ú„¨“«øŠª~F”Fz.†RÎÈÒýi(8- 5ö¸Ì/ÜŸ€Ð=1,ž·ÔËÜ|yXö¬éBœ€ÐZ[ìq™¹ûón1ø;M ûòÊïîÏ3õ| °$ ˆVLõ¸ÌÍ÷§âØË²\ŽG±ÓäY6.sóý¹‘£QŽlÜŽÒê¸üþ¼W Êè¨U>sÔlll7qÔúwÎU:êò{µ€Ó?ÔQ Õ‡d§‰©¶‚t\mn6úI¸žܧ҂6<³P Õ¯¥0mqÔ¶5.ƒF´0NùÑYíãQ"Ƴ 馱0&un)8-T¹5.3._ cº}ëpˆÇóјˆÿ5–5P}ŒËàNÙnF çß1+µtŽÚΖbh¡ÆþìÆ¶ßÍH©fd:†R¨>2£¶ÜçÜŸ©ëêu6¥­½ùþÜï÷ŽãÐr ËŒ¨›¹:Gí¦«a¨ÑxP Þ1Ó9j÷˜.Äpígä´²DQhTùDÌi~Ô9ê,ïxî÷;×qH'¶ÌØÇy®sÔÇóÕ<è7ߟS£1=nyëuÞäã2êþ|P Êg5ÛÝíñ:ꉵ>”ŽºëTû¾›ôñ§~™hú#æœJZÏVÏ'¡žvßœ=ƒ‡«0ÿ×k &zGXb6lK0Õ«¯Jûeâ ŒC®žªçbXõŒ®‚jxˆ˜¦¹a˜DþÏIQ¶dªW€ÓUÏÚ Ê'’ࢥAUþzj4*9¥ Óa†¢Ê'ÛG¿T¯V89“1ãVÑþºs ÷¦Ö*Y­âàtQ.Å0ÐÍŽg„òÍ^‡©Ïnöùäýée+C°ÓçpúWïOP´`ª #ó¼ûÞŸy.T܃Õ'ÛÇ,x™gĆ™˜=œž½?ƒR¾`§{8}·ÏÈxõã¹öK ª‰©&8}ÇûSÈû­ÓzÔ 1”½”:Âé;ÜŸskAÛÇ?Ô/‹œž¾?ƒò'uÔ%sÔlll?0]GMö€õ¨ZñC¨>h=ê¤×QëüÀ/úxŒÊ?qþv¿atŒÊóbèG#àï§fä©÷ߟ/CÉ÷ç¹ÿ ÷çc¨&9êâMtÔj½hei¿™^;ÇÖ—ž.å_HéÕlnUêe[^õz¹¤Ê9öã2òjE)…·¼ŽŸ^—Ö¾¾Ðäz&^+wŸ1Œ­|ýô¢Æ½óz&kguqïõ¨ñA.%Ò–L5Š䞉×ê¨ÙgŸ}öÙgÿ^þ{ë¨ÇÀÕ¶¬DYðiï*ÊÇ8l·[ä´‹;'.ÛÂΌߖ\Μä8w‰d`jÍmµëúå¯ ãÞK^sûSíÑ:j™)Ná˜Ý3ñÙÌûì³Ï>ûŸãw:ê¤ç¨ûü7ᨿCƒ û©mÄÇFš(’½é`Þs]ÀN…Ã[ >Åöˆ2“$­ÄçD}+Âv£¦˜;FZ$«Éò*ržaY–-ÔEïjcÇIêXí IuU;tVí iiûBNô®Õmy_ÈOµŽ£î¿OªòJu9¯£6L8™ÄØ8€¨«¦µ$G}­Žš}öÙgŸ}öïå¿·Žz™_¥õÞÉž“y8ŽCOj<íª)Ó÷} p"ÄÔîä–Èo%û „I™8Ka“ ¢]d¤µ-Îg9jÄ£Øuýr|¡." ÃR³nh“Pñdûì?Óÿ45`ž%Õ*g£ÆÙ¶MË.)Ð8‰À'™a Rµ6‹B§?á¨]×U/m§U¶Õ‡—ëêï—Ç¿Ü&zÇà,ô>×Û{Û£uÔiy:N5ë¨ÙgŸ}öÙ’ÿ:j!öØïóüìê £(šlX:ŠBzõÐ8jãrŽzÌ'«£žkŒ–FìÃ3Ã0¤`–몷†EŽz¨ôVmnû³:G}Öû»ÎõgÚÃuÔ'ŽºU5ë¨ÙgŸ}öÙ–ÿi:jã¨YGÍ>ûì³Ïþ“ü?ª£¾ÐZåSÎùjd´?äö|=j<â ¹¦N©|wj­ÕšîÛ¶M«abo—9êžé-ŦN&­&=.ù¢ôÐæF6.ÂÛnéea²®¶ÖÇ~y­ÉxÔZm­ÑûfªwàÞy­Ï´_ÒQWg5ë¨ÙgŸ}öÙ–ÿÞ:j¶kM­G}+y=êOµ_ÐQg¬£fŸ}öÙgÿeü÷ÖQ³]kRÜ2!¹ÍxÏĵ_ÒQŸ8j¯d5ûì³Ï>ûÏóß[GÍÆÆöc5ûì³Ï>ûå¿·Žší)Æ:jöÙgŸ}ö?Êg5ÛÝuÔì³Ï>ûì”Ï:j66¶»ë¨ÙgŸ}öÙÿ(ŸuÔlllw7ÖQ³Ï>ûì³ÿQþ{ë¨õ}·ÉðÜÞï÷ëõz³Ùض­ï{ˆë¶,‹NéëQ_ns›ž_RRåûq™ÓÚë…·¼Æ^—Ö…¾Ðh#›VîÌb[9V—^Ô¸w^ëãcuÔì³Ï>ûì”ÿÞ:ê1pŠ&" æû>m’Òʧ°Ú«§ð\·û=/·ñNˆ——\ΜäP‘ÿ0’©õ¨ÕŽä—¿&Œ{/y=êO5ÖQ³Ï>ûì³ÿQþ{ë¨×ß¡ÁñÝcÃS~·Û¡¤iš:˜÷¦öL¤}‘I’ÎVâs¢¾a»´gâJÛÛ±ßÕq\^E®Â3 #˲…ºÚž‰‡å=Õ.“ÔU{&ZÚž‰½ku[Þ3ñSuÔì³Ï>ûì”ÿÞ:êe~5 CEDÏÉ<Ç¡'5žöÕ”éû>8bjçnKäî¤Lœ%ȰIPÑ.2ÒÚöß³5âJ„‡Ô4…ºˆ—L®bÖ m*žŒG¯®.Ä‘œ¿ì=6‰ÞÑ  •ísŒuÔì³Ï>ûì”ÿGuÔªçRes൤P c«ÔÈÊ$¼DÝ1(ŸÉQXã¬`¶Î댮ó´ŽzµÚlÖsúä1 DBµüqu©ô¨µT<ª:ÒÉꓽÏõÅöÞÆ:jöÙgŸ}ö?Êÿ4µ2ÀB]¼ÀQcˆlÛ6Mc-2'ø$3 DJ^ÖžàŸ9ßrÔ®ë*&¹í´Ê6°:ððr]ýýâòø—ÛDÞçúb{oc5ûì³Ï>û峎šÀBìÉ2ÀÒQÒ«‡ÆQ—sÔc>ù[õ\kd–uÀ$õá™aR0ËuÕ[Ã"G=Tz«6·ýY£>ë}Š]gŽú3uÔì³Ï>ûì”ÿi:j±Ö‡¬X(tÔ½Ä~?<Ï[ á8'‰õBËÊ)®ë¨Õr“,.j‘ŽºÈóÉ’ƒón·+{Ø<(/$¸' ŒbðÒr]õëK¤³:ê~l'uÔãêßöÎ:êÏ4ÖQ³Ï>ûì³ÿQþÕQ_h­rÛ¯G½‘‹N»®£³ÇyžÓZÛóõ¨ñˆGaC®éS*ßZëCµ¦û€ñ´!öv™£î™^̃iš´šô¸<æ‹$+hs#ám;eød]m­ýòZ“ñ¨µ>ÚZ¢÷ÍTï†A½óZŸi¬£fŸ}öÙgÿ£ü÷ÖQ³]kj=ê»XÉëQª±Žš}öÙgŸýòß[GÍv­IqË„ ä6ã=?ÖXGÍ>ûì³ÏþGùï­£fcc{Š±Žš}öÙgŸýòß[GÍÆÆöc5ûì³Ï>û峎šíîÆ:jöÙgŸ}ö?Êg5ÛÝuÔì³Ï>ûì”Ï:j66¶»ë¨ÙgŸ}öÙÿ(ÿ½uÔú¾Ûdxnöû°µãØú¾‡¸n˲Ör©j}=êËmaÓóqI}'q²( U ´˜óïm^óm§—_Ý寋¼«±Žš}öÙgŸýòß[G=±g¢e%’È‚<Õv~µW Îâ¹n÷{&^nãJŽÛßív—·pG»p êGĆ~og¶×7ÖQ³Ï>ûì³ÿQþ{ë¨×ßѪãºÇ†§ûì³ÏþGùï­£^*Ð’ô8'óp‡žÔxÚ—R&Ð/8b ‘7"¿•ì·a”‰³¹6‰+¨dy¾Ÿ ð6šR_EŽÀH‚T•GfžçƒL4NIpîõ%7ã’ºî*ý‰Þ{àûÄÕ« Ñ9êɾÐ>Áû0Tüª¤~ùôêA>z·û¿°½±Žš}öÙgŸýòÿ¨Žš@õ\ªl¬£V†«Þï÷c0¹l (Æ×¼‰©$zTè”Ίn%¶'ñƒÎ÷€îU€ý8)ØØn»’²ú‚&|³Ù4M=ˆY·1ÈŸëKÏ\k—¯2·}I´¯øyœE sá±ýQc5ûì³Ï>û嚎š Ó’‚j•³ÀQ“Ö×4µ4ÊœDàz¦Vrµ–š Y{¥ŸU´­ã8…d’ÇðQ¡ zßívÄ6SH¾ïlï÷È<½P/”j%=Yr¯ªóä«Á ä¸/½€^k\r0/OQ³=ÔXGÍ>ûì³ÏþGù¨£b¼fŽ¢h² Ù( éÕCã¨Ë9êñrê¬išÏ Ì®l(Žw2s|vPÒ˜* 9ꓺ»ë]»å8êqSêJ‘ªø'Kêíc™£~?cõµþ¿sÓËàËÊu]Ê|zœì³Ï>ûìOúŸ¦£Æ3Wi-tãØdÀÀxpœ~ Šívƒ¡%曪먕>y’ïUBâ"ÏÇ%ÑÀ¹úuá˜ÅEºÞ,Ë c«2I¯‚G­‚¬èˆ^ èù»PR7¡£îÇs™£Þív‡å¾èJõŸXN–ÔÛgõ[ë¨oöñy¡ÏšÊÿúúÂK·@ÔÏŽ}öÙgŸý9ÿê¨/4}… bM»œó•+Èù¶*·çëQã¿Ùl ¹¦N©|wj­ÕšîÛ¶fu䬋:Nn§Xbú9$­õ¡^%€;ú< %Ç<Ž;ꫯMï€Ýέõq¦£î|ÜhŠèôɾÄZ{±ÖDůJšg/§öy­·´_ÒQ—o¨£¦|Æù@ÔO}öÙgŸý9ÿ½uÔl—Ø…ëQ_nRõq‘4š×£~W{¸Žš2eŠbo££¦Õu&ËHÕÇ«ÄÉ>ûì³ÏþÀo5Û%&-îÏÛQ• .Üó‘÷L|W{ Žˆº‘ìtªGý§uÔd‡Ã! ÃÉ2¬£fŸ}öÙeÿ½uÔl¿i@C»P˜ìv;}ó¶´G꨷‚£&5ˆLOõ×QgYºÙlæÊ\®£V›UšqçpçüÅ/Ýàèˆ#,=íÁOã?)žVŠ`'¿{ß[GÍÆÆö{ ŽZrÔKçUZ ­«ºðûïë¨mÛò}®Ìå:êgO>Û;XÕ´Eݦ•8~çoÉu+ú óÖMÛý±Ý„í:h¿pøé6%Ÿbsß½¬£fcc»»=PG½ÙQ'i™fU’U@ÔeõŸ½«Sˆ¢/nÜh5‰V¢Ñh5V"‘H5‰V"ÑH5óÎÌ•ÙYA—Ý•ôœÏoÞ}Ãü!O9œw¼s^®7c÷Qçy>™LŽy~«M{uߟ ˆ±,Z¨ì¿P³V”ïÑ¥Ü6ä6x ÀÛ£LÍ’OóÚr)?ÖG±°ÅN­RµÜ´Dÿˆð[Üú ˆ‡£Kµgþ[­8T¤ÚjÔOà£~HÜ÷Å'b¬_}¯tà ¨¬á±ÁîRZ{zy•|Mq®§›Ä•òl^‹½Z§úÐþ¨™vyÖËÝQyU_, 4½ÒB÷º¿Æ³áðh‰ÀÆ÷F±AMûS»õÝK5AG‡>j³'Qšæ‡Ã1Å++´F½Ú<úQqߟ ˆQ¬R4áüdˆëY—òÂ7ðÔpWÐì¬En°“a¼Q¦¶-noª,ݪߢ1&êw¹îu5ï—q’k^W¶ßÌøxMcý¤xˤÿÌBc/´ÁbÀËÏï ¨~`’%ßúî}nu}çAÜ·çóùd2Á}yµZ¹ûê›ør)‡Ü|ÔíqkÓóÆ–õìÐIÛZæÊxdóš/'mvíÁ\ÏŠ}ÔS£Q§Ç4=õ©«¾Ñ…|‚Tƒv¢Á/5ç!” ä{c\ÁË=#õª>êûF…8Ž­@ŠÛô-›Çz½–;µìè-•`¿`à"ˆYF ÞˆzàÕ¾ïK%Ž åÆ²Å\!-ñ&cd; ø6†² ß® JÛ•yž_Ubp™ÈómÕrZoét×úOÜÙ£0­Þžˆ«Q7Î…ñ…ÞÇQd×o[º§/cöÕrÙøÎãÅ£|ÔiƒZr}:­]'ú¨é£&â÷ðŒøœ­Ôà“1W§…¦:Mt~q_w§<$VOç£R}«´¨û¨«z­¶ú¾gÝ-- –(¢o»Þ¥ÄÒï³e§rTX4¼ùÂí]•¸šÈu¿re€Ø×+E»»ôò¼KKÓýŽ'Üüzyµf÷}«“ü[s¹•çôm¥WµÄøVŸÇQ¬áÖòˆ‘¢ó|ÔÙé`Rç¥ùéiòQ?*îûâ1>à öâ>÷¯¹TÏå£n;5ˆnÜÖG}G£¯ïlæO ¤²‘»•NËw!ð¦÷»{Ôʶëõúh”ä:}ŪгA j³,) Cíù• ³Ì"¥ÓrkZÎm÷/×ÜøhpÕ²>—ÛÀíUoyu]zq¢Û|ÔJ Ö›¼jú¨ ‚èÙI»…ýd@jð0cEõ­—gÁnl6›$±AãCœkz•Br©žËGÝ͹>ÌÔ`}ÚG]9xqë÷08‚õ‡ÅzŠ7DRÌYîú¨­?¹QïµFâcž×[bsûëÂºŠ‹.²à,Ë|ß³•b~ÎóÜRVL$¨´ÖîÆ–.´ºº÷5ê ìÍúÖ\r¦îO,[ºãÓGý”èÂG}µg4êŠN£D³åzK5}ÔAüàÒÿ$-Þ`ÔàaÆêé|Ô-áf¨ÕTòQOMÒéÍfíªÇ |‹Å¬Òûœ·x4öMN²õ›¦\v47ǰ.svM–'«&•X~)¹>ìcxG•Á÷¿+ĹÔ'ªºOf¾_Ï€­nåúøä£¾Äøw¡DNoœKçú˜ë\xCìúmËÙ§‡ñ™ëã)ѹZ[>®5jú¨ ú¨ ‚ø$å²|Óö®¹T¯ê£&Ú e>êö0®VÖhæ£~Vtë£>«Tû¨OôQÓGMÄïQªK¼^TßqÅêU}ÔDCËæ÷ãXƒJE-÷|䞉ϊ®}ÔU®“›ëƒ>jAߟ ˆ‘!;i:ìúW€‡_ªWõQ °¡ Г Ž4s¾6:ôQ7æ£^3õGÜ÷Å'bdˆÌn‰ë´xø±zU5A½ KõÍ=é£.è£&âûh¹["Ë’>j‚ þ]ú¨§Z£ÞçiªI5õÇž‰ôQ›?ú¾øAŒ Óïì–øâ±¢š ˆ?D‡>ê©Ñ¨A§SPhmü°5}Ô‚¾/>AcÂÙì–øêß'ö®¿TôQñ‡èÒGm4jM§k5}Ô}ÔA|²[⌻%¶‹ÕÈ}Ôúöº\Nk™¢Ý\ÓS}heÕwK´9q¾Q¹ù¢ë5Aüç£6?Kwû‚_ÕñtŸúB§ù‰>jú¨ ‚ø16­QGÙ àáÇjÌ>êÍfEQã¡:m¶¬ØÝ•ïRó¹ñÊMÄ/Ñy>j½g¢ÞÞ…ù¨ëqߟ ˆ1!ØéŸ%îƒP€‡_ªáù¨ÛÃó<ëå¸Â•ªŒûér¹”øKÂ\W¤©QģСÚó¤fòTïðRÒGM5A?Æ4Öõ±„<üX ÉG=1Ögë–Jù¡¢[:íoÝšzÝÆGmûÖGûÁéQG‡>jßhÔ`ÚúHuy*Õbü>j¼K‹ÅÂ|Ÿ½‡áÖm“çùf³ù÷ï}ÔA<g³ÿø{Ô¿ö;–RÙGÝ^£þt¨Î¨©QÄ_¡Cµç‹F×¾Ò¨ã÷Q¯W+Ðfç,Ë@­Ý6oooóùŒš>j‚ ‹Üì?îÅCQ€‡«1û¨W«U’$‡î¨Ê³ÙÌÝ1jî÷¥FMÂ#}Ô©v}ԩѨ5©ÎNh¦í‘û¨=Ï™¾ÓÆ0êVcö}ñ ‚ ÒB3êžíw,¥³Zî5¸Óª¯r}¸@³ù|.7ÐiÄr·µ FMÝ¡kõ¾¢ÓxO%õØ}Ô ÌËåòíím:⪷ьš>j‚ Š(׌zE~¬†ä£þò<_,`¼žwúN/Ü¢ƒ ˜LÞQÖ7”¡FMÝáá>êkZèt~Úez‡-hÜG .†[|£ô<¯Þ†>j‚ ŽÍA3jI7xø¥žš{&Ä£kµ¥Óxiz¹»z2™H \ ŸÛ´÷QÛt£‘Ö°†5¬©×xaF½ ÷a‹ » #ĦŒD••XêÙFÙGMÄèйÚü,ñJ£µzµZÉ£D†¾ï×ÛÐGMÄÃ13ɨ³SÿÚïXJ5<55j‚xbtï£~*užçA€6{ž—ÒGMÄŸ`iF-ɨËÁx•‡«‘û¨¿‹øPÎã"ˆŽ”cÆŒÿ"ÆçŸ>Õ©Ú—\åÞÍõ1~õãž¿ ‚Þ £‚ö;–R=—ú~_ÜÐW»2Ìô/X#–,Yþa‰Ï>}ø þE>jCª÷Ï’úñ¿Z ‚x)œÎz·Äi< xø±z%õ<.ƒN³Ü«K–,ÿ°Äç.<œõg°;µì™X\ !£öQ?0îû;˜ ˆq@oï©Y2íw,¥z.õý¾AtŒ2ÜÜÏ—ýž1cÆãÓ§?ƒú¨Fm*å(š-E£³úñ¿Z ‚x)¤…Ö¨e{—?PwŸ#V¯ä£´F}Æ}¹Ó7w¼E©KÍ(ÊÕ^³wýÀwÇÑ]LŒ¡´b?€óbù %>}Õ¨;ñQÏÇR}ÂÜhÔôQôQÑI®5êU:íw,¥z%uÃìB ñ×u:šñ­2?©MúiýJ}›«Š„ozÀ!œËW(CG£v¿s룮5zM5}ÔA|øºþªía@ ððcõJ>ê 2µa¡.âì|<^º+¥®9jíºDÍbW®÷g‘‚£Lw\˜zexìU¼Úk‚ª»Ÿb{Ïzø¯ÈÌ®ë÷š (½yº>ºÚë½팲’«”X€ÔWë·³k³£ËÉ4ǵ®Ÿ;Ú`ÀЮŠ%Ë.Ëíá¬?ƒú¨µF*R}ÔÿøÁ¨ ú¨«àqߦA<3Ö©Ö¨ãlÚïXJõb>j°G‡—ˆ•f¼g‰³âƒúâŽlZêÊô¨§æ¤…¨ŠZ»ñAwWB}Óâc|tÅM2í²‘1µÉÓ4X§’lûʘˆ¥^ÚK=èzãú¥ ÚïÍ:QÚ¾è‚Å€Àã©—ûª/b ˆÇ‡ö”Œwk×G÷>j]S‘jmëZo飦š ˆoa¹×õî8 xø±z:µêÆ2ˆ‹mZ.¼ôÃÂ<9!F°IK‰ñž¬ö:ֱៈÏüS÷Bß 5ã¸ñ¥e¢ŸñeË~Ý1¥ úb :/A5&âõþÛ6ã^mzö«¾î:m_Ä8_ü3°mÜöŒ?6Ƨï|Ô¢QKi5jú¨ ú¨ ‚hÀl˜˜ƒÐ~ÇRªWóQÎ KDkôòªýÜiy+¡¬Ÿã«îã×Ç´ãK4aF™Ÿ>Æ·m”!Ï·Öÿ1æî#ÞìK½…èÇ’>õµ#ÔçbÌøá±«Q»ß9Ýø¨?kÔôQ›?¾ómJÄëÂO´F *Ò¢û|±z1õÖÈÑóØÑø4¯©Ä¥Vk/±-µö›œ +øÔKZ.œDžÁ߯eSË0Õ.nÄ(7ûrÞ¸’ÝéÖúí:Ñæ\E´Ó>–Oë´}Q^׿bÉòáå&-ÿÌGíhÔÛ‚>ê*èå‹— ˆÑA¶ /ÊAh¿c)Õ‹ù¨5£Ž ‡ˆê[LuÓ/´™9ÓbÚfÊŠK½ÜµQ)¿%´ô5δäP˜J3&$™n‰YP_߯ûü¼7c®÷¶å…Ç™þ!¤´w‰Žµúöú±Nsoþ+Ü’çõNÓæËâM_±”HŒ¥bØú\Œ?<Ö®?ñQgôQ߈üÕJÄKa¾ÓÛ»”}«¾ãŠÕÓù¨ï ˆ PeÜÙCDQVŒ´¯Ì¨8xs¢C)õËä$94¢ÃÉö Ó“<’€âÚÀf3C°O¦{}|7Š[j }i‰|ÞLôQã–˜ ½n­ß¬S™Ÿ4¯–£ÊÎ‚Ë jzpl4<–zðùmzªÏÅ’åÃK|úè£î7þÓ/\‚ Æ |‘¾…Z¦î]õW©^ÉG-÷ô…Üâ#çvß:–A~Ö·M¼ÚiçÉ'*ò¹ xÚ† ýŽ¥T¯ä£ÀWçñÑèfÇÀ”ߊÕE£þIßû1 q9.ŒÆg̸ßxnMMôQÓGMÄ€/O0ê é_õW¬^ÉGMDï š>j‚ †ŒÔ0êžÕw\¥z%5A½ƒ>jú¨ ‚2’£fÔ«}ÿªï¸bõJ>j‚ zÇÃ|Ô9}Ôߎû¾øAŒq®õæÐ¿ê;®R½˜š ˆ~A5}ÔA a¦õöпê;®X½ªz2y—àýý±.ßß§Óéz½.ËÒ6Ó·ïår:™xµC.ðFm·[Y–ù¾7ŸÏíøí5H€¡Îrmâéð@uJõ7ã¾/>A#Àæ uœõ¯úŽ«T¯ê£¶ôu2™¸õ8ßÅb!1Þßóv»2œwôÕjÕ8êåí'2lÇo»¼Ï`ïßíN£}ÔôQ1d¬RèóþUßqÅêU}Ô“÷úúPÅl7›uE_…ûýf³‘1]Å[Ž–e ŠŽ1—‹…•¸A‚ @¥ïûY–]õ5Soè`!žôQ÷÷}ñ ‚–{ͨ÷ÇþUßq•êU}Ô·4jŒ°\.%ö<ï–ÍÃÚ +vG³ãƒÇqŒ¥8C€õj%ÿ þ0óýúJ0 ]A<è£þYŒ§ûÿþ]Õçy¾^¯ëõ·â¾/>A#@°ÓŒ:=ö¯úŽ+VOç£R}«´¸å£Þ8fé+²} ÆéQÚÑ®Æ-JÄõîu®Œ²Ýؘ Æú¨ãK_`ÎWõooo‹Å¢^+îûâ1Ì £Æ·hïªï¸JEõmÚÜR£vG¨ÓãÆ£x'W«•ïû`ã·VÒ’Ïĸ@õâõz½Ýn´ÝÔæV}=îûâ1x±fÔù©Õw\±¢úöOAz“$ùr(ûkDw4;¾Wu5êÙlDZÐuW-·cR£&žôQ7Î| 0̹¡Í­úzÜ÷Å'b˜F}:÷¯úŽ«TôQß–‚q²¸‘áN®îæúÐ>êjÞFµü¼¥ü€QŽÁA›Ã0´-ƒ °·<ú¨‰g}Ô߃ÙÌœøñ–_š>j‚ ÐiaÔ½«¾ãŠÕÓù¨[¢Q®#ÏsÉÔáy7óQÛ\ªÉGms},œ\àÓ©Nô%H»<¼àÝùþ|>WÌõAj‚ z}Ô=Æ}_|‚ †|y¾Ez“—!¨¾ã*}ÔAü!å£N_ÏGýû¸ï‹OÄБZ£^ì¡úŽ+VôQñ‡ ºÇ¸ï‹OÄб;jz¹„ê;®RÑGMÄ‚>jú¨ ‚,’\kÔëtªï¸bõª>j›/Ú÷íÅ|>Õ˜¬×+7õ´¾‰/—“ÉÇne¥¾ûY¯ï·´5‹Å¼Þ1ÎEJYÞý<~n_É}Ý&ŸÉV™Ýg|ßCßú{ø%ììÌgò² š>j‚ ‹(Óõö0Õw\¥zUu¸®–ËÔYˆw»Ý²ïƒçy¨QwwN¼;;3~Ùò~ÍÕQPŽÅbñ•\ÁæÜ¶»®·L¨Ï^0çö«‚>jú¨ ‚,¶™Ö¨Ãlªï¸bõª>êÉWlÐr?»x¸ËA€–³ÙÌ%óÛÍ´ÓòpeȧÞÑTŠ¥S~.Ò78°lïhÔ²æ‹"­‹÷Æövåvy¾ïgYv§/f·;6JÇv_HékÇ‘£v_È¥³/dÃìN_Å}!_ôQ÷÷}ñ ‚:6½yœ BõW©^ÕG}__ÅCÄ<$¾eóX¯×r§ÆÝ¤Z*Ã0AÌîNb‰zeÔo0L©ÄQ¡ÜX¶*Ô]EÚÙâü¦FõXuÝ.ålæßé‹•ÄqŒ¥]³ Œ)¬¸q=nw{"ëÕêp™}?ófÇ€v©Äë€>jú¨ ‚,V©Ö¨“|ªï¸bõt>j!Õ·J‹;`œõ|>¯Èû°ôÒ÷ø•óYËÕ1£¥Ù®ì*ºîš›}ÔïïÓéä–?¹Nƒ¯V"½°ÄõîÆéQ^õ²ë±ÝQ6voœýÖ\Äsƒ>jú¨ ‚,{í£Þ¡úŽ«TôQhÞÒªmÍoÑjµšÍü‰T62ðFeŒtbذéÝ ?_Õ|©Qo6û  .^å¸:øðý¾îóEûõß³ã͹3û­¹ˆç}ÔôQ1XÌwZ£Æ÷g׊îóÅŠ>jÚì1Ÿçù§³-L’¤qpé$‰åÑÃѨýöu]OþÒG}k4Ár¹(*%j6›Åq,‹¹ß×>5ÜÕ¨¯ÞvL¯:êjÔŸfoRשQ¿&þÆG­_dzըé£ô}ñ ‚:f‰öQgÅ Tßq•Š>ê ¸ç.‹zê9ÜúAüp?W†"XW¹>ê×Äø¨…NŠ3}ÔôQñ-x‰Ö¨§A¨¾ãŠÕÓù¨[âÊ«üQó9† ÏsÉeá}ÎG[^]ú¨5£Î.tú“FMuA5A-0‰µFïØ!¨¾ã*Õ«ú¨‰ïÂæ£~ æ£~Uté£ö´FmÜ R¢5}ÔôQÑï‘Ö¨Ëþ”ÞñÆêU}ÔÄwaÌ- †Ÿ{&¾,:ôQ{>þMi.m^hp*•¦ßôQWAߟ ˆ¡ã-Ô¤ºw½wŒ¥zU5A½ CõÔ£N³âR¼0ɩҨé£.è£&â €‚QO¢¡¨¾ãŠÕ«ú¨ ‚èú¨MòœÃ¡HR}*Ï¢QÓG]ÐGMÄW8FíÅýë½c,}ÔAü!ã£>5ù¨uG|eåéá¨_Y¡5ê}ÔôQÑ ÇR3j?Šê;®XÑGMÄ¢CõÔ;—øÊ:âKK”j«QÓG-èûâ1hä†Q»þõÞ1–Š>j‚ þ]ú¨õNCZ£6¤ú“FMuA5A__§`ÔóÝPTßqÅêU}Ôî¾ÛÜ·çóùd2Á}yµZ¹ûê›ør)‡Ü|ÔíqkÓó6-mÍb1¯·ùÈ¡=y—åÝÏqçö•¼Ð-!Ù(³3‹ï{æ½j{RõÙ™ëãeѽZ[>YFM5}ÔA´Gjõrß¿Þ;ÆR½ªºN\Á¢EÈÂ0”MR”¹ Û½Zp÷õUµgb{ÔwBlßò~ÍÕQP»ò_®ä 6µÝ‘¼ýcB}ö‚ù¨_]ú¨M®¡Ó0ê}ÔôQÑI®õj?Õw\±zUõä+6Xß¡»Üåƒ @ËÙlæ’ùmÓž‰²¯"*ÅÒ© ?éØ ¶÷öL|wöv¬vu¬··+·Ëó}?˲;}=÷÷L´»LÊ8rÔtöLl˜Ý髸gâ«¢ó|ÔÙI¿rÉõñ ù¨ã(Â÷ÉÛÛ›|¥¸mò<Ççèß¿-Çìûâ1hĆQoýë½c,Õ«ú¨ïë«q[!w±[6õz-wjÜíAª¥2 C0pÄìÎÝ –¨G€"¦Tâ¨Pn,[ ê®"ílÿ}S£ÆzÀÒ¯–‡r6óïôÅJpÊrâvÍ.0¦°âÆõ¸Ý퉬æofßÏü†Ù1 ]*ñ:è:µ¡ÓešëòcÏÄ1û¨çóyj” |¾ð¡sÛ€fã¨fÔôQñk„™fÔÛÃPTßqÅêé|ÔBªo•·<À#¡ú¾gÝÈ-m–^¢o_9Ÿ%°\ï³¥Ù®ì*ºîš›}ÔïïÓéä–?¹Nƒ¯V"½°ÄõîÆéQ^õ²ë±ÝQ6voœýÖ\ÄsãA>ês“ÚhÔÇÒnDn5ê§ðQ“8ÆG¦ÞŒš>j‚ ~pi0ê(ï_ïc©è£®´Ðu#ßѨñ­V«ÙÌŸHe#oT†ÁH…À›Þ úóUÍ—õf³±J²ºx•W¾ÉÓ{¿¯û|Ñ~ý÷ÇÄìxsîÌ~k.â¹Ñ¡OvÊìB^‘êzü>jÐæ··7¼Qõ6ÆõA5A¿Å*ÕŒ:Ά¢úŽ+VôQßjP‘=ÐBìÆ6àÒIË£‡£Qûí5꺞ü¥úÖh‚årQTJÔl6‹ãXs¿¯}j¸«Q_;½í˜^uÔÕ¨?ÍÞ¤®S£~MtÐé2u4êQû¨/8·Û-¾^êmè£&â!XF½;ö¯÷ޱTôQWй>LwÐBí£®,¾¸‘øá~® D°þ°XOñ†HZ9ËÀ]µMgѨ⢗ø¨yÞØòªÆ®9{s¼jo~$8 Œfˆ±¤û}í¯/QÞôQWïm£ºÞýËÙé£~Mtí£NŸÎGmããE޾®o?²Ž°†5¬aÄ^X€Qï £Þ†QŦŒD‰•XêE•e·z:uK\y•U•zj’No6kW=Îó\rYxŸóQãƾù>ÙúMS®;šƒÆK6 aìê¾F])½¸³ÙL²I×Ûãz‰ecNÍàzyÞÅÞØ×Éõ1¿Ÿë£q=6×ÇÂÉõ¡gŸ6Íîû2;s}¼&:÷Qg§4+t®üô>j|¬ä—‰øòÁ‡·Þ†>j‚ ‚`§5êìÔ¿Þ;ÆR½ªšø.l>ꇠ`>êWEçù¨«-ÈŸ&u…xBmÎpnµ6ôQñxñ…Q÷îIc¬^ÕGM|ÆÜÒ`ù¸gâËâ>êôÊG=uöL4¤Úî™ø>ê‡Ä}_|‚ ©aÔEÙ¿Þ;ÆR½ªš ˆ^Ð¥ZÿB6ÝçizÔ¯¬øÐ¨ŸÂGýû¸ï‹OÄ ñiF}*‡¢úŽ+V¯ê£&¢té£6µ¦Óù•F=jõã¾/>AƒÆ›aÔCÐ{ÇX*ú¨ ‚øCtï£ÎScüø¤QÙGýÀ¸ï‹OÄpJø/T“h@ªï¸bE5Aˆ}Ô¢QgZ6?NÏ”š>j‚ :Åé¬5êi<½wŒ¥¢š ˆ?D÷ù¨/t:ÍM®'ÊGýû¸ï‹Oįp,Õæ 9Ø0ì¿PùÉ€TßqÅêU}Ôî¾ÛÜ·óùTc²^¯Ü}õM|¹œ˜TÕn>êö¸³éy½¥»“¸ Ib;‚$sþÈæ5_NÚþìÚƒÉ@žõQ7ì™Xmïâî™HuA5AŒ^¢}γ]'ƒç'­QÏ’Aè½c,Õ«ú¨öL\.S#d!Þívv;?¼v¯Å}}Uí™Øõï´¬Aûˆ–9¨»XæýÁûL ú¨=Ÿ^½;¹¡Óé±üШé£6ô}ñ ‚ø9'­!ƒQÿ3)£;¾ê;®X½ªzò•¬Zß¡»Üå…ëÎf3—Ìo›öL”}Q)–Neø¹Hß‹ÅÂê±h¹ÝnÎÿÏ¢½»‘·U×1#æÅ_QÚ-³,3•·ÒN´\~Lä¶LÓ}ýìš÷I|O’g”SvwŸ¼5WY–ó @ôµëÿhéœ>ÆÁù¢¯â¦ŠOŠ.}ÔF£>Ê¡O5}Ô‚¾/>A?G˜]rqàµéàÞ¸?êñ—ûAè½c,Õ«ú¨ï@hÁ%{¼eóX¯×r§ÆÝ¼T*Á~ÁÀE³Œ¼õʨ߾ïK%Ž åÆ²Å\!-‹Ïû ‚oc(Ëðíʱ0ñ‡ ´íQ™çùU%—‰ 9ßV-§õ–.@w­ÿÄ= CÑê퉸uã\_è}Evý¶¥{úòè!1f_UÿS@< :ôQzoj)™š>j‚x,öZC—Fé%?9ê‘×é€TßqÅêé|ÔBªo•uµÎz>Ÿ×Éä}X¢èû Ü%ð.%––˜Ñ²S9*,Zn/æW%®&òAݯ\ öõJÉpéåy—–¦ûO¸Ù/£¼Z³û¾ÕIþ­¹Üʉsú¶Ò«Zb|«Ïã(ÖpkyÄHѵZÓiý³D]¢™nLu÷}ñ ‚ø9ÄD—}XÎ?Ê/ê÷ôÞ1–Š>êÏÃ\Cµ­¹£Q‹×w6ó'RÙÈÀÝJ§åûÄx&Lïw÷¨•m×ëõÑ(ÉuúŠU¡ f‚@ÔfYR† Ûó9*? d)–[Órn»¹æÆGƒ«–õ¹Ün¯zË«ëÒ‹{œè]ç£ÞŠ}Vì2”§ã©£¦š>j‚x;åÅ:ÇÝÌPëã‘·™Ö¨Ãl@ªï¸bEµmö¨ÑKpæ$iþÿ°Ù$‰åÑÃѨýöu=…=:›Íd=WõuudÃj¼•õ£W-ý¦²§óõìÎ‰ØøJ£®eÏ¥]cKw|¼Ô¨ŸÝù¨§ž_žÏàÒ ÕR‚x‹FMµ ï‹OÄÂ=u°9è8z4[´:Ρ÷ޱTôQWÀ=×z-\àÖî'6`p<ë*…çMñ†HŠ9«¦º>jëOnÔ{­‘ø˜çõ–äÜþº°®â¢‹œo–e¾ïÙJñ«à¹ÀRVL$¨´ÖîÆ–.´ºz?ïkÔAØ›õ­¹äLÝŸX6¶tǧú)Ñz2õð–¤yr8&éqw8~hÔôQôQĈ‘LÞ¼D+¢‰±g,2 ü «TkÔ»ã€TßqÅêé|Ô-áf¨ÕôRó9s…”d¬Òûœ·øétꛜ8dë7M¹>ìhn¼Z­0¬Ëœ]S‡åɪI%–ŸCJ®û(ÞQeðð­×±>QÕ}2óýzlu+×Ç'õ%Æ¿ %rzã\:×Ç\çúÀb×o[Î>=8|ŒÏ\O‰î|ÔøÈ•ç3¸4HõÎj­Q/é£þˆû¾øAüûâ¢AÞrîþãÄù^»?Bïc©^ÕGM´AË|Ôía\­¬ÑÌGý¬èÎG-uœæBª“ÃñX”`ÔôQÓGMcG”iºkq¼GZO¾¶þÁN‰oÔß«µ¯«WõQm` -›ßc *QµÜó‘{&>+ºôQkZ«Ó‡£”V£¦ZÐ÷Å'⇸x§³‹"ªœ=xŸ/ÖSä§ž•Þñ–êU}ÔÄ_l(´Ã$‚#Íœ¯Î}ÔšNRÒGM5A<Æ’±Ë/ŠèÚxžã‡ª›“XY”R}Ç«WõQÑ ºôQûøZÛg'I‡’ù¨é£&ˆç@°»dÌET~œ¸|èeCÆÞ•Þñ–Š>j‚ þç£ÎNûü´7åQgÏÛÐGM5AŒScÉ8ž.Šh~2;'Æ|㿇ý+½ã}ÔAü!:ÎG­ÒcYmD~:• Œº º ú¾øAüV@¶ŠèDj4~iò]{ñm ¶<Ÿã°Œ¶çh[n×çíºÜ¬ÎëU¹^7+£õᦠ×çhSF›s¼-ãíG¯pƒ£gÛw#em„ÈôÚEå.Òež“ðÓ8YÆ·³  ZîãóaW¦É9Mt_TÆ2¯Y•Ì»uÖ‰¾-Ñ}1ÚËúeU8»UuŽR^V¸Ñ+A_™7‰dµŠ>j‚ þÝù¨§z#!eé4‚ât^ŠFMuA5AŒ¥ÒŠ4(´«ˆÎd÷ õ±Ô£ùñ %öjJ¹X–ËeU.jå²›r¹¼®ièûyœÕUû[ãÔê¯âëqjë¼µ¶åýsl^zUu}çAÜ·çóùd2™N§«ÕÊÝ÷PßÄ—K9俣n[›ž7¶¬g‡N’ØŽÐ2WÆC ›×|9iû³kæúxVt磩Æ?C§/¤º¨4jú¨ ú¨ b´Ðt7R~òI7ŽÅJ>f ÙA,½A>Ï EËå9Z•ẌôKôçR«Ç¡)Í_µb¬µ_ý’–áåetÝuiÕ]Ý+2¯¸ "£BåY«ÊFOW¦Ä«j3 MZ–NKýÚ,/éâÌ{ÑÌË‹Ö]õ WgÝ~©GÀ õ¢cI»Ø¾Î;³Ng…åem¥zUu‚E‹„a¸X,¤ïƒÝ«‡p_ÿAžä–I˜¥e}ü Úð@´ÌGÝÅÚ˜úYÑ¡Ú{Nµl*úïß¿zÙnÕ¢š žø–ûWÑ]«¥æØóÝc¦­Ó;Èìjê´ÐéÅR3ÕCZó2ÏX6–êU}Ô“¯dUËq/‹¢¨± îòÂuÝ] mÓž‰²¯"*ÅÒ© ?éìÝê±h¹ÝnÝshïnÞmÕuÌh÷L´{fYVí™øQi'Z.?&r[¦iÆ›÷L|O’g”S¾Úe²q.½gb ÷LD_»þ–Îécœ/ú*î™ø¤èÚGݘëcÔ>ê···ù|^1êOmpßö}Ôñ”HŽZ£^í¯ÕcA:Ryù )ªd>f9•Úì±\ž×Ë2Ýê˜ëR^Œ?ÇêU}Ô÷ q[ìñ–Íc½^ËZvô–J°_0pÄ,#oD=ðjß÷¥G…rcÙb®–x“1²wL e¾]9&þ”¶=*ó<¿ªÄà2‘!çÛªå´ÞÒè®õŸ¸³Ga(Z½=W£nœ ã ½£È®ß¶tO_=$Æì+cL"ž ÌGý³øB››ÚèCôQÄó"Ê´F½9\g™XìuýC¬Ô¡Ù“S|òQ‡Û‹y÷®¿TOç£R}«´¨û¨«z­¶ú¾gÝ-- –(¢o»Þ¥ÄÒï³e§rTX4¼ùÂí]•¸šÈu¿re€Ø×+=ý¿áØ÷¼KKÓýŽ'|:žÏåÕšÝ÷­NòoÍåVNœÓ·•^Õã[}G±†[Ë#FŠ.óQë=Á¥“ôBªíž‰Oà£FÝØ‡è£&ˆ'Æú µhðê+zkê7ø¿Ü­aÔQ¸\žÁ¨ãíàáÇê¹|ÔíqG£‘ÃÛú¨ïhÔâõÍü‰T62p·Òiù.Þô~wZÙv½^ÍeªÓW¬ m0{¢6Ë’Â0ÙžÏQù¡0Ë,R:-·¦åÜvÿrÍW-ës¹ Ü^õ–W×¥÷8Ñ)ºóQ‹F§9u‚òp<%õØ}Ô7ú¨%¦š žËJ‹¾ÊÂ.†úàVê¥Ù“1Éñ7k-PoW½k¿c)}Ô·T\œ»± Øl’ÄòèáhÔ~{ºžÎÂÍfxçm’F•X²a5ÞÆÊúÑ«–~S£Q¸»/³;'bã+º>”=S”vý-Ýññ6R£~>té£6uE§XzÔ>jA£Zâö>jû“ÈkXÚ×LÃR6L £8 #”Û0Â÷^aRÞim9Š9—0Î*Ú‡fäs•ßãnâíFdØd«c)E•½®yí6ê¹|ÔíÑœëÃL Ö§}Ô•ƒ·~p?±ƒã!XX¬§xC$Åœeà®Úú“õ^k$–ì_Å çö×…u]dÁY–ù¾g+Åüœç¹¥¬˜H $iÀ–.´ºº÷5ê Šê?”oÍ%gêþIJ±¥;>}ÔO‰Î}ÔÖ5}ÔôQÄøáÅZ‹– ¯òEOÍ>/¿ÿq¢Ÿèq²Óe|o/õ´ß±”êé|Ô-áf¨ÕTòQOMÒéÍfíªÇ |‹Å¬Òûœ·x4öMN²õ›¦\v47ǰ.svM–'«&•X~)¹>ìcxG•Á÷¿+ĹÔ'ªºOf¾_Ï€­nåúøä£¾Äøw¡DNoœKçú˜ë\xCìúmËÙ§‡ñ™ëã)ñ(õþF®Ý¡ØŽ» eA5}Ôñ OM»^Ü¿~\–]΋ò2²Úu²-¿é%~åX½ªšhƒ–ù¨Ûø>ZY£™úYÑa>ê©ÉG F’@ïC£¿ú!qߟ ˆoCö›w3ŒÌ/ — p¿‡f ›ƒZÅ›2^•ÙaÚïXJõª>j¢ Œ¡eóûq¬A%Š¢–{>rÏÄgEçù¨ór_mòbòQë=ŸÀGý¸ï‹OÄ·QFíÅM»žUnö:œÆ¿šâtÖ28¹Œ¼Kt–í²wÕw\±zU5ñ— í0 ‚àH3çk£;µhÔàÒ²9¨uq:/eÏÄñû¨÷}ñ ‚ø6dðYRÛͰŠ'‘V˜‹òçS€–ëœ!I5æÖdùˆÖ½«¾ã*Õ«ú¨ ‚èÝù¨Aªq/H¥9¤KÜbD£~õCâ¾/>A߯¾¸ø: Ý=•§¸™«Tk+u¤â_ð²Ù“qi÷d4û$–û¨wÕw\±¢š ˆ?D‡>jÏÃí@Ôékš>jóGߟ ˆoCœÒëtº,‹mYlÊ"R-¯$× óüVêÐìɸ5{2â{ó²Obvè]õW©è£&âAuqߟ ˆoCv3éUåî¬éôV—§ÔjÔ6+õ±L/Û»è1÷»órUn*õ`àáÇŠ>j‚ þ]ç£6‰>N’ñÃäú ú#îûâñmÝó³*­Q— okœÏÖS=3Ù¤LͼDkԙ从¶å˜¨ úŽ«T#÷QëÛër9­eŠvsMOõ¡•=Tß-Ñæ@ÆùFQäæ‹ÆÍ\2QcÕju>—ŸA<9˜š>j‚ Úcn2N§ÇSYlÎETžËó)Ö1¨uå¦+gÈP”ê=R‹}•?d³úÈDÝ·ê;®X ÏGÝX¡_í¢2Œ;®Í`|•ôXˆ±ÄõÝm ˜ù|>wûb@Ù|DÆ·ƒñ3t磾Ú3¼úX05}Ô1nxfï•옳njZÿ8QÛ?b›ñ#3¿^œD?_Øøjo²|`tÐéå¢<쇠úŽ«TÃóQ»»æÝÇf³¶[Ò_¡N›-O®¥Z×ûÖ!âgèÒG F}6túBª­FMµ ï‹OÄ·11ŒúTìÊbmtéÒüDqsÖÆÒº©5ñŽ´s㻘íŒ^˜qò\3êÍòBûV}Ç«áù¨ïÚ+x¸V^Ž+\Q_ÜO—Ëå­ñ¯jnÑfê‚>j‚%ôn†‘2Y>Ö`ÒšN—§ò”ãÇÁfüØ.ù:¾t”ñOfÿñ2‰4£Þ®z×{ÇXª!ù¨'ÆúlýÏR)?TtK§ýMŸæ£^·ñQÛ¾cÎçó,˾uFA\¡KµÕ¨»C€>jú¨ bÔ(.»žµ"} Ïû]†çCZ–ùYÒèUuZè–^ò½ñ÷&õ|We¢7z·Äd3Õw\±žº úÓ¡:£þJ£Æ“Åb±ÿ8&â7èÜG}¸Ði”&×}ÔôQÄX‘]v3Ô6µÊíF¿Âm R]„:7u™ÙŒÇßÚ”ô&1jtž:;í²BJ“š>jú¨ÿ³w†ðÊê`¿ñD#•¸h´F*Ñh´‰F+‘h¤‰D*‘Hä¾Û`N“ãñÄïy~^îÿcDx}ÏÃAKU;›aTÔÅ¡ŽOõ9¤Xº¦ˆút¬Åó‰uóaôD†Ù£‚\¾óÓÙ•=Š«(Ÿ+xP%o’õ]7ï磞.ê cL‹cl¬]Tm»ÝÊ› …ÓĽ䳾-ÝÊ©ÂX&‚ ïê÷|Ô®Ëj>5ŸÞ…/Óò6g"|Ô|Ô´<…b>ÄCœñ1¨ÏÇêr®Ê¢º\xš:º4UXå‡J£W7g1jÇnòä‰rj'lSÜÕ5nMÔoï]â²y'õʲLŽÍXê>jZ  zÃkÌ]ÓÇC&UQÔEÎùt¬ 1‹by‘9jºâ±3·R‡i“WMYóW=Þ8O€Ÿ´‘¨Ã#‰ú¼Ÿ¬ï²¸y?5æL„ Öïù¨]Æè¦Ð…ÓüuËQÃG-þ7÷‡AÐ÷Dáî§&J£*Ýó¼4wä|GÜP«|σꪔyæ³ÈiSœÌ—aË|¸é+_Û“t‰„i›£®÷AµÛqõä{—¸lÞÏG AÐë·}Ô×| G µÔÜ>AßÓúÂã$ ë롺\jºª•E-‚ê:ê>jZšèÊö%§wÉ÷uÈ-";]¨LuG|ÜôÈ鹎蟧Úx¶¹]úÕίƒ ðvª¨µc7«Æ1áÙïýµ‰úÀG¢®¢Ãì™Þår5A¨_úŠñ¨á£† OPVòˆš…%èCŽò¡ç¨iI/Ѝûj/Æ‘–¯ý¾:žªð\EquMª,çßñxõÒ_-g*ggÎíäãI*6ô«ëeöLïr— |Ôý¡~ÓG}›3‘/µ9ᣖšûÇ èâÓ žš]”WÉÏ“¨e§%×qÌG–¹èÀ¯Î<Ã,g)¯K±TLWÃÓé°¥¹:-sË¿h^jŠÉ©…Kð™Þår5A¨ßóQ;®K7‡ó5£ˆš/ŠÚ»5|Ô|Ô´0ÄøÒû(áf ŠÙŒì´˜%¨öâ•î«l_I%béª,;è¸(«ÃqsÈ¿ø$Œ¼åÓµ¢Š¥›ó¾Šƒêzz‡Lïr—ÍÂ}Ôüöêû®ã0·?µGÚ嫵j`òn–FÚß0 õñ¨éæ¾Ýn‡îÕn˜ê‚~¨ßôQ‹5¨3™©V9êEû¨³,Ûl6___›õšX¯Cÿ<ÿý÷|Ôôy ®bÄ$®¢ð.;M!ÜáPKÇ5æ9ÒðÄÇ‘¦ˆ:;V½ì´ÎtÝ=wÇŒÕaYûÎìÔñ± ß(ë»,nÞÏG=]ÔÃ5cQÄ^µÏ™H÷åÝn'ÙœüE•Pd.ãgµŠ”/¦ó‘‚mÕAÏé·}Ô2G-·õ’}ÔþnGa3íÅ~¿'ÖëP˜M—,Š¨á£† Ï£Û% Ÿyü–N“J8=ø’X†Á§#Æ<¨Îã~vZgº,†çÄÏ!îvÕ‰ ªë¡JçÏñ.}Ù¼ŸZeŒêpØS”;¸Ê ›Uœl¶ßËZ[æ[´LnAÐù¨ã(’NiZD|µËHf°£‰>jõXb$}ÔÁâ}ÔôK?MSbZº®kÖõ¤6çþð!š*>ЇSºJºQ>’¤>Ÿ[×ôáÐÆr*SJAõ™ã}ѪS•]ªôR‹¥|ÕÙU´#<ÕIÂuLDZ;éšzLïr¹y?µ}q]Œn #6Œ^èK÷Sß÷ÇÚ…ÍçóY5AÐsôQ‹ðù†§K,O‡áEè‘ZŒõA±tZDb)ÆúXüxÔ] Zq¿/œÖfÍž­ùس•XÖ·e­-«®ŽÞV,«v©ÊÅߺ3 h_•xÕíVµªYɵuÝcõ*xòŒ–5}²y©^MÖqÁßKÔ©îßQo³ªENµyײ¶mu«ß-Í­ Q¿ÐÞQ´ß­¥^ñ?¿Wí’ÿ4¬øm³[–üÚ.Åý“×Éò*ÍÊ4¯¾ä¯L¶PÖý#©·n¿ºãSÑ¡ éउ£T©ÞÚQ-K}ïªò~Í×`ýè–òó½mUÈ>”m?{ŸWeìWï½î>¯»Ï·j?eí³ܯn¯å»ós&¿q¿eÙE½}¿}î’µó§ÛªÙ—îÝ»ã Þýî3íŽ[ÿHçä5çcqlΟütâ´LMÓ+ ‡3¥ÉµŽN<çLq²\š¯”^Ç*9µuná4–å£v„õYùŸe¡|PQ_jõG3Ɔz?ÅG­¶5*pYx‚&ªï£.KŠ¥éE!uw *Ä5'—Ê„x<}>óuYNšOòÂgxùˆñ¨¿¾¾Šî…œ:Ó}Ô_§6ë%çSûÒ–+£dJ)%ïö^%§½×”vžz¯'KÞþ½^õ¹¿ª‡Ï}¦½åê¥%á>’~éZ‹w>sˆ%SJ¡]t®¢cÓë –Çêzä¾ žÓ –AuN«Gß(Ó»\nÞÏGý9ê»UfÀXjÙÆ¼êZ¤?¢Huø¨Û±>òv½v¬ÅG ƒÁàEó\>j‚>Xã>ê~8-ꨇói9êZΖ%|†—Ï ƒÁ‹æ¹|ÔÈQCÐkÌG]uIê»+RTÓò¡ZŒG]Ÿãô|ÍÎ1Ÿ9ñ6gâ’}Ô`0 ^4/ÂG Aв4î£.­'NÉQ³ª¢ˆšÂéôBAõ5ã®>j0 ÏÉðQCôrû¨«^P-Ö¶A5-ú¨eŽ:ŒRTgTçEI5|Ô`0 ž‘ᣆ èå²û¨‡Ãéoç¨3™£.º5|Ô`0 ž‹á£† èå²û¨»j0 ÏÅsù¨§‹z¸f,Š¢æÑœ‰I’ìv;ÉfÀ¬J(2—ó¹˜ïåû;Ë<ŒM”ÝG­‚jq ê®;âGýäñ¨ Nó¥ú5 ƒgä¹|Ô*cüP‡Ã> ÃÁUfجâa³ý^ÖÚÜö|>ÓÝÜ>#AS4森„ÆFü(ú9êÑ9Ål‰Ÿ91)Äœ‰‡>j0 ÏÇsù¨§®Œ?ˆT ®êå“é~êûþXû½’Þ¶ôƒBæ·‘£† Ÿëz0¨æ&´Ç>jþd"Ÿ…¼ §åœ‰¾ÌQÃG ƒÁà™øï}ÔŽ°>+ÿ³,”*êK­þhˆkø¨÷S|Ôj[Åt<Ï“?.£† ŸkÜG=£.Õ•§(l>êšç¨™ÈQwá´ÈQÃG ƒÁà¹y.õoä¨ïV™õxŽúpØ_¯ñÃ6!š¨15ýzå'V7'µ>"áTõ5—'Òò6Ö|ÔVN’d»Ýº®C¯À÷Ó4•åz–ÃuÝÝn—e™Ü–Jzí´%|œÃ.Îëõš>²oõ‡>ßÍfCWZÚ–1ë„§sÝÍzM¿ÂÛ¡îG³o³sw„é;¾:Ây®2?òûòßOÕŽÚ¯<χƒÃÿù½þØž‰’K³Î] ïqli!•¢3‡Cßס:‡éÐû÷çv¯u<é¼¥3Îa~~§?öÏåîØ 6ëä™hÁèÛìœ\¯÷G85ÏyngÚ¹ÝkGí×ÓßSûñéeeÛú»óC~ut/\e ËéŠM{¤þIL%cÛöÎÿé}ƒ hP“|Ô¥é£.¦ù¨kŠ¥åÐyúœ‰ðQ[˜b S)x { ½ÎaH·EY.z·!Ï£(òkŠ ÌrW¼µÙ·™ŸÀ"úWFg ïïŽažÓ9Cç¤ü—~nÚgAíl·t§ÔíiûÛmZ¦.Ôê¤Y®Zx“c+Eß8ã·_Oý\ÒpïÜ–Rûõô÷túñG¸ )~ðÝù!Ï壞.ê }Êtímõ¡‹ªÉO¼á4±¼Û*m‹5ý\c>꺶û¨Ë‰ãQ‡qzŽS1sbš©9á£gºïDZ¬š^NG’ß­TŠué+Ë xn¼o–ß—SüFQœ¥Ÿ2P—·±ýð~½[µ=Â#Y5½¼=Âþ 0¸mïXYú3åøÐç˜$×±:oõWºï‹xJ?ó6ŠÏ R¤ÇíyÈOàÍÆ|_˹gÿ\Ó»t€Çß»Ùùv„'œK]œlû+ÀÃópðºñ­ãC\{„ŸýŒ^Ë‹š.°ò/ŒõÇ£¶l•$‰ø•½¢¥9¡ÌضðQCÐÏeóQÛÆ£¶æ¨ëÛœ‰a”¨W^”[?(þa5)8§«àJ¯ý%k¯å÷ucÝŸÂûí˜ÔívkÏ º®›gí/&ÚÄuœ‰}¦ OÇ#u†b¹6´¾¯Cå"¢n›v]g¬Í?Í Ò9•³*éµä)Ý«#ð`;æ_v]u쯃Û~«Ï}Ð2A=Vçÿ WyP§«:[UYPv±ÒëÐÝ_˜ô³Tµ_cT:¯dÌÃÏù,£Óò»}~ø¹ˆüÿÖRçO3¨y^Å7ô"(»}×ëÐ ,½Î%íãÞö;}~x|ÆþÂòóïÎÓ<—s&BÐË⣶ŒG=ÅGí¸.EÔ§ËU¾(¢Îò‚"êÙGíŸÓÿü뻘/ý˜¢k³Ž3ž)êù¨[‡ª5ƒª—ëׇ}¦šÜ§ò¿¶AÐÕ·:TBåÒO")o‘A üòë¿Û‹‚êáþäƒíô}Ô¾ŸOðQ«mŸëóÃããyÞõ[êüÙ_D8ý_“ýGKzQP=p §çMÕû©ù¨Ý ð»e£Ô[kÏžK?—M›>µ|þ.ƒÊÃéÿþS¯*†úãä#íôFüî[}Ô?üž><>m‚úgYî×ò\>j‚>X6µæ ‹\\xòâaŽZú¨]VVUØEÔôR9êÖG½ ®"–n_ôÏÁ j6!ƒÚkßÈRæzŽZ–óµHñMé³!ó—6ò¯í÷u(¨¦rê0ý¼¢–ß ƒZðìôו§ùÒÈž©ŸÙŽ%Û&ŒÜÊó¼·_Og2íÇ'Ž"õ—kßþâ®Ó• §Û :[™uèLQÜ·ú©Žž’xó–£&©õ·úlÿ\ôô©¥ov­àÙi-¢n´Ã¢ê¨#l¶c9—ã88÷ûõô÷Ô~|Ô~ú3ú žËG5}°ŒG­Õ|eT÷Ç£.‡|ÔŽÈQŸcT‹e–;™£þW}Ô_m,ËL5EÔfºû„|PŽïer֌ѵZ•'É•J ‘zè£dªÉ7œ‹‡Â,õ»›æp›é£.W_w9ê¡ìY{„¿™mcòwåt¯×lâ¶v¶‘ ¶eP‹?ü+…Ð2;ÝÕ_fß÷ÇF•±ôs½^'šÏöÊïZž‡ÊÏñQÛÙþ¹èçùX¿Ì ö#ê¯/³ŽïïÚ#üs‰Ž§îoð+¾§öãs;Â?Èr¿œᣆ hY²ø¨Gùà×q zì£vùpš!ÅÒ"œµõ?룤ë£{ùçÔ¬Ca›ëº—ó9¡¬ëÞɡúxÕt›ƒoúXY&Çú˜Øg¹aš¦Ô‹}Ä"ªÑëP¯D¨“Ó­¹}þq¤Í¿ôQ—Ap—£|³Nï‡#£©ôÚ§³ÝóÔNÔþy&Ór|¤Å÷a;öW€*óõu•fúvÓ¯³8¼–‘(Ï7þô¥öèPS /Cyò¸ëãUT50‚½¿Ì Ö¾¯GÔµp}ôê¨#\hGøá¹ÔaúU(°wéNùá÷Ôr|äþa–û7>j‚^®1µz0±(îFù‹i>jž£®Âð"ƒês›£þ§Ç£¦}§ úKø=ü0«/ïwr°Ù ,cöêEíðÑݳ„m9ýö±GMÑË`9ý´Ú¬×ÌuOZFWe¹é-Þn¥bËÙ3¨’y½úâÙéÀ«êÈ#ì:bÄï °ŒÙ«sÿOÎÔÙ3ü½ã£\±A-þô¯9Õ"SýEЮ5êË8jpjóDþß”µôQSD]Vç0â/Qçbô¼âöQƒÁ`0xv†‚ —Ëî£ÖçIäê‚êþxÔƒ>ê•Ã#êãù"‚ê EÔyé ƒÁsò"|Ô…0×¹ŽÃÜþxÔúˆ.û} V ÌBÞÍ„Hû†aÎÄû™C{ è³5棾§Õ)ùñ>öQ‹õ…"j^1¦ˆºèrÔÿ¬ ƒÁ³ó\>ê颮‹¢¨y4gb’$»ÝN²9Q‹*¡È\z±Ô*Ì“A¯Õ$uwÁÉÛ z’Ú•9êCÈ#ê0Šôõ¿ê£ƒÁ`ðì<—ZeŒêp؇a8¸Ê ›Ull¶ßË<÷rÔ;AÐû¨õr..9RÙãu룦ˆºŒŽ<¢ŽÎÑåÌsÔ>|Ô`0 ž•çòQObŸt¸\ÕË-ÓýÔ÷ý±ö{%ú¶üq_F7jg½^Çq<±cÉ⣖—¶4ׂê,›2µÌQ_adæ¨á£ƒÁ`ðLü÷>j}Æ[•I–*êK­þ¨%ÃðQï§ø¨Õ¶fƒt4v»]š¦ßÝ)‚tY}Ô…”¼æ¨pú[>êètŽxŽ:ޏºòᣃÁ`ð¬<—ú7rÔw«Ìˆzä̦š"jz ø¨;ËG¥yQReø¨Á`0<#/bÊ}Çs>ã­é£.{>jÖæ¨¹å#ŽЍUŽ>j0 ÏÅsù¨1g"}°ì>j®¼ ª -¨6}Ô2G-‚jéúX»ŽSW"¢–(No9jø¨Á`0<÷|Ô*SýV9j‚–¥1u›?æå¹æ ÎÆsÔ†Úu),)œ¾$qœRD]”¿á£ƒÁ`ð|¬û¨E’'‘÷#ä¨!zZvµ¼Ú¨˜ZÕc>êkÑå¨Ùš?™XÕÑåG<œŽ®YÎ]ðQƒÁ`0xNîù¨éŸü–÷f>j‚–¥>j~µé‡ÓÙõôQ»Œ"ê8Nã8‹¯ytÍ)ð–9jø¨Á`0<÷}Ô2®FŽ‚ è¡ZÏQgZŽÚº/T·>j—Å×,Nrñ*ò²‚ ƒÁóòZüÙTÞ£† èiÙ}Ô¹&é ÎQwãQÇ<¨îrÔl]ÕÍ5-(–ŽÓ2N‹¢ªá£ƒÁ`ð¼Üº’®ä¨!úl>jf÷†ÎJér4ì£V9j¶v™ÈQ§å•Vñˆºä®ý>j0 ÏÈ=µš9ñ­rÔÔ#ß÷]Çanj0 ÏÄ=µ*ÿ½µ#¬ÏÊÿ, 僊úR«ï˜H>êýµÚVç ä¡ FTCÐeóQƒæ‰h:¥Eaø¨£´ì‚jÍGÝ4b=G 5 ƒçäþxÔ]ùsÔw«Ìˆz{8¶ív»U•éh¨Ç!zNc>j98çàÐyƒ>j-G]ßõ‘7ðQƒÁ`0ø}x.õtQgcrD;ûXº¨ÅÉ´m#Âiby·UÒ·¥UÛ­G÷zjÿxŸ_Úwú5æ£V9ê¢ §óÖMÊYÈ-9êêÞG}… ƒÁoÃsù¨‘† Ö˜š~¿ËÂÁ?žðQ_sø¨Á`0¾ï£.ᣃÁ`ðÜ<—9jú`ù¨UŽZÛ¹fÿà5ÅÛÇ£† ƒÁoÅ‹ðQC´,Ù}Ôò*d>œhÍQ÷Ç£† ƒÁïÃðQCôr=ôQ« ºÐ‚jŠ·á£ƒÁ`ð>j‚^.‹ºì®BÝÉ™z8>j0 /”ᣆ èå²û¨UyoÄø¨Á`0¼P^„šzäû¾ë8ÌíGí¬¸gåòUZ50 y7K#ío†úxÔz;rùÛ{AŸ-‹z,œ~˜£ÆxÔ`0 ~[žËG=]ÔÃ5cQ5æLL’DM nNþ¢J(2ßn·có-R#ª}‚ž“ÅG-.;êbs ª)¢† ƒÁ å¹|ÔÓóÀ‡Ã> ÃÁUfجâd³ý^Özl¾EÏóh÷'ö ‚ Aù¨»ˆ·PW}Äø¨Á`0¼PžËGmŸ@\cLy9zêå™é~êûþXû½’Á5ÔôY|Ô¥¸ìtÅwA5|Ô`0 ^(ÿ½ºõ*¯îËòAE}©Õ¶g4>êýµÚÖlp»ÝÒqøîAÔÓ˜z0G]t9jQë9ê >j0 /ƒçòQÿFŽún•Q?ÊQ§iªRÜýDc>êFF¼"æU×"õˆb 5 ƒ—Éï€îŃ«,aùf³Ñ³ÍÄTbß jz•ú¨o?íyÖöÑÏQÃG ƒÁà…ð\>êé¢Î0Æâ8nõ¡‹ªQ„LÛ6"œ&–w[¥Þ¶HPCÐ 5æ£n†rÔ*¨æ#[ÂG ƒÁàò"ƣβl·ÛQ ÌXXvuU–77uW >j0 /—ᣆ hYzà£@>j0 /–ᣆ èå²û¨+-ªÖžPìû¨rÔëû5_ÂG ƒÁàù>j‚^.‹ºª*-œ¾ÅÓôß7|Ô|Ô`0 ~#†‚ —ëz0¨îù¨÷ðQƒÁ`0x1¼5õÈ÷}×q˜ÛÚYq9ÎÊ嫵j`òn–F~×C}ê>j0 ¿Ï壞.êáš±(ŠšGs&&I²Ûí$›“¿¨ŠÌ·Û­¾-m%ïõ2ØV@ôœ,>j ¨oAõý#ŠOù¨kø¨Á`0<;Ïå£Vã‡:öå®2Ãf'›í÷²Öú¶½ÈÜŸÜ‚ )óQ7<¢¾ ž×óQ÷sÔ<¢.»™\ࣃÁ`ðûò\>jûâºcc6Œ^$L÷Sß÷ÇÚï•èÛAp¹\‘?NAàOìAƒzÎGM_Áïú¨ïÆú€ ƒÁ3ñßû¨a}VþgY(TÔ—ZýÑŒ±á£ÞOñQ«mÓVëõZ¶C5ýP6õÈô.´õQSü\45|Ô`0 ~WžËGý9ê»UfDmÍQÓí^ú¨/—‹JtCôœžº ƒÁËä÷÷Q+K†)KX¾Ùlòüö‹€˜Jƶ…‚^«qµÌQZŽ:Z9jЍSå£nࣃÁ`ðÛò\>êé¢Î0Æâ8nõ¡‹ªm·[Ú¶á4±¼Û*õÆú8N*G½ÛmeO èŸÑõˆñƒo5 ƒÈ‹:Ë2 z)f¬?µe«$I<Ïsœ-Í ez>jŠÒ15½Jõà(üú“ÃG ƒÁà¥ò\>jÌ™A,»ºËNwW£¼ ª›>j0 /’çòQCôÁ²ŒGm>ÈÿˇrÔÂGÃG ƒÁà·ç¹|ÔÈQCÐËî£ÖçIäê‚êF÷Qæ¨á£ƒÁ`ð[ò"|Ô-KvuцÓêŠD—Ÿ,‡ ƒÁ‹eø¨!z¹&ù¨» NÞÕü‚5 ƒ—ÈðQCôrÙ}ÔE«\\r¤²u룮ࣃÁ`ð›3|Ô½\6u10tž ª›ïú¨ù>j0 ÏÏ‹ðQS|ßw‡¹ýñ¨—ã¬\¾*P«f!ïfiy°°7–5ÝÄcÞf#§’ è'óQ7<‡\HÉkNMgƒ>ê6Gmú¨ ø¨Á`0üF<—zº¨‡kÆ¢(jÍ™˜$Én·“lNþ¢J(2ßn·ú¶išR .³[žçõfW„ 軲ø¨ï®B<¨V)jþþ[>ê+|Ô`0 ~žËG­2Æu8ìÃ0\e†Í*N6Ûïe­õm)À®ùíš‹¢kÌBA?”ÕG]H=ÎQ—r|¼v©û¨ø¨Á`0üN<—Ú>¸.ÆØØ´à½5ÝO}ßk¿W¢oëyžŠ¨I®ëNìAƒ²ø¨õëŒåCÄÔßõQ7ý5|Ô`0 ž‰ÿÞGíë³ò?ËBù ¢¾Ôê;f#R†z?ÅG­¶ULïx8è8ÐæAXÞ‚ )²ø¨ÕuFåƒÂiúoÌG}5|ÔmŽš/ᣃÁ`ðü<—ú7rÔw«Ìˆzê è^<¸Ê–o69a„1•LÙ–Bkõä#AÏÉâ£î®3¹æ Î†sÔ<¢.ÛQòŠfØGÃG ƒÁàùy.õtQgcrP;ûXº¨Úv»¥mN÷FðзÝï÷HËšž·‘¹2‚ž–ÝG]ˆxZÅÔ*¨nžóQWðQƒÁ`0xf^ÄxÔt«Ýív3ÖÚ²U’$žç9Ί–æ„2ú¶~S M틚:‚~ª>j~µé‡Ó<¢~ÆG]ÃG ƒÁàÙy.5æL„ ÖCµž£Î´5|Ô`0 ^"Ï壆 èƒe÷Q皤ƒZ >j0 /”çòQ#G A,›Z„Ù½¡ó„ø³ ðQƒÁ`0x‰¼5AË’ÕG­›=ºp:MÛ'ᣃÁ`ð>j‚^.›šÿxϵu¦ç¨á£ƒÁ`ð>j‚^.«zrŽZDÔ>ê>j0 ¿ÃG AÐËeñQ‹ïýQ>dPÝÀG ƒÁàeò"|ÔÔ#ß÷]Çanj™1ž¢Ãa†áà*3lV1¶Ù¾™µVõ=ÏS3Pt=±o jÚxÔ™>tÞ3>ê¢ ƒÁïÀsù¨íˆëbŒ)oFO½5ÝOeÎy°}³„îæ[Ï#p]Wf­¥èŸûAР~a<ê¦ï£.ࣃÁ`ð»ðßû¨a}VþgY(TÔ—Z}ÇlDÊðQïú¨•h¯·Ûm*†êÛ–w„ hЬãQÝä™´S‹˜zt<êv”¼qu 5 ƒçæ¹|Ô¿‘£¾[eFÔZ 5è CuûÈQCÐKeóQ uAõÝ#Š |Ô`0 ^&¿¿:‚±ñ7,aùf³¡=Rÿ$¦ÅÛíV§IÛ­'“Õ |Ôô =ôQ·g3åûh¾ï£îrÔðQƒÁ`0xNžËG=]ÔÆXÇÍ£±>tÉá;hÛ¦ ¡åÝ––»ÝNîu¯2Æú€ WÉæ£–âÁv®Ù?ºˆ>j0 /15Ýj)Ð¥ø™±þxÔ–­’$ñ<ÏqV´TÞìÖÅ­y¹e9ÝÄ15½Jvµ¼ ™'6¦:ƒ ƒÁ à¹|Ô˜3‚>X}Ô*¨.´ º ƒÁËä¹|Ô}°,>ê²» uÓ‘gêáÄæ{>ê& ƒÁïÁsù¨‘£† –ÝG­Ê» :W9êé>ê>j0 ¿ /ÂG AвdñQ…Ó9jø¨Á`0¼†‚ —Ë]…´ :òQ_->ê>j0 ¿ ÃG AÐËeóQ«²X«øÑÀG ƒÁàe2|Ô½\u).;]ñ]PÝÀG ƒÁàeò"|ÔÔ#ß÷]Çan ÃÁUf¬bl³}3kÝ1ù`‚¾«‡>êÛO{^§µ}4ðQƒÁ`0x™<—zz6˜1¦¼=õâaºŸªœóXŽZÝÁ·ž÷\¯ ²Èæ£6rÔ*¨nࣃÁ`ð2ùï}ÔŽ°>+ÿ³,”*êK­¾c6"eø¨÷}ÔJ´×Ûí6MSK‚ž“ÝG]Ã'6ðQƒÁ`0x™<—ú7rÔw«ÌˆZ+¡}a¨~ºWYôÐG-/>ÝÚ6¨nࣃÁ`ð2ùý}ÔAнxp•%Þl6ò-EL%Š·Û­œò¸ß+DÔô Ù}Ôƒát5 ƒËsù¨§‹:Ëã¸y4Ö‡.ªFa3mÛt!´¼ÛÒr·Ûɽ6…5½DvuU–77uW©ï£ÞÃG ƒÁàÅð"ƣβŒÂ`ŠŸëGmÙ*IÏógEKåÍn]Üš—[ß>jz‰ø¨Ѓ ƒÁ‹å¹|Ô˜3‚>Xvu¥EÕÚŠðQƒÁ`0x©<—‚ –ÅG]U•NßâéBx´Œ5|Ô`0 ^Ïå£FŽ‚>X|ÔƒA5|Ô`0 ^,/ÂG AвdñQ›átÙ]yø¨Á`0¼L†‚ —Ë⣦€úTß?¢ØÀG ƒÁàe2|Ô½\“|Ôå#5Æ£ƒÁ`ðB>j‚^.ø¨Á`0üOñ"|ÔÔ#ß÷]Çanê.;Ý]ò6¨n¦û¨e!-³ŠªÁG ƒÁàyùý}ÔAнxp•%,ßl6ò-EL%Š·Ûm–ÝííóÖóz…='‹Ú| ‘ÿ—å¨}ÔQ×M"bi9ÿ‹ÊQÃG ^;âïµbŒ)g·Û™ÞËŸp–¥‡Ãa½^Sû´<ÝŸ¡ÿz{å²äŽ?üržËG=]ÔƘ>ÇØXº¨…Í…˜3B†ÐònKKºvɽVJ’„*ŒeÂ!ú®ì>j}žD®.¨n¦ø¨]Fõ5-®i§%-EŽú5xYLá´ä<ËÂ0¤Û\w#~Aûžç¥i*9Ë2úNí÷û÷Q•·%opüÁà—ó"Æ£¦ ‚/š±þxÔ–­(N¦«Šã¬h©¼Ù­‹[ór7÷~lÉ¿¼CôáóQw® N«+*òAujø¨EŽúšü%‚jþdb5xaÜËÖžN'ß÷%Ómˆb`™¢ÛU–ZUÛR溞øÓªÙ>Ý+Ë ñe£¦\Ñ ¼ï—â)ªwé÷*cò'-)Ž"•å¦o¥,íÏù¼¶ìc¡e­Õm}7ÒÎ`?Ÿ8Î`ðŸñ\>jÌ™A¬I>êî‚“·Aõ4µÈQÇ×ìzÍã$¿¦ÔPE•á£/‹{ÙÚ,M) •ì¬VôEÁñx$ %±Úv¿ßÓFZ~×>}Ý( O'•©Vu¨ žéûñÄu Ëgu ÒªõšÉúÔŸ8ŽyM1wƒ½?!ïðƒ},´¬õ^tCv•çÏvûù&Ÿ<Èsù¨!ú`ù¨UŽZ]‹òVÙ@ŽzÐGí²ªª¯×ŒÕ ªË.G 5xAÜ÷S Ýet)°L’D2s]úvˆõc®ÚV¦ iÉlŸZ H•Özž§ÒÎ…pQÊméǃg!zÇnÛ\1ÅäÇÃ!“1y×þXd‡Í}”^q}Ù¶ÃÜ\tƒR» ·3ØOËñƒgç¹|ÔÈQCÐËæ£.†Î“AucóQ×ÚÏS¤2¨¦ˆú–£†¼îyŒ³¬ËQ‹Œ±*§ Sæ»rrÛ:†WYgÚJ>UÄs¿]}åoTÛ È{œª˜|³‰ãhb,ûXhYë6Àný–ÃûeöóM>;0xᣆ hY²ú¨ )u_Ñt6棎óêzŸ£®©<ÎDPÇל?™5xilú¨…‰‚KËsëE®ÒÑ®«¶Í…÷Xå¨íïE_.×u$Sý\|%õ:] Ûg)îÙîÒÈcý™²…–™§m»hd¸þ`?Áàwfø¨!z¹,>껫¿¥ªuFõu›£AµÈQðQƒ—Ç*[+ÝË@¦ÒðpŸ1¦0ûp8ÐR÷-Ó?iËSkfî·¿ß—ó9?T³”¤·ÛmeîO>¨üÇʃ}Ÿo™ÖFQ$³Ü¬óxõǾªüæ£ÞïO"m.ÆïòÍúƒý|“Ï dø¨!z¹¬>êBÊÌQWUeø¨ËžÚ>jî÷ púšGZŽ>jð‚ø6µ#Æ£Ö|Èz–¸7¶†Ú–fWŒõ‘w…zûTx<Ök&ÆÈbü©Ã®}ÑèíèM׌Q#²PÏŠ+¦µj¬éú°ôgl{å* MßyŠ“eãç®zýÁ~N<¶`ð, 5A/—ÅG­_gÔ("¦æ9êÿÙ»VvÕu(øŽÄVV"±•ÈÊÚJ$»%Y‹¬Db+++k‘H$w’Õ.B_t¿n=ëÛ7wNH“´ô1¦Éµbû4ßäUÐGMü·pŸo™˜˜xBü>êÒþü„±ªï5ç£Öy¤=óQ¬u¬BnWi“—KÏÄLËt#7Žc.õÂ`|3|ÔzŸqgù0?OçyS£u~ç£ö|ÿ F}ÈCª+Ú¦šøÏ`®Ð·Z ƒÁøZ û¨M©.R=ÆG=ó £Þ¥õ.Í“C^”g0jú¨‰ÿæÊƒÄÄOˆ§òQ‹b<&ÖëXnçGmÚ¬»]çJˆë˜,nÎ`0ÆÄ€º¾ÏŽƒ:ïÖ¨ÕGÛUÈU£>ƒQƒNg6Í‹Ó9ŒŒFM51111ñTx*õðânøæzîü¨A}ñ<͹³þvŽYä4™xîk% ãk1ì£.-ŸVN­¤zŒzæygÛƒNg6ÍóòFM51111ñ„øÿ÷Qëœíâ–LyQÑMò½ŠqËG½zè£ÖÀ^/—Ë,Ë܆äµhú¨ŒoƵ¹Û4ét>ÞG}¾lÓ\èô6ÍŠò¼Œè£&&&&&žOå£þ úî£6£vrPad Õ2Èß' }Ô Æ7£ßG}‘LW£Îoõy¤tzké´hÔˈ>jbbbbâ)ñóû¨ã8Ƴ¸ó£Z¾X,°GúO`ä(–‰4û¶õè£f0¾Ã>ê qPwkÔ7õ¹é£>€Nç’§J£¦š˜˜˜˜x*<•z|”v5Ò4M¯æúpÅ@›±íµ¦Ðò´E†¡ìµ*« ê§šÁø~ôù¨ £¶4»1užìr9ðQ[º¦ÓHsú¨‰‰‰‰‰§Æ/15žµ ÁÖäÜœz`+ä f³HÕ›]¹¸/÷Õ™Zæ»–¹õ Æ—£ÏG‹×üûfö¨ét–}ÂG}1õÎÒé]¥Q›…˜é£&&&&&ž Oå£æš‰ ÆGŸºÖ¨ G£ÎU££ë£6êê/7uL51111ñ„x*5ƒÁxãèóQkÔ Ã}ÔžÌG}ÜíH·ûcaÞL45}ÔÄÄÄÄÄSá©|ÔÔ¨Œ7޵¼7gùRm\c棣Þð·Ý§Hó¢ £ˆ>jbbbbâ ñKø¨ Ækź5ižeÓ’¦FÝé£6õy.ìmš¢e•ôQO‡é£f0?¿è£žy†QƒvƒQÛU„aH51111ñ„˜>jƒñã1è£.;§Îí£6Œ:1\:‘m€Q—ôQO‡é£f0?_›z”z6³Œzgé´I Õ¨é£&&&&&ž?¿OÏÍf³X˜'éb±Øn·_¨dü¢çnI<¬ç6öûýe0þl ÎG]ÖKçb§¶œºK£îöQzoõé½ùßõ…¡YΉ>jbbbbâ©ðT>êñ±\º¤ ÀñxÁþl%«+ö•‹Ãðl#\.eÑFƒ1&†|Ô6jR}÷ŠâµçÍ.V£Þ¤z‡mé£&&&&&ž?¿Ú÷}eÔ0\×®¥(ÔWó7›µçyØPµeUž±³(,ùÀQ½¹¬“(«(ÊŠŠËå2Ë2Ù ÏúeüÖ2o}ÔåíÝÄ\}ã}ÔéÞ0ê½MËZ£¦š˜˜˜˜x*<•z|à)¹X,’$ióêõzÇ4ÒÍz-™Ûí ƒ…u)Ê3žÞËe€T7OSC­]Ý[5j<¸µ!Ôæþ“Á` ÇZÂí±TõCµhÔûdkIµIË""ú¨‰‰‰‰‰§Äÿ¿Z`Uƒ%S^TtS7Ð70^ß÷—Ë¥ÈP¾ï‰¶ŒŸJæ|>oso´•¦i°X¸©úͱ•–¬û9kÔðå]f0þZ û¨å.Ô~9qŒÚ¯5êt¿•5EôQOŠŸßGíF–eQ%I"ÿtI¯beénàÓõz…!žÚnæÌò{z5Øûî ƒñÎñÐG­¤ºtHõXõålé´üY:\ÒGMLLLLjcl ø¨Oõ]¨^Ž<×—Çø¨E£>¤F–ôTkÔôQO…ŸßG½^¯ñ ll–ÛmEú‘L¦‡tÝòQc×V«•dªòŒw†‚7ëµ¼¢ˆL-vÿjçúú-/3r®c| û¨5¿&Õ…jÔ£|Ô¢Q[:”>jbbbbâÉñóÏGî$ÏÍêÃÿæs³Ù¨°Ü7רuß\WK•å5FÔDZ7›¡r%ÌØûÅ|.}å|Ô ÆWcÀGÝG§;4êNµï]D£Nw’¢’(¢š˜˜˜˜xJ<•šk&2oÃ>êÛ]È!Õŧ|Ô5>F]kÔôQO„_ÎGÍ`0ž?†|Ô*!ÛOÝ?Fù¨E£>$†NÛôt*À¨Kú¨‰‰‰‰‰§ÃSù¨©Q3o>ꓽíÔÙw¤ú>êšN#-ëƒ>jbbbbâ)ñóû¨ ÆËź¥Q—}uŸúb5êCr°ªQÓGMLLLL<~E5õmãÉcÀG}:UœWïEúŠâ8µaÔB§mº/U£¦š˜˜˜˜x"L5ƒÁøñx裾 íM™Êö1ÖG}9Ž{pi›&¨>jbbbbâiñ+ú¨©Q3OC>ê–F­¤zœZ\†N§6ŶQÑGMLLLLŽ¡ ŽÜm¯vÉK{^=†}ÔtºòQ_µ6õÁÒi“b+0ê’>jbbbbâéð{û¨]÷bå/Ñ?ÁÙÜ…ÅͺäÖ·à–×2hN>½§/éºÖlJ²rÝ«õ0H%–¯V•¬âX~ž>ÒEÍíQ›ðKkhÔÈ×þ£ŸB}‘ ¹½ZJ/z ø¹x6ëP5à«wegYÉ][œÏýa‡ŒÆ :(pÌ*³¾­ê=E@ªÛ{š”©_=†}Ôç“ Õ¥3—Þxõ%5\ºúÃm+Šcú¨‰‰‰‰‰'Ä/ê£RÝ—j4|ÈFݲf`ì¢r9°°GWmÖÂ8>JD¶Ûmƒjº>g<î]'0*Q<÷ýöŽ(ùĆšùÐG ¬ò²öS|ú°¨Áàò/¨MHìì‘<ÞmÝÝD[éì›jüårvããºs½±>jÿ FÓã1Í2¤¥Ñ¨ã’>jbbbbâéðßñQƒÙ♆•z 2©$\Q·ê»~Þ÷y ¶vÍøÞE~G·ewÚdx8Ü1H{_:û&NéÅbnl÷ž~Á‰ÍxªöQ»¬ÚyCq¤ÚŒR÷Ǭú˲Â2jú¨‰‰‰‰‰'ÄÊG=õjª"-ì©rc·¼+/·›«èÚðQ£ÂÒ¾©'•´·!·¾ªÛjI4:ÆGíöó+uÝ.ˆ=z«¯Rj>H/(PgënÜiÔ³Q5¸´!íP®Ý9Ô¨_=ú|Ô—Ëù&S—Í?Æø¨=«Qï³bÌ%-ÏgS˜>jbbbbâéðòQ_.xR«F}µÔ.MSw^Ž ¶\ t‘çê7Ö³,s=Én£òŽ¡°qT"¼¤]Ä ‹8Â"Çé¶øT^ué£VŒ ·Û-R×G­†ÕïÝvë¾±¨ùè¡ï{’o$Áý¾s®‡>êvßP-*—9÷´äÂ9òôQ¿A<ðQw’ê±>j0êëþXì³2±iq:GñŠ>jbbbbâ ñ‹ú¨GFc>jyAO?Ń”L^-OVÖ ^'3rè´ò2*DªL²­$ã`†–¨£¹ÎJ<Ï䀦‚Í “ìžë£g~솢†Ëö\Z`ÀG-íº3Ÿ¸û"{.a°^wÏG­s}¸ÛÞÏõÑì[ºß£Â¹šÏ÷ê6>Õwž1>jÏŸFôÅBÐoú¨‰‰‰‰‰§Ãïí£ŽòþýDÆ×bµ2/…ýTmœú=bÀG Bí¾Ÿè¾¢8ÊGmìX×Äp鳤ÅÉ0jú¨‰‰‰‰‰'Äïí£<»Ã0¤úýÐ5$¸fâ{Ä(õék>j«QçpiIËóU4jú¨‰‰‰‰‰§Âïí£î‹ tyƒñãñUõé¡zæFX:-iqº†« }ÔÄÄÄÄÄâ÷öQ3ŒIbÈGݳ¼ ’Oø¨:}1i~±õ¦¤š˜˜˜˜x:ü—}Ô ã—âkóQ#c„ÚÕ¨/V£¾€QÓGMLLLLjƒñ{Ñ磮5êÒѨ {ã)¤XúÜöQ Fª5}ÔÄÄÄÄÄSá¿é£f0¿|Ô=Æãúí£—–Ôø¨cú¨‰‰‰‰‰§Äïí£nÌGÇ‘.#ÒWþSõãеWElÏÈg—_<»9·5 {ÖvqóÛ> î+é..êâ–Ç7E‘7›ùž™»»orÜÃ.sãc_>{ܮΜ՜Üã-cÀGÝ9ˇ¹ÿØ[ÐHµÒièë¹>è£&&&&&ž ¿·Ú¥‹f9¶íÖ]!q¸üȈãØíðl6sW9®Ø·Ëgãá¶èU»A誅8dÕ•«åÌišöuùrÚxž'dø =×vq¾u®ÆÈxéöQ×êt}7**R=ÆG-õîhè´¤9}ÔÄÄÄÄÄSã÷öQ·U_o6k¬(Wé½ZVEQc½?Ðc³fâl†TÖ@læ8Ž$n9Q$”5ƒi˲€BA;fé³i= Û+!Ê:ƒîºáÕž6WQìØöãð-†î—A_ÉŠáá‹$¶û|­gùFÓè¼JÜhH¨;²èä E‹$¾_ ø¨Û/$šÿŠ.ºßG-tzgÓ‚óQOßÛGíj§»›°_P8c%€ÅÆà–×2hNW0•Nˆt]+« ŠB\ÅbÓjqm`eï ”I’(n÷°Ñzš6[ÇVØépcPÐØìém‰ðÙ¬¼WƒA}ñíkOÐó>›‡,Ü–_oí…Û´v{Çòsüá.œ£¡u¢Bw ÂxƒèóQëäyww¤šTóQû¢QëßM£¦š˜˜˜˜x"ü¢>j!Õ}©FCVÒXÞ¯?,# WéÕÂ.+Øn· 36XnƒF"Õœ­åÀWK;Ëzµn¥ }>êÎÖ,"m…ᣵyKyaÑøºepqë‰CqÂ:=Îͽ¨›6Ýé0¿ö %Ü~2Þ#ú|Ô÷tZïH8s¹55êõÎÕ¨O×%}ÔÄÄÄÄÄ“âWôQ;úrÁ3]LW«µ* WÛ†[,qfùÌÄÍÕ¶ AžW#± 0ü©Ø*ðQ°¨rÊšE»”õ¡F=“V«Ög5tö¼]L€Q¼­j½Z­¤ÿŸÕ¨‡÷¢óSaŒ ÀÆûüH>Ïx•èóQwjÔEEª ñv}ÔE¿zkÕiIóòFM51111ñ„øOù¨±§^ÍÜÀ-…]#UnÜ©ôv6weÕår ÂÐн‘ã¾ÙIžïÙïƒÖ‡4ê.u½­QÛŽÍñ]kÇ4¤WM)¡o#^»”v¿þÔÕ¨q(0–‘.i?©Q¿wôù¨•N뽨¨"^=ÒG½;^”T«FM51111ñTø}Ô㣡QãI­õÕrË4M]ÒÔö«unˆºÈsµHÏíÄq7'3êqmÒW;1räíE‰Nývá´èâMíâÎÖGjÔëõZÞ1Dêú¨ s>÷µc®’ Z+ùs}uýÝuú¨ÛMƒ„£ò³õ±kÉ ÊZ½§úýbÈG]vL'¤x¤ZéôÖú¨—ôQOŠ_ÔG=2óQƒšºÂ¯%½ÿÜI3°ëàÉʺA)e† åŸf®ÿºÓ;IÖó\×°+ç^{f·EcC´îÝ·>ÒGíÌõ±lëÉfªGvë{— åXuš@t®k—Ò®s}„Î\è¿ç™1•ø¾Œ°³‹ù\†3œëãýbÐG]JÈ=§fÓ¹L÷ÑШ÷ÖG¶}Ô•åãRiÔ}ÔÄÄÄÄÄSâ÷öQGyÿ~"cdè|Ô?%ç£~ÇðQßÝ… ©V‰Ú”§š˜˜˜˜øñ{û¨BÔÔg ö/vÍÄõOÕÆ5ß2}Ô¥D[£.ú4ê¶:»ÜiÔôQOŠßÛGÝA, Òiã—¢ßG}rï3:ˇåÔyùyõŽ>jbbbbâ'Àïí£f0“Ä€Zï3î, Ó"Sר951111ñóà¿ì£f0¿ý>êúTT¤ºtH52>Xš>jbbbbâ)ñŸõQ3Œß‹u}Ÿ)u^kÔy¿F}y¨QÓGMLLLL<þ›>jƒñ«Ñç£Vº[«Ó÷¤º(Fú¨wõk‰â£é£&&&&&ž?¿º^ø{>›Í‹…, òÙhÌÞ<²$Ž š¿-ƒÁèóQ—ænÓ¤Óùš>jbbbbâçÅSù¨ÇÇrèZ!Çãû³•4VYÒó¼år9~[ƒ!Ñç£îÔ¨sG£飶uõg5jú¨‰‰‰‰‰§ÄÏï£ö}¿sñ¾kÏ }W3ÅñdÊBÞWGy–õ%8ª7—)‘euEYiQk£FÍ`|6ú|Ô¥üÛ qPkÜ4êhŒúL51111ñ3à©|ÔãOÉÅb‘$I›W¯×k<¦nꥱ?>>¶Û-ž¾,èzÙ¢3ãñ½\Huó45ÔÚÕ½ÛŠ45jã³1䣶4»1užì“>jW£¦š˜˜˜˜xJüÿû¨EV5X2åEE7u}ãõ}¹\Š %áûžhËHñ©dÎçó6÷F[iš‹…û‘ªßØ[iÉö¶_ß[ãOÆ Ú5{Ôt:Ëšõõ¹­QÓGMLLLL<~~µY–EQ”$‰üÓU+KwŸ®×ë( ñÈv3g–ß h×Ù—Ã`0†cÈGmï…£QçŽFðQßkÔ}ÔÄÄÄÄÄÓãç÷Q7½õêD4êó5¤š˜˜˜˜xRüü>êõz¥Ð`¤Ûí6Š"ýH&ÓCºnù¨±k«ÕJ2UgÆ3; CÁ›õZ^QD¦– ‚»ïv€5ƒñÙèóQ—–Oß“j•¨3Àñ>j¡ÓH ú¨‰‰‰‰‰§ÆÏ?5º’<ŸÏg³ó¹¿ÙlTXî›ëìºo®2å5FÔDZ7›¡ò4MåSìýb>_.—Zž5ƒñÙè÷QwLšgÙt†äN£ôQ'Ù\zŸ›´¬5jú¨‰‰‰‰‰§Â¯å£f0/}>jƒÌà½ÛGÿú¨kF}VR}Ó¨é£&&&&&ž¿œšÁ`<Œ›:w§Îû”DºúË.V£Þ”ôQO‡ŸßGÍ`0^.|ÔB³ûHµë£îШýjÍDi±|Xú®è£&&&&&ž?¿šÁ`¼\ ÎG]ÖKçb§¶œº¥Qßù¨/ u­Q 5}ÔÄÄÄÄÄSáWôQ·—€a0O 5î3õ|ÔÕM¦&ÕÍWÅGò>j€$«,ôQ?¦šÁ`üx4}Ô§S—F]8u~§QŸN¢Q§4êKB51111ñàWôQS£f0ž<>jѨ·ÛíídÈváØ?”Qo“Z£î™ºš=O|ÔÆGÍù¨‰‰‰‰‰'Åïí£¶kŒÿ³É?ß÷â8’Ä@ù¯´bë—¶|ß㸽hã§BgÀvû/ø;պʲ,kdîv»™ÓN„(Š<«Õªo§pþl6›«]#»†áú©íºó3^7>jœ^•çK-P_åý|ÔV–´à|ÔÄÄÄÄÄSã÷öQ»ÔNÖ[t—n.?>[ ·ò0t•Æß[\5ë2‘Ah‹8\²DÎÅFš¦íòÈ/í*“ž72ü…U&µ]œ’|vsƳEÃG}¹œA Sðéd·•°k›â6ê4N9l”VF£—nø¨E£ÞYuzWiÔôQOŒßÛG=k1Ro6WœÏç|ä¸jðÕJ¯QÉ‚Œªš‚Ì/ d"ÕeÛʶRJ­$Šn•`üî:³k5ªPŒ^‡ƒ”¢Ú›FÝè‘JP§4¡ Gj¨äÄ· G¢$°WcíófcHˆÖ‰>>>t_À“w»ÝÀ±Õ"ë¿«Š.ÇPw¿Ý7Ý}C¹Û^í’—4ö¼z´}Ô8€ðÍâ ß[æ ~í¤{œÙñhéôÅú¨×¢QR]4}Ô–N_„T«FM51111ñTø½}Ô®Æ{9ŸÁEÿgÓʯ².¹õ-¸åµ š“O¯ÆÆé Fº®5Û†’¬\÷j= R‰å«U%«8–Ÿ§‡tQs{Ô&|ÀÒÚ5òµÿè§P_¤Bn¯–Ò£іų!X‡¨_½+;ËJîÚâ|î;d$0^ÐAcV™ ômUï)úRÝÞSÐ*ÊÔ¯íù¨…T#Îý!„ú,µ¡ÓŽÚ7õñ F-i®5}ÔÄÄÄÄÄáõQ ©îK5>dða1c•Ë!€…=ºj³ÆñQ" °ÝnTÓõ9ƒ¾ºN`T¢xîûíQò‰ 5ó¡Xåeí§ø45ôaQƒÁ‡e_P›ØÙ#y¼Úº»‰¶ÒÙ77:Õxp.÷h0^1>ê/Ü£¬F}UËÇ¡¼žZé4Òât]ÒGMLLLL<)þ;>j0ÛÝn†•z 2©$\Q·Ô֮߻Èïè¶ìN› ‡;iïKgßÄ)½XÌííÞÓ/8±O õîQÑj#>jýs}Ô[ÐéãÙ¦F££¦š˜˜˜˜xBü§|ÔØS¯¦j Ò®‘*7vË»òr;°¹Š® 5*,í›zRI{[rp{᫺­–D£c|Ôn?¿¢Q×í‚Ø£·ú*¥æƒô&IÒÙºwõl”F .-wöGŽ5êW†ú ÷¨au¥Q/[G£¦š˜˜˜˜x*ü‡|Ô— ˆœjÔWKíÒ4uçåjûÇÕ±@y®~ci=Ë2דì6*ï G%ÂKAÚÕH ºˆ#|¶¾nÝŸÊ«Ž#}ÔŠ±áv»@êú¨µÀ°ú½ÛnÝ75=ô}Oò±/8s}<ôQ·û†jK3ùƒ™sOK.œ#OõD‡ú“ØhÔç‹Z>>jG£>[š>jbbbbâ)ñ‹ú¨GFc>jyAO?×E%“WK#Á“•uƒ×ÉŒ:­‡¼L‡ ‘*“l+É8˜¡%êh®³Ï39 ©`³Â$»çúè™»¡è†á²=ׇðQK»îÌ'î¾È^ K¬×ÝóQë\î¶÷s}4û–î÷¨pn§æó=Oízä9×ÇÄ5¾eyMµ³ÌµU§wY­QGôQO‰ßÛG=åýû‰Œ¯Åj—µËåûÁù¨ß#ú¨sû+¼_çY»ŒÎGM51111ñóã÷öQ„Y”- ©…~?tÍÄ ®™ø1ì£.òÜþ"cæHG ܸÿ”#}Ô}ÔÄÄÄÄÄOßÛGÝAèò" ãÇcÈG]›õz¹ Âp™¦)RàÍf|·üm>ê¢c>êmýZ"}ÔÄÄÄÄÄÏ€ßÛGÍ`0&‰u²ÛÅq,6žÏçܾùkç–Ù¹å]šóQ?9þË>jƒñKÑç£[^Y:«tº´“ÞÈ+ŠÈ.-åGø¨ok&ÒGMLLLL<-þ³>jƒñ{Ñï£.3B§«|!ÕÈ­rl:ÒGm4ê3}ÔÄÄÄÄÄã¿é£f0¿>jŒâEvó‘s¾wkè|Ô>j¥Ó2×GH51111ñ¤ø½}Ôù¨ã8ÒeDúʪ~ºöªˆíùìò‹g7ç¶FaÏzâ#ÃN>vÛGÁ}%ÝåÑ%’$qË㛢țÍ|ÏÌÝÝ· ¹Nîa—¹ñ±/Ÿ=nWgÎjNîñ–1f>êaü`>jK§ú¨‰‰‰‰‰Ÿ¿·Ú¥‹à‡ÛíÖ]!q¸üȈãØíðl6sW9®Ø·Ëgãá¶èU»A誅8dÕ•«åÌišöuùrÚxž'dø =×vq¾u®ÆÈxéx8õC<죶êôY}Ô!}ÔÄÄÄÄÄ“â÷öQ·U_o6k¬(Q-6Q+½WË*£(j¬÷zlÖLœÍʈ ÂÇ‘Ä-'Š„r£f0mYP(h§Â,}6­‡a{%DYgÐ]7¼ÚÓæ*ŠÛ~|˜_Ƶj¸_}%+†‡.’ØîóµžåM£ó*q£!¡î8È¢“7u.’ø~1<õ<ì£Nºæú š˜˜˜˜x*üÞ>jW;½œÏ Â~AáDŒ•ƒ[^Ë 9]ÁTV8!Òu­¬‚( qˆM«Åµ•½ƒR&I¢¸ÝÃFëiÚl[a¤ÃAAcÛÝn€ô¶DølVÞ«Á ¾øöµ'èyŸÍà n˯·öÂmZ»½Šcù9þpHÎÑÐ:Q¡;a¼A ÍG=ðQW25}ÔÄÄÄÄÄ“ãõQ ©îK5 °’Æò~ýq`Y¸J¯vY1Àv»m˜±Ár4©æl-¾ZÚYÖ«u+íóQw¶>`il+ 5¨Í[Ê ‹À×-ƒ‹[OŠ;ÖéqnîE}Ü´éN‡ùµg(áö“ññC>êù¨ÏôQ?~Eõø¸Ó¨/ð[1]\­Öª$\mny°Ä™eä37WØ2xy^8pÄ‚Àðs¤b«ÀGÁ¢Ê)kíRÖ‡õLZ­ZŸ ÔÐÙóv1Fñ¶ªµ¬¯á©QïEç§88€÷ø‘|žñ*1à£ÆÃRðf'¿Øn?ï–ï÷QßkÔÙ™>jbbbbâÉñŸòQcO½š¹[ »FªÜ¸Séí lîʪËå2I’†î÷EÈNò|Ï~´>¤Qw©ëmÚvlŽïZ;¦ùvѺ¤o5ômÄk—Òîןº5Æ2Ò%í'5ê÷Ž>5®5ùåèÃðç*£Tù Ÿjù0^=ðQgµF}¾†ôQOŠ_ÑG=>u’Ü4ê«å–išº¤7¨íWëÜuaWI–LYëíZMws2£×&}µÓ!GÞ^”èÔoN‹.ÞÔ.îÜi}¤F½^¯åC¤®Z2çs_;æ*É µ’?0ׇñQ×ÖšNu»iðÒN8ì¾AY«÷ôQ¿_ôù¨‘n6kùý×rpUÊo1¸âÜòªQ§y‡:1tZŒç‚>jbbbbâ©ñ‹ú¨GFc>jPSWøµ¤÷Ÿ;iv–m=ÙLuâÂn=`ﲡ«NˆÎõqíRÚu®Ð™ëý÷<³#f¢ß—Qvv1ŸËp†s}¼_ ø¨/çó*Ž}YH̵鬢X>òQ'Ù\zŸ›´¬5jú¨‰‰‰‰‰§Âïí£ŽòþýDÆÈÐù¨$JÎGýŽ1<5zøÒ—&ü·¶CÝF™>uͨÏJªo5}ÔÄÄÄÄÄá÷öQ„¨©”F¿vÍÄõOÕÆ5ß2ÌGm]@øêÁ«‘Þ%wÊ ÏG "]ýe«Qo°‰üÖv¹Tµ=‰jALLLLü0žeVÛ96òß[£‚`±XN3¿ç£>Ù›Oš¦7:}_Fç£ˇ㣶Œ:»XËÇÅjÔ×pe4ê,ËöûýétB'kþ11111ñ¯byîà„'QÃGmópûÎ5ƒÁø½ÀÝ#Úf~)³¤º"Э2V£¾öù¨kÚѨOf»üx8$I’ç¹Þߘ2eÊ”)ÓßNñÜÁÓÇX>ò¦štÌŠÅÏD£T¤S¦L™ŽLqßX|万|áW3ÅF£îñQ_웉wµq}˜æpO;XRìnALLLLLü«Xè´;¬«³<ÞåóM6ßäóµM‰‰‰‰aÜ7*:ýw=šs}´|ÔJªÕG]«ÝîiÇã1CØ”˜˜˜˜˜øW1ž;xø8âÐô¾nbbbâÒñQ›ù¨[>j3ч: Ò¨§~Ë›˜˜˜˜˜˜˜˜˜ø©p4裶Sç ©¾Èš‰Ï0 &&&&&&&&&~|7ׇ㣺Z‚¼Ò¨/ê£~ž111111111ñ´xØG­tiQ¯™XN= &&&&&&&&&~|ç£VÚø¨}£QÏàÒ’æ¢Q?Á(€˜˜˜˜˜˜˜˜˜øypÓG]Üù¨wÙÅÒi“§ë’>jbbbbbbbbbâ{<ì£Þ@-i~º,ëcúQ111111111ñó`×G £v5êãEIµhÔvÃ;f¾ßïƒ ˜ÍfžçEQ”g2³, ÃP2d6þ޳ÜäBÃ.™f~þªdð<# bbÁÙñˆûn/²†žã5w-p·ÐòóH ªs·‘F=UNO[¸-ÇqŒúqÛ™R ÙuÕ RÒ6Æñp˜üX?3ƈǨ›/gNû\ ‡á2ÙíìŠ-žìòìpË€`p!âªDÏQ¸]6ŠežàE’`&Q}Íjy÷zÄe‡ò¨°³o¸äÇ­³Ÿh™æ.‘e·«Õ©G.X¤ÈLÓ}£NÁ¨sa;JPváãccJÞïãÌtþ¶¿UsOpŽýoû[" Y†ï÷ÏÏnƒÌýÞ‘_ ýõ`s{*~ýÚüñóÖÝ£<Xy áéƒ2í}ùñëë½ñueù¸TuôÈGk6;U]!ùø6=;òBZT¨J¶ëA&NBŒþä‹–L|YV0§.2e„ˆXGg«UŒ­|ß“³BFµ¢ø…Ë¥ÜÒý§‡ µD,íPna•„Åb®JºŠ&<ÛŽÈlɹ-i4“Þã@ü²DwƒÎ2 µ¡t„è¶"ÝVÜm1œÔÓÏ\)ãVb®O+¯nVR':ùSûKü®g XÇñxÐ|ÜEA ô4sïz/Å?å×ßóp»Œ[«ôU÷U¹ËVÚžÝA°@áRoò¥9inVä8ÃöYžn>¶*ê_jp™ˆfÛ(ƒ} í«S¯Ù†2ï^_mÁC þL?‹úÚ¼S«Âp_ÿ¨Ô·ïè<î6}e´Îšh5Ÿ³u¾%Æ)Øþ9æ|«1ÆZùB¼qNÊã^¿‹NŽñøüì<æ]ß»‹QíA¤O^›ÿÏy‹ƒ#›œœÇÐo__ï?á£.ø¨¸9XAÏŒã>j•jQùðo?>òzÄg†9­zðáÄffd\¥ŒÐñOÁBxJÛ–åf+Ô&ê7†Q±lŽÇÛíGiµD3žµõ›ŸÎÓ´¨Õ©Ó³ÏŽò–iJÆfó­)i6ƒ§Ã­dÜRˆßãd°bHGW):¬Ïá¶"=¬b\f¨B¥NähTÔöUƒ|W£.ì?ózÛç9nÄφqfÇ£«&‰Ð§çª½Cn\ÅŸ®×+ÃFv;<%cûc1q5u¹»£mËTeïϮ҅;°Þ{‡û âaE­»|äÔœ§(n"Ì]tLÕËvýzͺף¨Êz}¹åå7£Oõû0¾e£<.j"PÁ…Ò¹V†øY鯳-í³ûëiu(ŸoŒqŠgÂvÛø®qJ 'ÄG8=vöÜ«Éa§¹üŽ€Ø|­õ¹ÚæÃ}p¹›_Ÿ ÝÛZo^·‚ýðÚüÎ[üÏ»Sþ§ëëíñ÷}Ô:BÁ!×_~)_ÍÜŽËzDY•´¬ ³É×á§ŒÐÇ£`Ü À8ìN €'ÛjnßLÉ\Jš³BòÍïàÞµ MÊënüºNËaª0@õóGÏq ~ulî*ÕÒ,Óp3Šm£s„^>òQ§‘Å‘üdŒ»:j=5®¡Í¶|âø¨764GžÔÏp܈Ÿ Ëh ææ‰Gªð=WqU?ÞÙ;déleó ½œ§­Üm&nÑž§m‡“}£E|(ùxˆƒ9<ìs!Ô¨¥èáÂ^e*§··­~Á¹ý³ºBËúšmäKZQÙV6›õÖª7#û9³á; Ò9þâ¯F`/Ú¾Ù²¾Ûh¾öPûÜȯŸžþ|áÔ2n^ßÇW ¿˜€ƒ¼ãÜÆA–39•çž'ŽšÚæí6¸æŸïÜ6*oÞ®Íß>oõ˜ÜJ;×ø]™½¾ÞôQïú}ÔŠs;èS¯‘¼; ™2R —Ë«Q#¿sÄWÞt:Gè³Ú:âÞðg]žÕúËýWŸ&?³W%¾î`±HS9ÁÌønµŠÁa‹…ØÞ¤Îj[û'uÚ’«[ɧÿ®†í]eÜó°±íg}Ô¢`àD©h„,36¬¯š¼¾j–MÕ+·B.Ÿ†³úŽñ³a9“d'*1Ùòôwî«3¹¿UwÈÖyÛ‰[4»yþ/—èÎû³CýÌó ONtYíp||lÌO9­27 ­ëzÔköÞç™l6úžÂm[ýè3ýD>È̲壮UU‹m…°´ƒš¼õ‹X»Ï 5îmOì_ÁÆ”žb÷-´Ú¬aÅ…ØBä ©ØoQÈ;æâóìTSG¾SàrÉ7Þ§ŠØwo[ü_¿6ý¼- G£¶¾ðʧñ{××À£|ÔŸ˜z´Ú€»õ˜»P-hÏkÙÄõ¯zµòœ; ‰hÔþ¦(zúYé~|›YÕiíEßþîÍŸadDüS7·ž·ž;ßÚ–À M~F‘üãá \wL»¸— ñЫ¦”«¦åC‹ãXf ¨®U˜¸tn°·áätßÊà[¯Qc[÷×–N|ûí/¿ýJ螇f†kx^i¯‚'+ÅxMýŸ‚•Ÿ7ò­Ï³©Î}ÍšZ×ßú©Ï87锑°½­vÞÍo÷¹á£.þžº…  }CÐ0íí6°NŒ“½-ãðŠ‹ÏUSÛcD»·cn¯¦…¤;ËËÙòåkóÿ9oUc?=ðQÿÌõõ°Ñ¨Oßš:4ï_$òê«*Ï8±ñEÈ´ø:šs}EcħuZßZDìêõ«û:2ͯêã1Rµ= ;¤k¿>ÅY2ýZñ0™lZÙÐòdSÒíæ8µÎfɧÿÆ}÷jy›'lTûÁÜó°±íμ–%/¡s]p{qµg$†ijÎÃåÞûËxÈÚürçªÑmíïé•g¯©Q?Áq#~6¬ª‘¸yHuŸ_=+«;dõþHŸ‚§¸yn)T¥U̶vá~ÚÇ‚¶¦Xn×e!†Õyý û]™Bç"°CZq}?ð£šëkÓV•ñð:týòØÙÏ(²ATæfÙúEIî YvDq}´÷Q´ëÍzÙ¹> Óùm»Ï·_Çl¡J£~‚sìë8Öi+²Ê¦[ñ ±¬7pi} ”☹@ë<û†‚dŠëãdo}vHõàÂzW<ëOêi=lI;+ˆ}ÙäyFFÄ?ˆ‡TÎ1ßœÒwóQlkn_öÜ@êžo‘'ɧg§—Q/4<Ó³Ö|Ôè’zöè£&~ˆU5²f¡Û/nš‡¥{‡,OpçlK …JfÖMœÙ:Ú†÷R¯/-|»ß.8çûö·Èu¾Ü¸,Bs¥T×E§ÏS®ëâþú¾Á¢³þÎ~ ]©^©0SüÚyÝVú/³q–=ßWîÌG-¾;+…€Î78Úý|cl8íz­³vË«…Ræx<ê›V‚³úçBË |PGu8trŒÎó³“Û4ÎýŽÜï=xôëÌÃkó÷ÎÛÒÙÙ²žïýÁ|Ô?q}ýìø¨Aª¯Ã>êpÐGý#x¤¯‰˜˜˜˜ø91ˆMõ{úsô‡˜X19ñïá>ê–FýÛ Ÿ¾Pbbbâ×ÅV4 øë ñsbr âßÃcæ£ÞÝ4ê¡ù¨?œ!˜˜˜˜ø9ñb±¨VÓxŽþ709ñïaW£¶>êë½úƨ‡ç£&&&&&&&&&&þ›8¾Ó¨›>êÝÿî£&&&&&&&&&&~-­ÌGí¸>®»¤z¯Ãx½ŒÖSâh½4xD+ü-Â8WøC¦M¥¤ù‹VüÅ’®[éíSSÒ–·[™mWQ¬øþÓ kº‘VîÛúpÓx"ì¤u랇Õ1¬þ‚0vÿ‰ÝëÔ9L™2]™Ë'Ò×KFTòWr•µ>C¹¾ÚùÒxÚTþÂ*géäßÕëóú¾ÿ·«»î§›SïÝLJý¬rš=iÜc›õ·÷±§•Û1\=:ž­ãÓ_gß§ãSïË&rÓÕ¤ø]Óï{ÖõàÏÖó½ïåΞŸ? ëÛíÓâ|mjÔsëãdé´IÁºóò’Ÿ.Åé Pœj|jàŸJ»ê¿µ{11èÕ9+äï”Y,™…-†ñþN’^œôÜÄæÏ”·[™m¥†syK«O«šO>_붺ZüY¬½}€/å-çÖ7wïê£w’Tþ4'/OEš¿Ó¹ŠR9b¥{ô˜ÞÒòÉð‹¥÷çØ.ïð/¥î•’·®Ü^2À­üVj¶=!-lj7qð]ZViÑJµl«G ¾ê›Çð>-õèõs[g}O8µj¨óËf™ªæóEî±Õß©NÝú»ŽLq—ÞzÒµÍ>ÜÛ} õ÷ÕÓç»»ç_Ú÷Þ'À—ú¹Ö¿”úÝ}_¾§§Qùwûu¾œºÓëhüéwÚú>÷ÖùðûÙsàÛça/þbZ󫬼:o&^-£^€Qï’}šË\F£NzÓËÎ4ç{¸»þsÝCò·‡þ>Òë$ßìÍŸüs{(QfŸŸíêêfåuøOŽ@j69ﳓý+‘&Dzú§äWŸ¢3‚Ïûü‚ ™?±£›Ú¤N§þ²‰;RÛÏQ¸j¥w¤÷{·Ã!:”)ŽŽ[ö£—døÛ"Ó¦øÛŠÝ!O$=þ×Þ¹r¹ qü+\Y[‰¬Ü¯°²²¶reem%²‹D"±H$²‰Dö&™¨îþµDþ7Ä/×}rÈr¶r0¨wG4o¦ýÌ)Ï?¿m~K6ÿÊ$†jƒÓÌc½ÜqëÐ0èì!°³ÓÙiÀi;;]÷ÆšAã4x”Uƽ8þìLõ·–S|t÷êÁv •£npŽú–;9ê@vz¤œþmð›Ðÿ3;½ ¤ÃuB+•uÇíN£CÜŒÊøçV<ͱ U=´¿Äûsur_#; ÙGâîl: œ)8/¢ýÑed2êéEhízS¤8&–yï§{ÇÉKȺš?j¡1?꣣nû'Cèäðq:®·ô|þú³Û±’¶ãõ'¡šö¨Ÿû¡ºi ÕðàG'ü°rÔ <õ‘AŽîga¨¶¯­<8­29r UhÝy3ÕªÕ)³Ñ÷ð_†ä!ÖGƒ_, 5Âiœ£6´'GíÍNÏQÿV¦zÓïSvZcÒHÃó ™ê2ègún¦Úƒ¸vŽºIK“£V|Ôpß'5ÐKqÎ uµñe’}UJ]PŸYåœ ôÒ?òJå« (jô%7^U¦_RÑúVûzlèÞE›%Ê ª^:³êb'j`ÛAîÖà4­ï¹²Î囘ë‡ýÌJ¥Þ¬ó£i“y6Â#Ó5ôA‘8x&©˜q ¢rH×ÅãunÖS¨n7¨žÒÞ‹¸ÑǾ%{ü’ºcx¦¹€ý¬<;Íày·ÛÇëõšÞóË5=]öÉ!9²,žü×€¼›jëzв:(µ§SblÁçÂ=¾‹y¢7è¸aíe$q<Ù?ñÞ1?¬OÇW6±ÿ]K¯ùsÇ­_aü»³†Ì·<êsÆÇ!ÜfÌ9ò¬]®S?¤£mºí/KÆOÜ8YçŸoæÿŽŸ÷÷ôçœÇQcŽk]ÿßk[ž ›“ÃÃéËår»ÝÒ¬`ÿTŸÎ_ŸÇãêlðØnŸì@‘±Æ¹¢j ¨ø­€A55á½B÷ÈOp›‰ûaO_ýþ¤Òc„(YÑ“1L¤Êlºé¦~…9Bf Òƒ[NZHB*g«ëã:ö»> endobj 35 0 obj <> stream xœµWÛnÛ0 }°àãö2[w(tínèAS`ÅÜDkƒ&Vá8Øú÷;26[•dK6 0-™çˆ¤(RQŠrR–œ#åHhKª )%©’dYÆW—“¤1£%™“Š Z“Õ†œ°¨+¡é¨°I*…$]È dIBähHcÉhLFAZÌcm%è+z [TNÆ@–ŽÀ( @io™Gÿ‹ ™½HN˜ÄîE2d·ÉÜYнX>2I¹É¢jÆoúü7‰¼C&/Õ³”›'Œè1ƒg€˜˜"ÖÆN8‹²(Ž,$ Æ¡8v°`ݱ$íßh‹HŸ’͘ô‰ØŒI£zÌé»0~Hâ’9o–k~NŒ^ cÿt3ÝṡÉÍ¢ñ©e²n³Í;Ã{oеönqÔ®–[#›<Â[´ùºí“ò9fCd“ùiÅ6ï,Ÿ»>¶8*·ž§Óì {Ú6€s ßŽÞî¿?=2]ß6cÒÕl#¦_FÊ4fý6ÙÍÀtIØ#w5Ðí°˜Ú£wÀ˜$FºsF‹™¯Û¤‰}gàæ¡9Ë 7ÎðxQí÷ ËDz¦cMÇšŽÉg®ã6ã §lo׊ —÷!´ÙE˜ú³ê>^Y£Åƒªµñk¼¼v‡þª@tôñë9ZeÛü®:´>;÷õøi»ôuø‘ ý¨Í>ùjì~˜åûçz:©ýð¶ŠƉà U; u?nÚÉ· /ÝèKhî®C¸{Šoœ™ßzßF#Ûì¬5ae|t‹çÊøxRMÃÍÊÄp:û]^j7M5ës³÷õ|1›_ÅÿGd¯ó«a>¯f~~ÅÃÿyaX“9 è»×üÇŠúK:qçz,³ÑØ>Ã~K­DN=&¡\‚”k±¾ endstream endobj 52 0 obj <> endobj 76 0 obj [ 220 0 0 0 0 0 0 0 0 0 0 0 0 0 205 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 563 0 0 0 611 0 0 0 629 0 815 0 0 568 0 0 496 0 0 0 0 0 0 0 0 0 0 0 0 0 488 0 441 555 488 0 0 552 278 0 0 0 0 558 531 0 0 414 430 338 552 0 774 0 504] endobj 77 0 obj <> stream xœì} xTÕµðÚ眙¥Y@g ¼¦žaCýI# tÃU£G*|paY›(×n…#6;Â:Öá¢ðÿðAãÛ`¥Ð +ùßA{¤NX>Îó/n#\ó… X9âã˜ÏwÂåÜ [X 3©N[ éÌ6’þá“ø;ÂXóýWaÉðåüiÌ…Ç`•` ç‚0Ë@;×¢–¯ sÍ M8Aá&¬w«u« ß— ð·ÃtªÓîWs!’ÿóú¿È\ÿU€; ³ÿ.,áçÂîSX©ãgB%yæ^܆ŸsùüóvÜÍ0—Û ¹4(àëÃk/Üwµµ@î?~ÿ|à¦ÃOÃÒPOÿ:ý°æ3aàï>°(D! Qøÿølèýg! ÿZÀσà录„Ÿ„£ž§.õÌô,ûG1 QˆB¢…(D! QˆB¢…(D! QˆB¢…(D! QˆB¢…(D! QˆB¢…(D! QˆB¢…(D! Qø^Å4¤DåñÈ?ÞB^"JF”`@5L‡z˜ 0Z  VÂ:ØWÁ&ØGЏwøq½¸ex˜ùNÀ¶·h…vX WŒÑ‚ ÿ€üpø'ç°ÇQ?íÃw ½q<í¸EýN’üÿ“™’øI@¿›ˆWÕ¥Œª'lÄa¤_G"ªH£ã #ýæ Cô8;†äCäÕ*j§«H#Y¯b"âi<T´ ÎW‘~»@‹Š7!¶"¶!ҿ¦ÍO¿±"„¸qâÄ+é·™Üx•Š÷"nRq/â>q.¤(Œô¯¼¹wÂH~‰U1a$Xׇ‘ì¢ßÄFr€o}O׆+Öw®[»¦cõª•í+–·[—-½|Ée‹ù›4ÍŸ×pÉÜÆ9³ëgÍœ1½®¦ºªÒWQ>Í;uЧlò¤ÒwqQ¡«`b¾‰lŸ×ç#Ûç/j>ŒÏ‰âö¦æƒáªUþ¾,¬k>ŒO¥>¦å¨–*iA¤ê© ±ÌÞqØÐÍj¦`åÖ~LÑhíçÂ:c¸£Ö‘„[û…p/b- .6¬ë[ç©Ö±Xc¤5O>º« CÐûâ5¾X_œOÏ%pIª:ˆš'Ñ6ŽÀ!=I Ž>ôÙÈÔý¤»/Îç8Ì<5ª–ÝhIuÝ#:95åû O|Áù,XÔ|HèŸQ´¨¢€w°½¨YÂ-T| Í×ùÛC?MUHo_<¤rP8©G¬Õ+ñR[•¢“ª¨¾‚ê+Âz-ÕÇHUxxàÑ"Òƒ2ððÄë«ÄOºÙ¸l±xïŸA¼·œŠ6û2D¼žâd¿ˆgÀ,´›N1€êéJwk Ýä<½ g¶ú•؇h2S‰Cqª´¨cmèŠZ1ÅZ$&¢–n¿â—i§Í+©QħÉÒE›ö©É¡¹ü!³TÌ.cm¶Ÿ½²8½F˜ÆEìÌRŒGÞ*aUk@ çÈ|ÜÊBýÄ;š6|&rÚÆ;ÔJŸ?º„x%®€Þô1LÖ CüÄøýáÁ³Ò6Õû6*:QΨPª 0:X5“Ž?Ûp¨ÔôYêf^?4JWã FÍ<Å`µ’=³ëp{j¤²HcôËTÔÇ‘°6†Î\Ï^šú‡•69GølC7€ŸÀ}à]¬Pãµ{±6©C¡Ø„±„ã›0‡o¹V<Ù—ÓÛN¤W-æˆx?cPµ93[v”™KèÓÅJÌhiV7Wfœ0š…× ¶¤ˆ·?Ê)ýÔJ¢G==Ôþ¬eD™óqj¤DÔRxyCÊŠ ‹í#Å:Šô»@}~rØEãTV9”ÌÕˆ ]#|91JSè¥4…5žN1€Ë6²QpC`ÒmÔÝ*6/ÃôG‡x«Õ…°±µE ¤Ú“²V¾À%î‚鄎èt”î1àxó‘yôE÷'rqy‹â“ZèО¾cÐY·„hÒ½^J ÞSË[Ú$öäLϤpôéu##’B ÛÉuhŒîsè²Q†ŸNYjiÃÕ¥ý‰-mê G‡zsÔJ¸»ÛèÌT6/Ãe”´²‡ø%#a ™C¢'„‡ò¼O„œÖ…¼³èÕ$²¥nqHô¥Ï8“–üè(l—M Û‚ŽfÜ·$&û¼†}ÖÉaãXæUd/½ ¶Ã¨°_öð éäI#{äg'O«gbx}˜Uì•YT¸¦È‹k?“6uD,Ü 5ìVQw\_6ÙÞ0ú¶ºLI®o\ìÀÀN„à ’¡Çãìd–ØO¾Šg#Â#—ááLD8>§"ÂɈðIDømD8>ŒD„÷#Âñˆp,"¼Þˆ¯G„W#Â+a0"û"­awDE„ía[DØG„EÁš#BSDhˆ³#B}D˜&E„ˆàŠ#B~Dˆ‹1AãfÒŒ~ÎègŒžaô4£§=ÉèÇŒž`ôCF?`ô8£¿aô]FßfôMF=ÊèËŒ¾Ä苌¾ÀèFŸct€Ñ3ú4£‡ícô£3ºŸÑ}Œîft£;ÝÁhˆÑ[íaôfFoBê+Ÿ%v³ÒF¯gt3£ËÇh£3­b4‘RCe«P ™ˆ.Ä ÄK—"®CÜ‚x+âCˆŒø*b,å?ƧÂnþ ؃¸QA@| ñâÄXôêF¯nôêF¯nôêF¯nôêF¯nôêF¯nˆÇ1” u Z— u Z— u Z—@ ö*Á{ˆ§y0 ÍD¬@\Šø ù$Í™÷‰rnà7pîµsÇÎ9'„?0üÚð±á3ÃBge¼Ã@úâ1Ä3B¶O/{æÌ3#†J“àDÇNú­Õ\3ZCä°ÛxZb'†b¨t1¬¬Eº…³1Ûû!Ñ…Xx âRD-¼‡ô4â0w¿o>ÿÞ±d[ÚÏäÚë’×^—òú(_y’5H:Ö!Y½6Ù±zí–+R»6&YÓV¬B²|%’¶ö$G[{ÏúÔ” É×T§87!¦Tq·Á½ˆ¤!ͧw/w·ôÜ.n7w+ò·ƒÛ zpp÷ÂDœÒ‡„ø+D{m…î!lû-ä÷cÛ!aø#n÷Á$És…û¨P™ÊÝÈmÆ%–¹¸ë@ƒüzî|Œ“¹Í*¿†»”é¯âV0¾‚»ô FÆë§ó Cô<Í]õÔn-꪿ôP‘ÛWYÉ­‡Äǰ¾ŸÙ¬ÄÒ»(}„Ès7s›0¢2¾]lbí· §ã¸V囸…¬þjn9>#Éܕȩ~£Ê7¨|¹j×…˜>Ì×q ÆÈã+°L`+¥Üîrn)†p×ÈÍG>—»„kÀP긹ˆó ž[SQö£|%âF,ïÅò¿ƒ<ž[‰-Vc@[ÑSòzZ†|%x¹VÄâÄyˆsk8/‹Z5gÂ…’ñý*\.Ç2õ4΄Q««´¢ž@Ò9n*ÖÇ`½9ÝdÕÞ‰ö14ÊdOe2çR+ T>9í _-Ë*Ÿ€ 5òôÊ*,Ð }˜ i*ç†zÄ –º¨-WÅYוȩ§ ätèST}™Ê'©¼Tå¢ÊKÔvE*/TõãUžÇq ¡ÊµX&Šô0WŒS¶qv.EÇá›#òX.Ž‹g‹‹¨ÃàÛp´±¸8:\.Ž 'dž‹‹õ¶ÈÆÅHGO™ÈSÑSr "1ц¨CŒ/™OæÐ™‘¹*_H.£±" T~)rª—üÏ6™¼­òä9®òcäÆO#§ö'É'k>/Œ‹ÇÍ6@„ƒEEª€›¦xàñŸfŠ´àæç{ž$<ÁPÌ'¦â¡Œ )¢LO(ÓÒF”GD™”ªJÝ:‹*ùââQâ9äkØ¡:”*ã }a¿2©ŠrlXÀF‡$‰ŽžHÏðø>r8Ø0—•íYØOb}òë·5òÔ·êßâ|Š.Áóì€FFßä‡,ï~W¡çþûˆ¼÷>|_¯ ç^A¾÷6^ö=Ÿ_ä¹­——·÷ÞÓËŵÚ[ÚÊ‹­ t~æñé™ÙžŸõ“x_¹çN"O~Üu''ÛïΙà±ÝMŒwVø<ïÜIž"“H>Þ2)<8(Èøpqð(eòÈò©ò)2›Ìb6³nÑȇÉbÒ„ûÊP™BšpºMÀ‘­d;[œmÈéâÞ¢òíäVÖp7rZ¾õPF®¨Ô“}@È+ä(«|9nCò:9zPKW6æ`q±‡²< áßd°eõ™þÞêyée^~ùEAö½èGµ‡^´Ú£Éxr*³–~<±ÈÓ0ã4ã}§õáXø`ÂÏàQÌ £U5Ìþhn.åOµ¥zžû˜à¬ã¾Ë:ö¹?ÎÎö¼÷1ñq¤{õiä>\ßÀ´iž‚üæ|`3×ïš“=Ï?MÄÝĸ›P—;&•1×;re6”âè{ç.¼+$È·„4rãøÅi^þü´Fþ¬›“ÏìäÓßÉâï$öF›ï›×æµÓüÌËÜéöá¿·ìÖTæ?Õÿ¼ãsÃ"_£ÚŒ]œB|{ ÙÒ“¹½‡ÈÛoÆ^nBßãé™ÙÃ/ï!u=dRÉé!ŽÉVû$«µÔj.±ÜV}±5®Èª-´ò.+XÏ~eÏžårrór äÄ|Ù0NJÌ’ ™‰b¦4F ç–¨óvyïõò£I¯Ókcbõ¼ Ñã¡×òÁÌÎ Ä0è õ<)¦B ßÅ~eÐê@Çë SajœŸ_w%¿öÆÝkxô‡‰Žè} ’ž`IM°m f!)ÁuvÝÙ‡Îî;ûêÙ×Îj+ÎúÎ8«œ=vVýDwÐuÖõ$ÑAÑù …?yÏz¿ôþÞ›ïàÍóæx³¼ã¼¢7ÃëðÚ½V¯ÙkðÆyµ^Þ ^¾¡ÁÝDs=Ô7U)‚|~•â–ëûy±Q)–땸†ÅÍ}„ìö£Vá¶ãŽnR„íøºØ¤˜«-nî')´ºÇq”ú@Ï.¿,§W)ÁúùÍùîîô*¿RÌä={P†z¥xžâªä±`CׯßÐ¥ªð‡A_^N­2¡¶Eɯ ÔÈ-²!l¯¶á£}Žô3f(#L‚.ꬋjºº.0£jÿgJÌㆠÛ@dªI×ßÖæcîŠØ)v¥—ðbƒ>Bײ¡±Šþ¿©z%ØX¯d4,(©RU½ò"–&5,V¥*ô½! ]ô³q]UÇþ{lG‰ÉâÅÍ•­d‚ä+ijˆDüñˆgO#~Šx ñ$â'ˆ¿E<ø!âˆï#G<†ø&∯#¾Šø â âQÄ}ˆ·"îF !nG܆¸q1â"D?b3bbâlÄzÄYˆ“ ]ˆóãc5¾•Á/‚Ÿ? ž žž ž ~<ü0øAðxð7Áwƒoß _¾|1øBðHð¹à@ðÇÁ§ƒ‡‚}ÁÁ‡ƒûƒû‚»ƒ»‚;ƒ;‚¡à-ÁžàÍÁ›‚ÝÁ-Á냛ƒË‚ó‚ ÁÁª`bpÌŒù»ƒÿ¦Í.úÛ*š`9ü 1‚ýžJø—gž Ëøšõ¥ýPãy[÷A? ¨Î:|†;Æá}£-¾ ü±H/±* ´P›UƒÖÞÉøÂ¿ä žû‹µcÃ+ðüˆý¾ ÀSp¾§ê¿?€ôø\ÍÊ~|0ºö!mBÍ"˜ àrö;9ëa?<¬¶Z(ú6åѪ}>‚’¯Ñnï7ú¿{¹ú±§½0 ý•C/Îöø.<õ°õ‚ï‹Ñc\ ¬‚ ð((Ø6¨þKsà˜—áØê0Jëa-ö¾ÀãÐ}p/ꟂFxPû Är]t¥†ÿ‹›2ü_°ÛÞ…oI7p»ùnè‚ëàAø à«?Ü:ôÜ_^½¿öÀ=8‹›a7®é"~ßÀFÖö¯Á¯g16Wãª<‚ëñ ì!ÙplƒÍDÀS¤ø¿ýéOÀNô}!ücÜÆõÝÛ€ëòï8ú†‹›’<y³ ‘Dø –þ7G26tb.\w#ösμ–cvmDÞŽ8òïXá£p9lÇUÿ6ž‰¢¾ ®‡µÄ‰'å °Øá´µwÀ“¤m7Àã$΢ÿÅ8ËožFõ<º/I2îÜ›üW´Ì9"”dÁ‹£Ï"‘Ì·'à1ìÿß`/q~Çaˆ¸H®ÜxxñŒÛ“ð,Æï$ZØá—„\<’oŽ[ìд jí7ǂپ낳éÜ)÷ãþÚŒ9ô8îõgá6ø!òXÚ‡;ènø>æÀ#˜KÝ8Öóý.7Ò”²$bfÀH¿T?üúð ëw0Òjh÷ˆü ÜÍ¿ÂýÜþ7¥¢…ÿ)àb¾þ@ó7ScÐáO„Çb„¡Åä÷Xñ0îøÛ‘^‹?+ÆnËŸã?ÒþTóäP•ƤÉZ?tÞe¿„wàUx>€71³_†ßñ…üóüqþs! h5ƒšƒpÜu±?a­Ð.Ìö ‹„M.–Óð®j„Kñ® à}¹šþ«fOL‘p›f¡&ÈÎ¥¹›u๷ϦÛñ$£Ûî$[5MùP%Pï˶ºJòÆçC~º®¨ $_WP Ë/J'Áx¹Ðm¶Xíö‚"*‹]ø©øõ[ƒÅ&3±y\ÆAã Ém,6þú…¢BRZRÎM.çKKr¤q‰\ŒT:i’»8ƒ³&a!‘·ZmV©”˜œ&ŠÜdmò„,[ŽÃPY.f¥Ä¼·T×µ–§²¼ùbŽ5Ƽ‡|}NË·|]F~—œœ=¡47ÅåöHõIYÅ7f¤»ëÆç”O«›èÌÏÍKÓ®ýÖ·†>îûÓrá˯¾‡W ŸÌš6ái¥Gnö“Œ‰ÖIóRó:3o¶Aÿð_!ªp¥¸÷'Äñ\†-‘OìõÙ,Z^Û›!d­–‹'–ýœÙ<.þúT×q³Çå>ŽAðà*NÉ`¯H¥ÔxdéåK<²ñ’¢BÇØý¥Ñ_oí­·#C@Wr…<ÚQa‘Ÿ˜¤N§Å@š“ÝÅ“&Mv›´Zi˜JÌYîâdÁÜjŸµ¬vÅ·.›}GGcût±õòç¯úzÑþpñ}š²¡O.]‘ËЩŸúd—kyëÐû))¤™,x‹à+®§{0Ná§Ô¼ìœìGj ‡Äñ99z.]“Χ÷úˆÆb 330€±Á0¾fÄõz+oíÕ V½>Ö¸ßdâ&nEWÊæXªãD“Çä‡J[…Cf·Ùcw¡‚Š4d¦¼lì–vªIïíˆôh£ÇÞ=‹Öˆ?¹Øäq-1¹1`Ù1¹£ãc»(~¥˜ 9¹¥N+æ¤0iîÌÊÝ—†DÛ·ä{+[³V\²fÿâ9÷¬»ò KádrGV–%Fc›—C“…¿ Uß·¦ZÔ&ï:ýì‘¡w­Xí]ÔŸXT¤Ï,¨.Çxn¢ÿJ&Þ´µp‹gLà'ø’sxÊ‹ 7£Ð‘áæ ‹ ©IwÚù¢D“ŸV>í‘C\_Î×êLÉgvŒ¼e²ë¸­*lnŒŽÍmw©ÅÔA–ciªcæò¼»°+ŒÍm²¹]š:HÓ‰¥ûÁ@°8p1„dp6«w,Õñ/É”V•s¥¦’N ù÷ŒœŠ¹ )ß /È#‹å•C é˜Rå74={œ-å?Ççäg—ÆYÇ -.ŸoË) ççÚtÜI{Õ¢Õ…žÜtG^¶PT#M«ÚzîtSu¦¦¨ˆON6»³Èâò<ËÐ’êò,»¾¨È˜;cßR[!YŠŠtÉΪZº¯ñyMKß&Á^ß©%)¾8óŒ”w¶.1—ÏÊÎz¤&!ŠÜEÔ”Ú¼›O±—Ø dNII¦öT²­”7›ËRn>³[¦!Æ$µ¹iªÊn·nŒv8Ø&š 2ËÍ”‹º¡]„݇ãìvÑ(›˜f Ö!4O–ðNJ6[¿YBœÚ#¶¥ž|cœÖ4ÔµahIÇt·GBÙF–’I£&‹Åãë‡ÔP%fÕÍ#[î&¥ž¯üéiCߺmŒ(~íæéßf`Ž eà |S]Í¢˜&òb¯/- ³=6&ö‘š‹ù‘Ëx~|¯…·]Ÿ•51¶è]Çé6;N÷2ÍC*"ÜÍlÿ¦‰½ª ËøÞ ì2…ÃB·æ7¦^œLïi\n)ÍËÒºUcÈwEC3#“>óñ‰7?ÞºnZ×úÛçÌë¬j->7µn¼Mwáì=ÑþȪaþÔ[Ö/ÜP—.Ðlé>Ã߉§[!gó4¸è©ìšè¢­¢’éãEM²'»hï¶ ‰¼Ýž¦ËÊÒ& ¼Ðë›h±¤ñi½Á¾ÿ±ÙòÄSCaÞ©˜˜bØ_>åÌì”s-9~þF0™=õ„3{dÊi Æ…4ö(p½tNõ‚01¿¹ÅèZ28z¾Ñã­€+-¡×ïÈé§µ&%'Û·1FÛô󤚎7—\¹iãöê•?»ivïšV[Ýåõ•+¼îŽUÝ».©Úøí–ÉäæåE×l¬_¾È;eÝs:^dLú¼yYnaKõôeM%¾µ»._Ù»x|)1Ó¼jÀh+˜b9PLX^2üÑã#™×ßG¾x*q$]Èë>櫉3ÌÈÓérùÜ»|ºn¢Ð  3êt{tût=¯Ó¥’¡€/xÐ'$gg=Z“m-…–}Å""á,ô¿|çææÏ,IXJI°¦['Zù8ÞjL1òÆ{RŒ–¤¤ØŒ¢Ëá‹i§qx—Lè‰ÅÒ!,ĺd¶ýçÀ ]AÌkŒ.ÞVëå%ë+" îÈr®e‹ eÙq¨¶€°•±åÞÕ±'sLwF'ê|8³Ýk: ›ŒPð`Îd¬Y¤Ž5‹ã=)FºðÅØ›:",°ûn½ì–×Ó}Ån¼œœÒ’¬lºþ¸àøüUŽéœLÏ7ζ͸o.½wáì®r|»uæ-œ1w©ý¦@wwUëÕ^.>)7èSýÏ^.¨+¬YW~«àŸ5uuÝm%T¶_]ÖØxc±ËQqãMCûgM+ÉLÖ‘¹ö•žª”ªåÅt¿­Ä hÄ pªO5"¾*s !$³Û2]³Ñ,šù¸L31˜I oö匟a6Ø ¼ánŸ=9ºîôeÆx³h±H!RüM¶·qqRSé @ø‰K}îz‹.×/IM9•j7.YOwXѨ~Í–écwl7ÜÝA{½³#ÓÞ[/¨ÎäTê‰äÖ‘ Ķ×d|@ÄP«Û‹ŸµÅT\UT×QaŸ˜^{•Ïãžë¿Ôýô‹/ßܸƒøõÊúŒEý= vË&ÕT•MoùÓ©;N\gÁ½‚‘dÍÝ0o²;Y¬ L†GkŒVw’Ëîâ]4$ãÆgfâ)L££}´&bI|9i/Ñ”¡É,Ieù4FjCEŨh$síTkn²S;FšœKÏŽœ‘“«10˜Tîâr¡”ø[®ûó©Ú$yÒI¯ÞP±d¥wÿÁ®ç¯­êð$:˪]Ý[×væO)›âH*³K3Ìq%ñºfnu^Š®$~¿P]wö“ý':­ö¡¾¹—ûò“^48§âcÆ«3ë̬Ì­#,^U¢%9ÃiâMwùœN]2LßK9ÂAŠ1EL9“‚Š)ÉB2Ÿ|7º >ãNŸklfpš7¥¦Jæ›cXÔ>3{Þ2y0·")I,¶ÓSNE®8O àÄpÇwuÐnS`zÇ7ú’ïî ÞÙ¡‹$uÆNkJÖUõ]ªêœó×9§Î¹-ô{_ÓÞ —~zΡÇð:䑇÷!tµˆØj£Y*‚>°†8‰F& ú¨ÉI —²³š& ³@3dö[­mÀ¿?ƒ‹Œ,\A†‡KÕ#È‚%l ‘-ä¤[Ò¥Ôupõ‡ú°…€…£$Œ- ¶Nn$!¿„C…!²u¬DHøœ€A¸]²pÍê|ÓŠ®¨Qn-î û,Ç¢¾¿,.7FãÅ]‰IEøN]uzì ¶-)Þµ²ÓÏ¢è\ïé)@Ùþ{g;ûçÇ‹Ÿ(´„m(|`Ì>¾—î-¡ãl)ß9iÐi÷ÙÅ,›d²`3•Ö§ŽÚ°“†:úw4¥£×£_4 ï  û)•èRT¢ÄTéxü»§ØS™êó ì,ívܨ[ܥަ6ŒÎèO‰‚zÁÓ Ä;ÿÐIö5È5ЂH(aYÉÁ[m {-‰5›<×Î&óÅ›¤…—^úð•q2ÍöÌÄ BèõœA¶8#8:d~J8ñ–P ‘Q…A ò ’j$i ƒ#˜ ‡g/ ‰”ôÏíjíÜ5õË»b&…1”.cöøœ¨‹«›?7S¼›*;]|_‘Ìãý¶ú£K‹wõv  N¿Š,’8Á0‘d@ÖèÌr;m? gͬ.0ß¡ò0 *·ÅyÏ¡3Ú;h t’FK*Žm©x%rÄà`w`÷s`T.Z$ iÙúšŒ2$–—>eüé¯'~xÛ«¿Ùñãâ§î¸¹g]«½kgÿ¾O°ï¿}èôØ¥s‡Oß?üÇ׺v<¸èá—ÖüLÀDó‹8p›mVÛdÁjÂqVèHP§ƒ P° ÊH+¤·18HÕ³æ~–1RV©PDÃP¿ß-êßJPu2âØ#ürù|ç0_%ԛР¦“¾ŸG]3Èú`œŒÛË‹à'Z¶˜üeqà˜¶iÝöÞ–ôÖÆt ¯ÛfÎç‹ÛC^+#hž6Ó= b²3ÙØâÙ™â_¯éõ(§«¹¿½¸iU…¤’…j"sÚÞi_d§ÿÞµ{fí!~σï×›ûÓ(Ä€Ò³‡ù$C™†¢Ã2<³˜¹‘*¥Œ+eBvõb<%¥¥R¬ýÍËÁC±¿ßçË+öÇʈS\iÝT\8Äúa+1 ÄûãX”ÿÁ̲ÿ™™¡‰ÅÓ:0šbK[‹Êh•Á°ü…(hÚ."„6mæ2«öÔ•Ìmó›¯-Þ4ÖÙ8t×âe_š7äØ:èmôD–m]³£sõ“7vß2@jëuÎëJµÕÇs‡ ‹F n›ñåµKuþ¶džoH†ú‡z–ìã5l·‚WÞ¦^’<‹VÛA¢™¬…¥J÷xF×o`ZÌ”J'ƒ -“ó*0Yp¨Þ’ë F¥Ò-—ª÷ÛÓ§š1D_ÅA~.Jmîœz5GYz~cU—öàhu§¹ê­Q¹Ž˜){éé8zÚ†“+†º)oòš¼\)X$qyçæ:üFõ}°§ø-61+™íKöÝZøÄÇ¡?£ˆt®_ó¯ÃÅžÁ‰.§·®±·ã±¯PqÄï!éAÜMøõ¹Ž8ŽÉ‚ÓäQÉýrZ~÷› ˜˜,¡Î¼ßãfJ†IQû#$Bäà‚ˆŠ`»,¤9À„4Ç5‚C ‚ú÷ËŽâÎQLj×<—/G…‘9aœ³·Š»ìÈÃs Kèn Í^”i þ0tÉŒ±Æ?´ËtKÎ_³§Eì _,Ò»oçÍͽÍ-~³^•Z¾  F„×/ë¿ÿvxó²Î ‹õïºòo²’G\2ºZùX>°ø@¶˜.Ô0zÐÃ-öl:|`ñä`9¥Î7²øÈ‘#y®]gCg=øÈ Gc†& ×©nZ®O$«Ó'Uè‚£Ñe  ÇЮWár…i$X=Ú–çñGZ•Ô]g¾³äõ„·h€ø-á飔F(¥îÈ“xõA?-äàÈ&Îb€yƒ¡±œÀ£å躻øáÝo[ê ïn‰2Ì!¼÷WOkõÉF8€PãµwõÇ)–:ÓßêR!»;ZÃEŠ–]þž õºc)Éu®v~ʃ¥m,Ε¨´SàëDÚj+gîÿƒú}5%çQ ÈY¹GŽ"¹š—¨iõ×x‰ÓX³)£­+ ,:Å!¹ÜÌ'jăßyÐY–KžDÚhžÀYà²ü9æk£Uƒ 6±œ>íb'ØOŠ)%Á:5A1IÙU Ž’SЦ-[ÑÔ»š øÜé|o¼ø¦Ã Ãé^£}懜!Ý÷„c1×’¿úÜBÍ÷£ˬ]«©wæ´¸‘óP›¼vGK¸¨†¯Gz\ñ Þ>^ð­ðý=`-DzzjNLŒz²Àè4¬n¿´G§E×ø<º_¡rš8½¦«£½Á¡^ö#¢<êJEys%aà~°‹ïö)¥~©W¥õkí6¿×ç§¥Z‡´®.hÕ½*Zu‘×yë¼iï­^´}ñzýz› ëhNÏM€E+uJõ ]4ÚÀàà)/­iœIcØ[°ŠÙ»¾óü“%i>$è°W. –$þš`ž$›¡—¦žÍ Z¹¸Ö`Ö¦[‹ûýÙdn»†-uÑè”ny"˼Ǭh_®¹Ù#ùÊ‚}KèÜå£Ýí^U>ŽÿðŠuxð:·á­>¥£ê(J (–¢ÐfŸÒSZ®ì\+;Ý*‹Ö-ÓâÚN·ê6<Ë;ïÚ'üûÞ£qùR٢䊑Æ|¯Èfªf±.cÂöâÄÔÀëÌ«øÕ·÷vþÕšÂêNçºÁ'oxí=§¾0ÿcô‡æ¯nùÁ=¿:]µüÝ: d âПdñd+Ò\·Ä, :x¯ÅI;/Zx œ,XÔ~ÚQÍ«U“µ’;_W²] Ñè$+éõè÷ÖbÉoA1äsh®1ðÏ(лߤ0)´:9ö=†íÉCr!‘=ô[áÅHg{.Zykâ/«\ôó~ßdÁ¯T<¡TÊdºà¡Îü„É”€º ®´°çòìµÅÍ*‘6ú…”ĉ3?©ik§½UR!Z„Ÿþr8áÔ¬\©vF_þ0Ó :Öq—¿[çØX#ݺΚl©§¿W¼Ó˜ÊË™‹k4¾Þ¥Å³% u†4Ù÷z ‹9Âblæ8ÎŽ†Â¡ÉB8ÌPn©›v#1©Í.2*†5™8•ôáÊYò‚Ï—¶½[*œ‘`pZ1_U6+çÞ³¯Pð*Ï2#cU)xQ&úk=½mc‹_ÿݾß~ñÁëý«ù•wö{ [çèb ðæX@˶{\"õö·¬òoúñƒ›ÿæ–Bÿ’p@kPèèt/Ù‡tŸ×ñ©†Ö<ʇꌡG­o"M»ëÜ“…dR"×Ó&£i² 3‚ aµº!¤½`”Š‘tÙ”«[d,Å÷HQ^®êC…t¢òA¢f¹âx}œ-¦¹D¾8&¨>Øeá˺x¼¯1ªûmdÐüC| ´Ï`W«]éj~ë­–® n‡ùfKx‰¬Æ$oµ-1Ôª‰W.Ȭ6Ù…H&.…MNÉž{gΗp/M›šêUãOÂóå;ŽFæÁß"t„×Ó>´¦ÀcÆbÏÐÉTr²`sé”ZÛL·µ¶¡9)XºU©—x½.N¡ÕZ]†n«µD.Ô µÁãôåÕEH²z‚£”Éf”Ãh?î¿¶Zà§ÙæÖ[g™ÂIøÕÀ’Þâë°>«‡êê¸ÈW5“ÑeÎÔUàâ·Îåôú¥ÈÆçz‹¿-Žeâu($¡¬fs¼áwœ™UH‹§¯­2l‰†^‡¤•kù¼ ðÈö`Uk‚Z…íeXí4[ÍHyHpVe ½®–¥½‰<%{‡k[yÁ‹}d%0ý$§>“u+J.]hòCêŽâ?©%f÷I‘ÝÕÐÛ` ‚#†ìnY¹ºøâQêØ¬ál‘¿ŠÁ©õÔã‹S‡ªÐûZapjâzã•K’1Äu tó¨Z¡TL”q%àL—Û5YpëÞ)Ž–H2FSœ¶½¨”vïO>Ù¹ûÜ“Ç.ŒÊ¼ù§þÅ៭ý É9ã·k‘tñ_á›Ë‡mØûò6[PàA“¤“ÑŽšîÝH$ãs\¥KYÒ*?5­R:Í4“•#B6i6anRÙÙí1ï~–ÍÆöËåyp([©_3‡ƒ¢I$ œQÉfª+µ´Ù’ÛR,ÈeÊH†åÛ–ÎæmØáYýÉ¡áNGÇè×Í{pÕ §cQÇš­þå»ún\ûÁ÷W>y”Î]Z±4ÕͷĺçÎM.ë÷jõgúçy—õç{Û²îÖÙ«Úû¬•ûÄk3~ aÀ –ò ŽaäÀŠ/Y®•Óò‹¼VÉâ´FÖÍÒh\– 2—»äç°RO¡Æ»§Ê5€*€cÏG²r8‘û‘óB/bB`!ž·+vå;2ÖuôÖY²ù 6Íg¶äìê'þå‹¡½xfáŠÀTÃü9†d«û5˜OìzžwÊ<´ç"/3ëcI!¨ˆ•Ù·C¡¼í‚¶$÷RDA–4âr]J2Ý»i1Q Œ~cœ5ðÈÆ¾ñ•­!‹Â씔Φå|ÓÚv;Žn‘ÄÁ¹ŸØ27fJt,ݹ~½•Ѝ¸¸0´¨ÑZ|ãZxT D¿„³_Bó‚u|>ÈÑv_Žöú*%òIz'Úñû€\©Ä9+É)è58«`Õø­nwXCË+Û«Jn᪠Î/@è‡.I°´øæ£Å,ž@>Œ³OCó¯½Ì“p£%8~€îº|œîêos)KY‚©¿£¯¿|ˆêðΩ«d .sgÆßr ñÿ¾1(7›µ0&ÄHŒËCë´:ä‚”z»_ªUše»GB˜Ò+&º ‹'ª1…ŒdßÀ•’þ“ °Ê1UÃô\È´ýkàíRZz†7‡°#Þ5®KZž¦¨:ÅI³PŸ}U¨Í¾wZÌUvzèÙ‰O*Ѹ@_U¥6PÛ‡tMóV-Y‘Y™N¥\_¿ÿžE®ëÚ&ý뇛ïÜûñ›¢‰|*ë~òóC—è¦îgL«dßF~?Fž—J& Ò#òcæµÉűIrügyVœË¦ÃgxVœIwD£a& šKŠGçó ±“6©̓,kESe'ɾ÷¬!bŽJ©žr½½\p7UÜé*#s6?–:¾š•ËsÝ=Ác­Kî_“é ¨Í¤wÅÀ‚멼‡‘žíÏÖ)RÊ©H:ìP¦å›èLÌ4÷ÁM£çjt¿H6œÌ{?~³Öô™ð_¸Åúz éËÜ |š>ïQ£• Tg­³¦­ÅV>‘î·ZU(4£gy D¥BÛ^…b¬»dñ s³º“V«—=!“y=\ÅOrä¬Û*¡ˆ­†¯š‡B2½º`ŽõÞwÔc o›ýУóï][êÓÅÙë×çÖ6®’wuô,SP4ý윖C_zti¤w×éž±DþŸ†×\_¼Y±.º– }ç>9'°ÑˆÎÒ|…B™#Ùã4bÆ`t!ÚðO´¤aƒ¡Ôˆ•Ã=Ñd±§‘6E˜ a5Qß´0ZˆífîõÃ:~(ß½:¤²Ç‹¯íÆqÛÒívÆ.¾²32笿ËHudó×/(¾Ÿö•ɤRLü´£/Âq©„©ø~Ö¯G'ÆP’Òôåêi%FõBºî‘=4l<¦ z² ;¢=¯·˜)Ÿ,X.Á“wB¡°Y‰¦~Ž‹<+òõÛódÍ5ÐU“.£÷öm™a’ÌWöüÝw_>ªÔ‡0“ Û•iEÑCf‡¤ŽæCkÎêÁC8Gú<lðHÏEceÚ‰34s–c$Ù¬”½ž¶X-ȵX/eÝ‘P·á„;ýÎë9nº„µeÉ‹ Ëñlðˆ•ô«Þ†û3gG­¤Wå6Ò+êQÌ$Çã–\œ¼üR­'úZz’[ªõJkˆ²4ް˚Ⱥ½ Š#Y…¬mS}תˆÂž¤Û‰²^Ïõ…t¬ßçâŠÚª’‘| ݑϮ^\|?å3©„I'¤X´= ×à½ÐÀ+\´ë À¦SM§ç’æ¤ÍæãNJ$>/W‰Dß;}¾œ„eƒ‚Ó3‹ÈÞ·öÓó—Ý¿zùî¹ÞE¯¬YœìŒè“ó2‹WSt¦÷Ñ­çgWìîÙvp\x뽑Þ5¹[fÝUì‚F©UhžAp¯QˆŒh†—"a¿`ÅY¾À?›U,6_RœVÂ`·þ„ ëñÕiXÊ| ÙK¿`FØt•ã‘eºúâ‘ðJu0hH4iY_ ø«’¤sé|Ò¢ø²ßgΗr!N6]êx¬@¶ïóˆƒ$ÚñM ‰ªÕ6‰Yƒ3¼YŒ¯ŽÅÐ>à̈ÙP]ê’Þf²9Í1 ‘Èáï‘­“ŸMºNgÆü{Ó´×Cñ‹ð•”Þ{'›÷ÝòÛ¢ˆË²+ꬲrcævÁ@Íš¯ïîhìp4õÌ^œ¾ë¶í{êçt¶ö:fõ…z<±9ƒ ‰5… RK¶>µ½g¶'=0X÷pKc.éyݬ?ÙèÏ¥vêQÄ{ ñî”-ä©WðJ©Ì€â–³¼™,™LìÊ32¬=£ó’D¢tØ ½Áà–)O ý’‹çPòÍiö$ LÞÃUV!(IŸd‘«6àü«)oò_U …©hÚ®;uJ§Újó6®Ï ¬Ý ß’¹:WyNñ§³º½z»1›Š m;¦~÷h]Ȥ_Q4_FåpÒ ‚Vœå=dÊÂèdƒdÊVl:lG¬— 'ìÝNµ:NÁÔëtm§s"öDóæ›6 "Æ®öÍä|ß+JÖéu6lcœáW¤ŽÆ'³2y=ßéïZU8âÒ¯ïIwxmÌò,”!{­HÒ;ñÄÙ¨)»rT¤|FòöQšn‘DùPü¸ì(kðÑdö…Ìæ¤• %bÇhÑ#W®ü—¼>sĵ-aõ¿¸G DÐyÚ’+ù%KŒöÓ¡PuøŒw U´?H-xfÛ!}(=þ€J¾õé‘7™ýAÜn»î‡[á R®…§ëãòd’¶uKñà™¢ æg9LN:™”Z⢗FÜ„©m@Ü$Ðzº™wèÈ)h=­PãŠ!¿X„•PJZ‚ƒ+½ÙŒø$3RTˆuºR3æÑ삺îä%Åïå:î„Ø} ‰Ë•Jb.-yöTWNçr>ß#ïíÄý>yCV•É v%þûB‘J`Þ‹,Z×ÏõËÝ&g$ü–Æì«ƒ‰°_¿ïn…RæBÖYç6ÝEÛ—mÌ0÷ج־ÙðuoÈŠd!5›©â·Ð®ËâT[’dR¢&Š„ÓHn‡·’wÊ“j õš/QR‰àÇÈÛbä ?ój˜øV¼õ~å Yàâ~ÉíWÚÉHºçP—ôÒçaúT6C£ÿwå>é©+í²¯ û‚¼ÞÀS;©Û(ZGA\8[D­§$z¼2áiÙWÈÓ`;:47ÑÓQŠ~ú6 >UÅÏÿY|šdôñëŽx¢M?~óÍ71οƒzxXºõ =Â÷Ž(ùÓ¼ IP0Ò^¡»¥OÉÌŸvˆïžÂé”´}êq©T¼W<ƒÅ€`mÀ#<Ü5˜ýÑ¥Ë/Ï£Ÿû·‡Ñ`xþ.uXzŒáM2Yü|øo“=EKž’Ë•Šr¤ƒc¹¼°N¼è.ãG씩s«Mz°˜„¯àœw¥“Ôû²{ÑŽ†AÇq¨»:å7aEVR˜:ê (à8ž5êtÌ1˜z†3~ˆ¢d‘A¤‘s\>}ý_Ù¾”ö/B±õSï/4-]Ô·£s×ØÒ•î…]ŸYýܱõÇoŸ}£lô…¾Ù‹ŸøÛãí«¿Í~þó;¿¿C'ÌŒ^Šfëx}‡ý~»Žá8ã7a 0ùãg eG“â5Ò}>—%ȱlÇ`âY—+nùâB‘>‡Äp®9ל>IRVø}™üé4b€¼„~£y e§Êö¤z6­è¤m˧†;Ž>?xü¶þ[z™—¯¿k÷øÂ…Œ7£‡ŠngS6ÓÿùÙí?ØÎ꟟SøæäSßhj§S6ÙbIŒ¹-~^ö1Ð náÍœ€[“JC«ë7Xm3?Üp æywìR4ê hrUS½Fn 8ôV½Ë¥²ÔÇ,M1îØ•·Ÿá@à0Òés¹Ü¬|.Îs¸Ø›'~9—&ÿ LŸ9oÉåÓçó–ÞhŸlª"t¥*"7ˆU‘†úªìåÕ5‘Ne káfŸÃ»=¿ÿ OÙ‹/¦Ý_±Û\‡Æ+þ8æÐ0ÎlŒº´R“§sƒ^Ï"c* Õÿ¦ø£LÔJ%“”… ¿ì³0ÿš TɤРd(EÚÛJ} #JI²I)öñLÆ›²Ùäa[*…eS§ö”Ìa‹M}Iíó«ÙŒÙgq©Ìä|²(HKðK„eë#i2éz"’>—OçsB¢áU^À׬ÇßúæóX@9ötúäytñ<ÎÿÁéûœpðC?+ W‹Çó’%Q—†A2øYÌÉȤÊâ[+Š © I£ø'Q*š?BÉ%Ùb¥ÞŸú ´åñg_˜ŒL£–´}«ïUAU2™ú!5‹ìåÛi%’ˆ „ÁâoL=ïvËÙÃá ZG9‰ü0‡e£”]’²RÀÊЦ>ùœÏ•¹F‰¡CþFIš`"‡>WN/ÎäGüB“ø&’h‘ÃNΛ,þ½ÈËÐá'ò7ìç[\Ž7o[?Ò’¡• ”Óæ¾s¼cÓl'œ³äúeKñ?9†½y­ê$Xú‚Êï§­I³ÍÿƒÕêLÒ ÃqØ€ö¨©çbZ-–0V¤R&KkA"Q§%v†# š|½åt¿ÙF¸&ºÄaNU€I]3] EŸ1¬^¾hYpÑŠÞõíùã¹áå‹´™î|ßÒºÂò•›fý¯/ÜøD±¨«—6çîöÕ­m«š*óц6Kgc Ÿ† k:öÞx­yO… J¤g¢†¨!ZBh&IæÿW’f}_¶Nö>&ùC(‚-ѤRNè!ÕzÕ/ÕëÕë3¢çþ‡èOIÒŒ"zEÕ>£}FÖ}N÷KÝ/ÙˆÎqK¹Ïâ7xô÷$†ë Ç ÇQD_ø¡gþ2djEôˆÙ`¾‡ÐK_™~cÍzЦ³m±½l{Ù> Ñ‹zÇþŽCê\ƒzEº¥F5ú”Õè¿Hï ä\'Ò;Îw\7z “»¿F5ªQjT£Õ¨F5ªQjT£Õ¨F5ªQjT£Õ¨F5ªQjT£Õ¨F5ªQjT£Õ¨F5ªQjT£Õ¨F5ªÑ/‘V8MµˆÿÀ°†þTùÖ‚Qò‰&Ÿ6Òï‰m´’Ub›ZõئA@½SlKªî‘«ú±-«:/{Ô#¶ &¡Ä¶Ìf|b[%W•ïWƒÌ±Í€óm±]š3]žsé_>Ï1gÄ6rUlS@Î~QlÓ@Ï>!¶%U÷HÃ>/¶eUçå •}Il+€‰9/¶•ÀÏN‰m½¿|¿Ä9Øf€‘›'¶5ð»Üz±­ü÷Ï¡D)ÊYh rÚ‚œ…¶ g¡-©ºG³Ð–Uä,´9 mAÎB[³Ðä,´9 mˆÜ伌€ `Œ ô³ ìÔÚ v"E׆ÁFtfº¶]K¡öjÔÞ~oƒ`ú½Ýÿmæ]` º¢ên|n qÿÃèî]è˜@Ÿ7’ûpÿ¸|fÝÇÙ‰îŸÝ ö2,~$}ïDŸÇÑØ#èÜ.qvCdx|'ˆ¼p{Äqåqfr€9ó€½DNÐñÚ2Û+rŠïÞ€¸ÙÆÂ|^Köø™QÒŠ û£è÷0º6$ÊåZ½ sø¯Ê¶ÒûFÒÓftëvÝ1N¸Ú…ŽcDöWsPýêyµVas"𲋌·“Hsô/ðºÙK8Cç?ŠS{ƒÓP5Lô2&®„önôi'9zÈlKÚ,õƒïEwüGÅêB­}3z/­QÊ?x¾CDÒ‚n·™ï- h/¡ÑÅtü¥È˜ÛÑ=»Ð8X6›‰tv¢ö¡³%þ'Pó± ]ÛfŽŸ$«î&ðU4‹È€,húŒ÷•$_Ò©Àß("‘Áf2ë‰2„U ðÓƒžÃXÁ÷ ù Ãè&(ØHžÁ½ì¨²2ã"ª“èš`7„» RA] -‚v0ÆÀ6ÔÚLZÅu&<[­ÿäYÁÊáU$pƒçq3™æq¹^šñÂ×>‚þ=bXŽƒh~3g#èV[e%à> D›É™A2fé¡ÿ]D Â<ò:7Jú&³(Ý-HyÉJ8;N5NÐ)hjiï#÷î"óÁsL”-Ï(yb ™#æZÀË (‡kõ^-©Ò(ꢄ•UþG°Q»ÈÊÝ^~Ëi§hC'ÊvNðý#D U’“à‘FˆŽÇÄÈEèÏ~ï4 4HVSi½n‘4RöP#d…xD<W)°dšÞ¯ö¶-W­Ç¢í(I¦õÒŒèÚ«vŒ=N¼ã0Ñý8ÑÞŸd7“Ï»IT#Ü-Ä; ç#¢ÝX@f1VÅßÑ®Ìô~ˈ¥#-á^Ám#kù¿'¾Áö¢ã\»×Êu±·¯zr™l“gÁȆñ±‰±M»<=cã;ÇÆwŒíHyºFG=ã#›·ìšðŒO ïÞ˜êÜ>4>2èÙ28áÞáùwö¾=<ªòÚû}÷Þ3™L2·}™Ù3™ËžKf&cR1'ƘbÀ”ƈ1*ÒÜI0™Œ“!q#¥(RJ)FŠˆH1RDDÄp1\Œ)"RH‘r89H)æããàY=-|ÏÓ?Î÷œÙ¿½ßÛú­½ÞµÖ^{LeUcíÔpU¥RÝUÂßi¬ˆ’îhUYemxªR®Tb J]CãÊÔ††JeF ŒF¢µá¬)‹)õe@ÓX;«ª1C£‚›ª¢3•ª&˜Ø)«¸"&m݈j03¯¶ljC¸¬ŽŽÀüXm4jÊj£uµáªFÚ *×VÃe´ Ô©ƒ›jªª›©4Æ¢ á©é Hm]•RÓ­ÕŽÁâk¦*Ed=o¡ª>ºžT£U ôƒj`›²XMUT‰Õ”¾1²¨az šUõUuMä¶&ÕÔ6Ò{®¨'4êcJ¸´®*+']a²@©=j+‰‘@ ÒS×0£*ZQÖX¥TÔ”EË*bUÑ!§—WN¯" éL*–W‹Â²Ú(\زª®ª¾* [ØP­ÌhˆV~§¶¾l*Qê²W¶TšÞ8´‰ejdº;d_”00xŠis¤S½¨a¢ß¹ªÔÕj¬i˜^WITi¬#¾VUN¯NÕoš^£†©r Ð ˆ)Ó¦Ãð Í¯,˜ÞH6´Q©l¨˜Nïd,]­š:½®,ªÌ¨",_ûcUóÐâµ±¥L9SA—ª1@}é#®QQ[®€þ™õå uCšÜ žû(¾kf´¶vâ[Ü|:Õ54’=ˆ@TÔ6‚µˆtØj•0ð¨XUY=¨j†y±Fâs JYm}u(¢Rmc |xo¸jÆ •Eé¾Öƒ‘jI@ÕF`WgF®Ø*£hðÞ¯†íØ+ûX ÞA”“qÛm×lm™‹–UVÕ—E%êýû:ä§F¦GHwEC}¤,\ ¾ñÃpMÂ7|åJøMª6(“ ¼èÑÆšX,2vĈ3fdÔ_•FÀº†©Ñ²HÍ̱jˆ¬k§Ò6™ö5=Ø0RWK7;ËPl˜»4“x#è º“nb-H+e±ªt¥²¶1RW63}(%€ÁAqâ0ÔÛÁÎõµ1’ZÊgÆDX˜È‚päè•‹jÂþMk@’g''ùÖ¦“5W`O kTÔ\£Ù …¸®›.÷µö apû`m$r²ÙW§ƒ„¿§-Nó&„H ¼b0¯ ¦‰!Y·S k%¹ ²6øìL“Ẇ²ÊáÖ+J¹Qr; 4_A’Š@žª¬"·IæÔTÕE†[á™CÓɆÐ,ÖPS[^ :g܈ Ð QYU]¾œQÖi¾ò+úêIx}Û‡úfá œcôο÷Q³Ÿ%'c˜ƒ—ßè|ŽÌgŒ7:ß` óçÜè|£‘θÑù&™ÏÖÝè|A€ùjv "ßÔrt>ÿYˆÕéÙjve!3Ô%OBíôT s¡ÚzÞPæAëgè¨u …è´mBÏ¡íh êA¿DŸ _¡è×èoøfñ)¬eÇ` ¨åΉ½×pêá°g8ïÎç#ÀRœÀùSàœœË€s pþ8·ç^à<œŸçàüOàLN8êÃ8™{®á4§8¿œã€ó~à¬Îp¶çπ󗀫óuàÜœïç'Àùgù+p~…~yàtg8¿œw çdç} ç÷³8 œaàœœss p®Î À¹8ßÎO€óÏ0òp~…~…“€Ó œßÎÑÀyp çä]ÃiNðvà,ÎjàlÎ'±8_ÎõÀù´>ΣÀùༀ–`ýóÀéÎÑÀy/p–'ù{? çTß} § 8³óàlÎ_Ûï€ó-àÜ œ§€óú–P; …8-†{x?œ5ÀÙ œÏçjà|8{€óp~Nâ[£Fš„¬¼8|ò²4 Csšþ¼üÂH~^¿&i´ŸÅ+`cï'ŽõYœ®ÈËËkÎÉéÓ¨@Vs[¼Ô¨i;«V!µº´ÝiPsH­Š´Çãí¥‰j”˜ k4ÍmmmKëèœHÛ@¼-Bºrr kòóóÏhX¤as´A¬ZÔ ûiÿÐøD¨Ðý:aP~ŽÒÞ§á†ËÉ9 Ý… ]xÖåg©‘:qo|¼³k{|{¼ †q] ÑW'_u“/Ÿ[à¡J²hY¼´ë‹>ëŒ}Y‘ÌHf | –,½[¾[Î…#GîˢܗélrT Ù¨ôêqé°.1«zGWW$3Y­žÛÓœ¨Á‰‰-Ýjuvuwï ’k5Y•]äS™I…%@ULŽ;@èΫÇTt{œä{ÖÝ}¹««œ2kû†˜Âô‹¶¯/ã:ëT¥¥õ•~ÎÒDÿ7&…´YÕ;ɧzð™Ò½²„¨×Ò³«¯Ù>¿g I^Z:08X3’ ü|˜8ª¦%þj¯ÐT´}¯}ôþÑûåRrè´(QûWHºeèV4 ŠR;d›Ûáèƒ' f¸®.jw ~È6wuq*¬K8|øp;­x¯ÔÃä}€©¬ Oº67^g“ë;£õátòæ^—NÞäMW Êbáo掠xyb âÊ`²UÀ 4v*Ì@udÆZQ£¯¾BI(÷  4"ö‡èa¨õ~ŠÚ FÂ÷å*(“ÎIe•·€R@c Z» M„‡Údp°ú½ÛOP+zfh6F Pa‰` ÕdäƒAu÷ýFš|ûØ×¼«ZˆP7i ú”¥‚13Ñ¿ @^~M÷¥ =Žæ@hFìøÂÂ|4®èG?TДIE÷(`l"CK„3Cë‡J}Xnš€îE%PÇ’ï¶C3À™Ÿ‚JÌO„jÐo· ï‚gÞŠÐÏi¿´ÒBŠ”ù©t:nC9ÑîA÷¡Á1«á-#ŠšÑP+.bVydEn”õú÷ óGhzˆ~cZO¿yC]ù,j¯ÕXÁ–SœF1JqÅVŠó*Êêbì"ŠË)®¦ØIq Å]äk3¶—âAŠG(öQŠû)¢x”âñ†heXuŠb?ÅóÒ‘ QTSL¦ÈS”):)úaïÔ!Š#)Ž¡˜E1—b~cm¸Z=‘â$ŠQ,¥XM±Žb´±¾"¢n¦ØJq>Å%;(®mly«z#Å­wQì¡xâQÀQêOSÅE—R\Aqŵ;)n"Y"Ñvg–þµ.ßÿÓ†¬xcH~çKE~' 2]"d’yÿy½škz¯í»~†¬{3ÈA^7A®þI× äÿß9cÈË7‡ ]Í ý$¾úÄÅÀzsh¹ItÍ7‰@ñ‘…g·ž7sEþ⯋Ú/O¶?cxþþ#d@ÿô›8ßøbxrß(J7·A•3-F«ÐF´ D'ÐyìÁ£p..Âå8Š[ñ"¼oÀ;ð|Ÿc†g<Ì(&—)bÊ™(ÓÊ,bV2˜æ"d3Ù|¶„­a›Ù6v)»†ÝÌîa³§ØNÃÉ\Ëäò¹ˆV­ô\öÌuí ÃÛœæï´¡Öâ8”€¯´Õ©Æ o«×ÌW“_š¥mŸƒUíÕ^<'m<'GϺµÃWë\Ó†(4Ì®ÍØÅÃÛw ×>]×^qmõ·´;‡óåô _§þºvöuí®áíÜÜëÚÃÛw9‡ó×·ÇøÅ×µ{‡¯ŸpÝnÞë¼®­\×ö o1´Í@®ç-P„†Îmß¶¯÷¥ž'ÙÏ÷'ž‹¹o›]Ü?x~`âÐyýà¹Ä<ü®Kš†ïBÉÖáZ>¸ô´÷\×Þw]»ç›m|m»÷ºöÑëæÞ~˜»®­ÿmí?h_·‹«‡ïú#uÃÇé"¿3–›ŠNÁ»ÒjÅjô¹ ä-‰GjË路,ë¤7¹õܫĦx=³1eDÊHÚ.ƒy¿‡{Ñà~—Hfv!òTÑÐ7£\x;)†w«jDÞ5x'~ïÅµ–W ó€³ìseV¼±µ‚&ÍðÓo\¼û,A+Ðj˜» ­Ep^yq œ7£mY´íA½–×cYgÙø;ˀ륋÷JoJ‡éù-é8¿í#ôü–´1ÐÚø–ôb¸O¤Ë­‡¾ýôü–ô!œ_…öz~KúÃÐøÁ¡ñ†Æ ¿M%¾C%n§wÒžwiÏnÒcy•êÙIõ|èiÙ@{~O{6’ž¡‚D“Bö ‘¿‚­‚·Ònþ=q‰ô"´¦Á^lÄ›ñV¼}ЦøCüþ÷1s™66ƒ{‰{™[˽­ã~G¬Ímärq‡¸¹ÃÜ'Üî(×Ç}ÊçNrŸs§¸çÎsã.p_rÜÿá.rÿ—»Äý'w™ûJ…øéü ~&ÿþqþ þ§ü“üSÂíÂÂ}ÂýB‰ð0Y˜"” åB¥0î, ½#m—vJïJ»¥½Ò>é=©Gz_ú@Ú/}(þ ”>’IK‡¥O¤#¶m¶.ÛÛ.[·mõª¯ïÛFï;ƒúî|±ñÿã;¦÷Ë6¡ÁzqÜ/ñúV¸žÇô'b$ÚèOÿÀÈzô*¼ãoƒãÔ Ç<ðÿ=àïÃ1}€ö£Ÿ£¿ ÓèYÌaZˆŸÇ¿E¿Àkð+h)~ ¿†–ã×ñëè7øMü&Z߯o£ç¡–Ø:p7îF/à}xZ‰{!® Æ´ ‡Ћø>‚V3v& ½Äd3w =ÌÌhsszù3õ0ã™ñ¨—)` ÐÌýÌýh?óóúy–Ù0»˜]XÍ|Ì|Œ˜?3Ææ æ œÈœgÎc-ó%ó%Nb÷³ûq² «È·p ªlR%ª1¯JR%aA¥S±¨’T–Uw©îÂV¾‰oÂ6¾™oÆ)ü,~¶ó-| vð³ùÙØÉÇù8vñ­|+Vø9üìæçòs±‡oã_Ç^Èu¾W°V\$8…±ø~!KÈÂQ!GÈÁB®P„cÂ$a~J(Šñ\áAáAü´ð°ð0nÁÏ?~Œç eBþ™P!TàùB•P….Ô5x0MôàgEŸècòE¿`Æ‹!1¹G!Ž`&Š£ÄQÌÄLq,S(N'2÷‰?K™Ib­XË‹ubó€Ø(62%b³8“yPü‰8›yXÜ/îg~,þA<È”Š—ÅËL¹%#S!1ÃTJðaª$Y’™jé×Ò¯™©Òo¤ç™i¥ô[æQéEéE¦^Z#­aÂÒzéU¦Aú£tœyL:%bfJæ(3˳¼ÌüÍòŽÌ°·ÈÉr2Û Ûd‘}²}Lþ9›Ê¿’—±1y¹¼‚m’_”_dgÊ/É/±³äõò«ìOä×ä ìãòëòëìòy+—·ÉÛØ'åòNvŽÜ-÷°Oɽòì|ùCù#vü¥ü%û ù’|‰]lͳŽcŸ³Y‹Ø¥ÖëC쯬“­°Ë­Ö v…µÖZË>o [Ãl‡5b°/Ø:m›Ø•¶wlï°/Û¶Û¶³km;m;ÙWlïÚÞe×ÙvÛv³¿³íµíe×ÛÞ·ý‘}5¥ ¥€Ý‘vÏ—M®Ë$×f‘Bùïò<ÛóÁHÈ)ƒù€FÞ4’:T¬ŠEI| H| C$>ÐÇ:tXePÐ'$JÐ%èÄßÑQêïZêïIà›.¬'„wÂïÂÝăðnâAxñxr‚_à©_¿`Z‰Õ™nbQæ}bQæ(©¸~î?¸/¸sCY|óÐüO½ª=}u0zòñ\ÈÃó ×·»€fâm÷@æ%y².ɹq;ð ðŽö[Ⱥ/ãµø’/![ÞE²$äÈÈ@6ü²àý¾d÷CÆcTœJ¯2 NQÝ¢òä È·ˆé1FA¦¸M ™²äˆ:È  +´ˆ‹³!/@N€˜ÿ´BzžÄ;DûåÒ)ˆcÄ/Ä.‰\ˆÛßÈ+ n_"ñJ¢bu‹ü–¼ât'Äçny¼WÞ'¿'÷8…(= ÿA>(‘z "ôÖq¡%Ö­‘è„Ø¬…˜Œ|ÓÀz_ûÀÿÚïæì7<Üú?$†à€{û€h¸†ûP§Ö%ëx¬sê|ºn¤nŒ.[—§› +Ôë&ëÊu5º°.¦›¥‹ëæêæëé–êVèVéÖê:u›t[u;t{t½ºƒº#º>ÝIÝÝ9݀ÓkõF½Yo×{ôA}†~´~¬>G?N_ /Ò—è§è+õÓô}“¾EߪoÓ/Ð/Ö/ÓwèWë×é7è7ë·éwé÷é÷ëéêëOéûõçõ È 6$xƒlp|†tÃ(C¦!Ûg˜`(4&Ê 5†°!f˜eˆææ–VVÖ: › [ ; { ½†ƒ†#†>ÃIÃÃ9ÀᲑ3jF£Ùh7zŒAc†q´q¬1Ç8ÎX`,2–§+ÓŒc“±ÅØjl3.0.6.3vW×77·w÷÷OûçMȤ1éM¢ÉfRL~Sºi”)Ó”mÊ3M0šŠM“Må¦SØ3Í2ÅMsMóM‹LKM+L«LkM¦M¦­¦¦=¦^ÓAÓSŸé¤éŒéœiÀt™çx-oäͼ÷ðA>ƒÍåsøq|_Ä—ðSøJ~Z«j©6~¿˜_Æwð«ùuü~3¿ßÅïã÷ó‡ø£üqþßÏŸç/ HP É/ÈP[ù„0RõU®/L„ªê!xV uBThf s„y†N¡]X",V kà¾ò„õÂFa‹Ð%t =Âá°pL8!œÎ Âe‘µ"/Ê¢¢<$ŽLjYbž8A,‹ÅÉb¹X#†ÅDv«Ø&.‹ËÄqµ¸NÜ n·‰»Ä}ï‡Ä£âqñ”Ø/ž/‚×k$½$J6É#¥ i´4VÊ‘ÆIÒ$é!©Tª–ꤨÔ,Í–æHó¥EÒRÈ«¤µR§´IÚ*íöH½Pë‘ú¤“Òéœ4 ]6sf­™7Ëf§Ùg™G™3ÍÙæ<ós¡¹Ø<Ù\ižfŽ˜›Ì-æVs›yy±y¹y¥yy½y£y‹¹ËÜmî1063Ÿ0Ÿ6Ÿ5_0_²0Eo1[œ¿%Ã2Æ’mɳL°ZŠ-“-å–Kâs–%n™k™oYdYjYaYoÚ–M–­–]–ËËKŸå¤åŒåœeÀrYæd­l”Ͳò[º'È—­jk²•·ÊVŴ޴ޱfYs­ùÖ‰ÖIëÊ­Ó jk²¶X[­mÖÖÅÖeÖëjë:ëëfë6k·µ×zÈzÌzÒÚo=o½hC6µ-ÙÆCùé´ùl!ÛHÛ[–-×–o›h›d{ÈVj«¶ÕÙ¢¶Y¶V[›­Ý¶Ä¶Ü¶Ò¶Æ¶Þ¶Ñ¶êªn[¯íí¨í¸í”­ßvÞv1¥¨S’Sø9EIñ§¤§ŒJÉLÉI¹«(¥$eJJeÊ´”HJSÊ씹) R–¤,OY™²&e}ʦ”­);Rö¤ìO9œr,åDÊ锳)R.Ù»ÖÎÛe»Óì#ícìYö\{¾}¢}’ý!{©½Ú^gÚ›í³ísìóìíö¥öû{§}³½ËÞmï±°¶³Ÿ°Ÿ¶Ÿµ_°_r0Cï6‡âð;Ò£™ŽlÇ8ÇDÇ$ÇdG¹£ÆvijqÇ\Ç|Ç"ÇRÇ ÇjÇzÇFÇG—£ÛÑã8à8ì8æ8á8í8çp\vrN­“wÊN§Óç 9G:Ç8³œ¹Î|g¡³ÄYê¬q†1ç,gÜÙæ\à\ì\æ\é\ëìtnrnuîpîqö:::O8O;Ï:/8/¹—Æ¥w‰.›Kqù]é®Q®LW¶+ßE"Û5ÁUè*vMv•»j\aWÌ5ËwÍuÍw-r-uu¸Ö¸:]›]]®=®ý®Ã®c®®Ó®³® ®K £h½"*6EQüJº2JÉT²•­Ïè3ûì>/èËðöõåøÆù |E¾ß_¥oš/âkòµøZ}m¾¾Å¾e¾ßjß:ßßfß6ß.ß>ß~ß!ßQßqß)_¿ï‚ïrª:UŸjNu¦úS3RǤf§ŽK-H-J-I’Z™:-5’Ú”Ú’ÚšÚ–º uqê²Ô•©kS7¤nIÝ‘º/õ@ê‘Ô¾Ô“©gRÏ¥¤^ös~­ßè7ûí~?èÏðöõçøÇù üEþÿ¥š?âoò·ø[ýóü‹üËü+ýkýü[ü;ü{ü½þƒþ#þ>ÿIÿÿ9ÿ€ÿr€ hÆ€9`xÁ@F`L +ÈL L <( TêÑ@s`v`N`^ =°$°<°2°&°>°1°%ÐèôŽNNÎ..™ &¨ŠA[P úƒÁ1Áìà¸àÄ`qpJ°:XŒ›ƒ³ƒs‚ó‚í®EÁ%ÁÁÕÁõÁMÁmÁî`oðPðhðxðT°?x>x1 )±4uš>ÍœæLó§e¤IËN—VV”V’6%­2mZZÄ}0­)mvÚÜ´iKÒV¤­N[Ÿ¶)mkÚŽ´=i½iÓŽ¤õ¥L;“v.m írˆ iCÆ9dyBÁPFhthl('4.T* •„¦„*CÓB‘PS¨%Ôj --- u„V‡Ö…6„6‡¶…v…ö…ö‡…ކއN…úCçCoA·¨oI¾…‡ŠíYúm;¢¿S’Hÿ­›žþnŒEÙÈ‚rÐÝÈŽ& äCá {ÑdDSàJQúý÷z·Ó‹—¦£'`ÅoЋ¨­AëÐè½óÞDo£ôzÕ£½hŠ¡8šP/3Їè jF‡Ð¿¢ô'8æ OÑIô:ÇÏÐ8æ£~tÞ0ƒ–`Ñ ø<­Å·â[Ñ«ø»x,êÄY8mƹxÂåx*:„kñ£è(®ÇÓÑŸð üStŠÉd2Ñ_™Û¡¾>Ï<ÈT  L 3cf)³k™W™Wq³‘y'3o0o`=ó&³˜mÌ6lbz˜Ì3Ÿ1Ÿa9Åü‹Ì¿1ÿ†ÍÌ¿3ýØÂœcÎa+‹YŒm¬ÌÊ8…u°NlgVÁNÖÃz±‹ °ì†êjöÊ ßÁ¯çàqüAþ~”?* Aƒ)h^!,–ãÕ áyüšð‚°ÿ^xQx¿.¼,¼Ž7 ooàÂ[Â[x—ð¶°¿+| Çû„¢osTL㫘/Žgìâ=â§ø#q2㧈S˜PE•3#ÅJ±†¹U|B|‚#þT|’ù¾ø”ØÎŒ‰¿`Ɖω˜|q£øS-±’Ži• ’‘ù¹ÄKó¬d–ÌÌB¨¨üÌ"¨©‚ÌóRº”Ît@u5ŠyAÊ“`VKJÓ™-Ò ©ù*©MÌÒŸ¤>æœôÒYæ<¼s$3ÌzóH6 ê§Z6Ãü¨y[mî°`v™…µøÙK– e§Xê,-\…e¶e³´[:¸§,+-/sK Þ9­°|j9Î=o9a9Á½ÕÏ)n¥å´å ÷¢¥ßòWnåo–¿qëe‡ìà^•Se?×)å ·AÉéÜïå y$· j£ïsoÊ·É·qï@]TÎuɵò4n»\'×q;å°ávÉQ9Æí†zé'Ü>ùqùqn?D‡_£ø:Å7)î ØMqÅ^Šà½Î RTá›ߦx„üŠèµ†ÊÖÐ9:G3ÔßMqÅ^Šd•–ÎÑÒíPÏŸ“h2•–L¥%õì ØMqÅ^Šd­ŽÎÑS ºÊ@¯MôÚD51Q &ÚÏSù<åéZžŽòT>OåóT>>BgJCø6E"ÇL{ÌT‚™ö›i¿…^[èµL¹d:S¦3eÊ%S.™rÉ”K«$Œ6ºÊFWÙè*o§ývÚo§ývÚï =Êë 6ywRÜHq3Åíߥ¸—âû?„ݤs_¢øÔn¦¸•â'€OS©OÓѧéèÓtôi*õi*õi*õi:ÿ:çÚóÌPOà<ªûn*m7•¶›ÎÜMuÜM¥í¦Òv“µIÉtôçÔ¢ è½. ×ítm;Õ¡®m§ý ©ä…tt!]»Ž.¤’RÉ ©V ñG€ÇèÌÅC¸•"‘óíyŽJxŽöÿWy_fEqµ]˘¸ÝÕÝ·¹†ëÈ&² {ØePGTtÆ .;È ›(Fðû’¸åWƒ‘ ¬3 c"F1jPPAQqá;õv‚äûóü_ò|ùŸ<ýðÖ¹ÕU眷ªnÓ=·›¨_ ¬„•J´©D›JX©„•JX©„•JcÚÖjôZ^«Ñk5Ú¯EýZÔ¯EýZÔ¯CÍ:X_§Ç×Ð- ŸnnîîîÒÜjDÛ¦Àdƒ›[€Zk äTèNE›T´I5õ»€{€û€ºf†p0¨¡±á¨Ï„¶LhË45Û€»€{€û€ºo-´© uÐ ßXnA¶à‰ êmè·qÖF_gm跡߆~[=Š–®Á-@­ÇC ê=ÔG G û°å£¥–>lù°åÖ[¾žmBm1Š^QôŠ¢Wí롾ê롾ê룦>ìÖ×c"²õ7\´æˆÙ„]€¹À<`~€ZÉs PS ê Q_„šb` °X Zކ\ j+e Âeúû'–ëˆP{õ °5«qö!´|Q6'Ü­‰jÍ—Pk{ò>Ôï×m$CËot¬¯ÛH¦ÛHš4}–…Âs€ã€ã“€S³ž5­¦gggãüË8ŸlPëJÆ~œ ÉИ ÉИl4¦£m:ä°ÁqÀñÀ‰ÀI@Ý/ô oÔãAø¤F݃äíµÏ ®-EËGMÍvȺMÔà8ìùÚ㙨™ ,N&€“±«o6­¦§gïÅù}8?ÇàXìÜÛ —'@­qŽÑX…¶ /58XœLu¿¥A¿ðõŒ>¡Q÷ yd­£Ò ®?€–Ðò€©ÙY·Ykp,öIì~º†p,°8˜NÆN¸Ù´š œœ ¼ç1<ÕàXìsÛ —'@­1ÕhÌDÛLȶÁ±Àrà`¨ûÙA?‹k–„OhÔ=HÞYëð êú´LAËS³ ²nSÏàXì$zCÈÒa G(uæAYGP>mʳõOâ;œñ7‘4¦BC¦FëA]cÍCMªÉ±[Zq`¶þö@N†œ9rr²‚¬ {=ÈiÐLöñ= ¼¡ ÍäeAmà[4ÈZCÏ&!ïIºH U6‡o5ƒ<õ5Q_Ñ»fh'¾ß{ÁZ—È^©Vc1Ü‚¼,Åä§{á™–Ó + ™WZHÇètj/u~ ™¢U&,Ö"YRVºuµ‚:Xª¶µ¡·6ÎÖ\'Ѳ<Õ#ð´)w£ <Ï-ƒº· V Ể.gœ!uù» „UmÜ@F/¾z¡­À*àN¬™íf íÅhD°3EÐÓ‡¬`Vr]“Ãj9† 0†³1ؘ‰ § ¸XI¨ŸxÞE±5(Ÿ2åÙú'°‡í¡ˆ”:Ã|y×SÖirÍ ö |ZdßXµ³0³0«٨™šÙX©³1Ö”]ƒ¯.‘cNæèÙ°G Ëœ«GV¾‚ñó¡k>Æ}>VêÌ^ÖkFT’^9‹Ðvì.ÆúXlVÎâ ö*У#]K / d´\5÷§L¹ åfLÿ—Ô½—«„¼ #¬u­À™8C8Æ‘>ñ×™ÎÂõ¹•°¼­WÂÇJ¬ÓJ0­„/•Æ—J¬ÁVa‡\…ž«¡e5ä5ט|\Ë뉯ÃÙu°1?°„6«‘ׯÎ }BxT¾UO#E“-Èl· ÿÜ‚œV®ÎõHè\R >Ÿ­Q(8_#X/”·ïA^½¹1¡ª£kìÓ¨I7¹1® ôz$|TÇ'È©3!gB¶!Û]È.d²9škèÑÖ¹4¼±ƒµLePøV/¸ÚÐk™×D–c§å-á[Jp}úÔ§ £NÑs£¯)À:5XäñN Ížºùt𹮨Ï´œ]Ș3B¸–Ð+Z_W@GíѪ6,êýTjÔk‹× ê`)Œ¶aèEfGc©e+ÑÒ‚§v°ŠPîBù„™§à›‚&…Þn€°êò=Ð…½”®,ôg¼`Eë:´ˆà\$Ñ:}½¢ «€;±V_ü`EóºÈRê¢gZ1ò䘹æxWúj£>ÎÖ‡ŒÀÚDqíRX+ºJ·-pU¿n¾ÜÜ Ü Ü ¬þ¸E£ÞK_Ö:À¤FÒ”[Mù‚)·›r³)wšr·)I»“¡½!Ü |¸¸¸¸¨½É‚÷Yð> ÞgÁï,ø¿³àq<Ž£}íãhÛ8zÅÑ+Ž^qè£o<è †qÃ0nÆ Ã¸a7 ã†aÜ0Œ†­À°¶ÃV`Ø [a+0l²áq6<ΆÇÙð8gÃãlxœmÚÿ¸×™{z~šBOSèi =M¡¡)44…†¦èÛ}›ál ƒ;¸j…•hÙ-[ÀJ XÉ•XÉ•x›=9Г=9Г=9ГƒñÍ1ã›cÆ7ÇŒoŽß3¾9f|sÌøæ˜ñ-Åø–b|K1¾¥ßRŒo)Æ·ã[ º„Ó¨_MøäÇ|øòáC>|ȇùð!_ÿvžp/ðEà³ÀçÐ :1âùñè/€þè/€æh.€æh(€†h?m !¢o!ú·Âà,4BC!|+„o…ð­¾Bs!4·BøVߊ ¿ú‹ ¿ú‹ ¿ú‹ ¿ú‹ ­ÚŠ ­ó_dÖS‘YOEf=™õTdÖS‘YOEf=™õTdÖS‘YOEf=ÿbøW ÿŠá_1ü+†Åð¯þÿbøW ÿŠÁ¶l‹¡»ØøZl|-6¾_‹¯ÅÆ×bãk1|®Ð>¹BûDø„³y?á&È¿îÁêânvµ–½&‘‹‹½æ.÷ –ÿ< |¸g÷ù5Â7!¿ üúOˆšï`åöà,ðŒ¹O¾¨eüýG6*`Φ™¬ÈZ-BÎj³$æ³Al1«æú‰qÙ©fŒEyœ®9[óN<—÷åù`~3/å£ùD>ƒÏçËøþÿÿ5ÿ­¾'Ç_åoòƒü0?Æ¿ ž/ðjþ ÿ3—j>å§Hm²¨-<‘%‹–¢ƒè.z‹«D‘&JÄ]b¼˜&f‹…b‹Š!âq‡'¦PÍb±R¬›Äcâ±Uì/‹ýâmqH_ÒØÔ™Ò‘õd¶l*[RÏoeH¦K[F©¦™l#;Ë<ÙO’7ÊbY&ÇÈIr–\(—Ëuòaù+ù”Ü"·Ëjù2‹Ê5ò!ù ùkù[ªÙ+_•oʃò¨&2:ÓÞ[ÄšðùQþ ?Í¿áßó3BŠ$QS¤Š ‘)ꈰPÂóeY"o“¥ríl·Ë;äò.y·-ï¡}n¬'Ëåx9AN”‰¤,ë²Ð„âGøÇüÿšË¿B„D ‘"ÒDº¨E;³%l1O¶§Ýòy­¼NÉëå`yí7É!r¨&‡Ë›åÚIGÊ[ä­Iõ­MxÆzŽd7až¹n|ÁçŸ×ÏxxŽ$x†¤5žÏfh±[þÞ´ÐO™ÄM Záþ£µ5òa<}{¶-g©L?®ŸÚ²Jîa!ŠdÁÛ«kÈšËM”E0Úí©¤܈ÏëuIŸ×ËGÌçG‚ÏÔ+›Õ’«äj¹€âÄ"¹XVÈ%r©\&±B®”•Ô&¤ÇŒüÑœE_²tù¤|’fS°¾,.»É+dÙSö–}ep±Ñ»Ø„åÃøpÊýFðb>’ßÂoå%ü6ÊGñ2~;¿ƒßÉïâwS^xÃÇòq¼œç(KLðI|2ŸÂ§òi|:åŒ3ù,þ(eƒŸñ/øIQ,FR¦w+å·‰R1J”‰Û)ﻓ²Á»Åhq#ÆRXN¹á1Q$Ä$1™r©”)N3ÄL1K,§Ä×â´øF|+¾ß‹3Ö¹RR˜DÙaM™,SdªL£¬0ƒrÅZ²¶¬#ÃÒ¢QQæèJOF¤/ëRÆ£<²¾Ì’ ôo_)¼T6”dcÙ„rËËÔ1uB}©>SŸ«“êˆZ~?üAøPøÃðáð‘ðÑðGáß„…‡? >þ<üEødøËðWáSá¯Ã§Ãß„¿ þ>|Æb·„%­•dÕ°jZÉVŠ•j¥YéV†•iÕ²j[u,eùöû-û/öÛö;ö»ö_íƒö{öûöö!ûCû°}Ä>jdlb³ÛŸÚŸÙ'ìÏí/ì“ö—öWö)ûkû´ýý­ýgì3Š–”’*¤’T US%«•ªÒTºÊP™ú·±ªŽþm¬²ép”KGDùª®Šª˜ª§ê«,Õ@ÅÕ%*[]ªªFª±j¢šªËT3u¹j®Z¨–ª•j­rTÕVµSíUÕQuR?SUÕUuSyª§ê¥òUoÕGõUWª~ª@õWÔUêj5PªAêu­ºN©ëÕ`uƒºQݤ†¨¡j˜®nV#T±©nQ·ªu›*U£T™º]Ý¡îTw©»Õhu£Æªqª\WÔD•P“Ôd5EMUÓÔt5CÍT³Ô½j¶š£æªyj¾Z –ªeê>µ\­P+U¥Z¥Ö¨îê •«z¨…j‘Z¬*ÔuT}¤>VŸè_üª/ÔWÎÇÎ'Î1ç¸ó©ó™sÂùÜùÂ9é|åœr¾vN;ß8ß:ß9ß;g\ær7ÍMw3ÜL·–[Û­ã†]˵iH×u=7âún]7êÖsë»Yn7î^âf»—º Ý–n+·µ›ã¶qÛºíÜöîÏÜÎnW·›ÛݽÂÍu{¸ynO·—›ïöqû»ܫܫÝn¡{{­{[ä^ïvopotor‡¸CÝaîÍî·ØéÞâÞê–¸·¹¥^¾×Ûëãõõ®ôúy^o€w•wµ7Ð+ôy×x×z×yEÞõÞ`ïïFï&oˆ7Ôæ ÷nöFxÅÞHïïV¯Dÿ¶Øå•y·{wxwzwyw{£½{¼1ÞXoœWî÷&x½„7É›ìMñ¦zÓ¼éÞ o¦7Ë»×›íÍñæzó¼ùþGþÇþ'þ1ÿ¸ÿ©ÿ™ÂÿÜÿÂ?éåŸò¿®Û?º,vy¬y¬E¬e¬U¬u¬[¬w¬O¬oŒò%Ö xΔÿŠÿŠM¥ëËãl?Á?g3ðäé,1_Ìgñüé&<ú&ž?=€çOßÂó§Áó§oãùÓwðüé»xþô¯xþô ž?­mÑtñ:xþ4¬ßmÀ«í=ö>þž6ýƒþå7Ãqœ–ü˜ÓɹM¤à™ÓŽþ>ÿu1ÙÃ? æâ™Óѥѥba,k$źƺŠûbÝc=Ärä65(·ÑoélÃ:°u «óN¬;ë…wÓpÖÏùïm‰;0¡ö¸”gª½n'ÂÝ.¨ïMòoܾ@CµÕíGø‚[@¸Ý„6Ã5žóª1kN^uf¹ÈÛŽð üa¾Q?댿Uzøûk®v´L¥þTÇ|ªóÿ ©éÛçM"iŠØò“Hù¿'ÿ—¢äÿOÑ‘¬Œ$Ç‹{~ˆ’äíu4ÖM”üsc¥ý½bJ(¥<Õ1óŠ–‡t³?¢ˆÔòEGƒ¨xæïŒ‡Îÿ%þ4 ¶¡½«5ÅÀ¢ßÙØò¯ˆuK)v·; W«5”{¼¬Cg:ß8jQË‚|C-§lãsû¤ê¨s ÕÉ>m"%EIwŒ;Öç–»ãÝ îD7áNrg»sܹîà>xÑØúåÿ ºÆþŽøÚÁíèvB”írÑ8Û›"m_÷J·Ÿ[p^Äô7cîðPÔ=?æÿGD]Uí÷b-Ô}ÿ}ü¥¨ûCÄí…·T3¶ífɬšíe=ØKìOT¿ŸfØG<‰ÝŒx÷ýhFùŸgGëü£õÜþÜ|þsìµ4ø³¦½³ñûçò Æ~ ú—Ųf¬5ëÀ7R~ð0®ÄÁÔOðŸãÉ€:_à.äÿ‰gô ‹Í¨½rnt ¼‰ÍúÑ;Ó~Ƕ²çÙ lÛÎv°lÞŸ¦ß§ðÛGk[¿Gí ;Ä“Â{Ã/ž{Zc¼­£ÝÙîcØ×Ø×Ú×ÛCìÛ.xÚ4{º=Ã^ªŸí·—Û+ì•”­¶×Økíuöýözûûq{ySÚFõxš~óÙ¼ñìÜ[Î"y‘"¼ÙL¿õ7ÍeG)?\l¿¬Ö«‡ÔcÞcÞ“úÍA¸[]EOÊaõç…ÏÑhS>G2åss¿¦ïT]ýÖ#Šû7Ó7èrºþ™Dñt6}ƒºÑu΃¬wY®åcF¦ñ¦Æ³êž«¹ÐfÐ"bEÔGÿ>£ÊŠ©gìJµé‚7ï¢}Zº=æÜ›iå=Wò'ùÓüYþ;ýŒßË_æ¯òýüMëRÙªoeY ¬¸u‰•m5¶š[-¬–V+«µ•cµ±ÚZí¬öV«£ÕÉêeå[½­>V_ëJ«ŸU`õ·XWYW[ÃãÂãÃÓÂSÂÓÂ3³³­ÎVwku­u½uƒu“5Ôn°FZ%þóþ6‡¿Ë¯ò÷X ­FÿÂï¹Ä»§­\«‡•gõ<ïÍw4C™CØÞ#GæÃæ/—$ŸùÓy“¬ÿ(È0õ§ôò;~£ï}î÷5Õ÷û(~”ˆRQ&F‹r‘H’JŸ¿èA+𼃴œÄzÐz=ï «?š^pè{‘ç9?=è{pÞA\þÆAWòçÄùü£ôb}·Î;h”Î?¦áøáóè Ž1t”ÿ#q±ƒ¾Ççc.8f_p¬:ÿø—Œâœufu)¶^xýäò“ãâqFÐzÌ&Œs4²z,]¾bÕ³ÙöïÕêçêqïqï×^•÷ûÿvÇeÿo, endstream endobj 78 0 obj <> stream xœ}TËŽ£0¼ó>ÎF`;Ø E–fH"å°mfO«=p²HƒrÈ߯ÝÅ<ÂJA V᪮î¶;iµ]m]7²ô»ï›Ù¡s­·çþâËööعDpÖvÍ8!z7§zHÒ Þ]Ï£=mÝ¡O–K–þ›çÑ_ÙÃsÛïíc’~ó­õ;²‡ŸÕ.àÝeþÚ“u#ËcXk!ЗzøZŸ,KIö´mÃ~7^Ÿ‚æƒñz,„9’iúÖž‡º±¾vG›,³ð¶Ü„Ç$Öµ³ýIµ?|Ðe ‡eaد°ŠŒà‚›ßQþF”oºæOí£Œ ð¤!T- ¤ BæçÀ&ZŽeeîæk¢å0TÐædÈ3$£ø]Cžm"s”¨Ä¡žrþ ,8ìUNH ^¥åS@ 4é  PIHm€ªû‰jj ×裺íL1OTO²(JB:ª€¡‰j „D5 |Á1hXq ”Ti òsÚbž¶Ì‰&óWI"¾X‡«?¯±[ BÌ›¢ø¬(©$hë»’A5úŽåsP1ZâØÛbºxòÆ"ŽLœì÷yl.Þ‡Q¤ñ§ŒÓ×9ûþ1ôCTÅß?ZN1ˆ endstream endobj 79 0 obj <> stream xœì}|TÅÚþ;çlKÙdS لݰ$„N@$ )ô’Å$´„ŠAjè`l¨Q,×Þ±P7`/Ø»b¿*Ö«W°]õzAÈ÷Ìyw Á(x¿ûûûÝÿoßä9Ï3ï̼gfÎÌœYM "ràb¢Š‚Qù%CÖ¥¼Câ« ¢Øï FMÈûøÝûŸ&ñé<"ËÏ“‹³û_÷øà}DâÔª¨ZX¹xÂ[Q×|)‘ùòªËÝ;¿=ˆèæ|¤¬]maåªÅ}샑ö‘{aÍòÊ«Oß¼‚Ä¡¤Ï<¥raÍõûœM¢y7Qße‹-[Þê¤èÏY~ñÒšÅqs»v&ÚPŠÛ}Er,,Ã>J©}á«ÙÑäÎ6’öÀWë^üÖØ•“ì?ض׆{RiĆz:DbWøæû÷oÛkDjc·J³'ÝHZE:j:(›ÎÆ(Æ}5äê¦,q™Éf¾Ê<!»0ë¯ÐFl¤E›5M3éšéSêÓú(u[k´6±Øí&/Ñ·Áz½–á&Ñ*óôæ(ÙSŠ7EixûFòпa¦|ªìп—¶¶Më_´Oÿ–éwÓVs$MÿwÚò{fêþû÷·¼…ûö<¾6ó^];ú7ºCÿçÝ6mîJwý'Ú‚¸]ŽYæ̾?h&ݨ?O ;Ì«Á¼n¿¡}ú7Û1…n4Nu¿Š·êH}±÷÷c!?æWqŸå:ÚžŽëZ,¸ïÅç™î¢Úcµ»Ý½žä8¦&ªÕ÷5“il‡uÊ(¥Ý=7Ñ äž–4¤Ã¸gû¹êoÐŒ?r/i¦t•>‡Ê;ʳ.¢rËû€à|”­hw¿4óxî¡-¡tËÕ”n{ƒÒM[ ¯ êá”~<õ-+ޝ\»:a¸GÞ¯ï!c™öñY»Sº¾‹]ÿè¾}W)-Þ¤³ŽÕ”¹ª#¿~s»8–±TÓUmï÷«¶ätüÌ~³|0–ì—ö\û¸zuTÇ|O{¿v¥µ‹ù9¥™êÛû:¼7ʘã(Í:žÒ,ﻼ,ƒv^z¬ríÚÚòëg(M¿ºþ‘8ÚVÊ×>£:m’Ác´-£nÚÔSû’êD¿#‘®³¨Î4 e?7P`ÔCñ¸/ŸGÖ1Úñ õ2êm$×iÏ«a^“xéÏnEÈB²±i׈ðßÌ« }ú»Ñš™®è(g½s€,ÅA_ֿݾex‡Ç]~!¬ù•ßKgkŽUîxLDç™ëñ~†³Ò?ÿÿ93]J³µçÉ£ï¥9@‘É×ñ»Ÿ‡Ç÷K¹€»Ãry4WÛDõÓh†¾ŒÊô­”ÑQ9œ!¦›€`0ì¸Ú×ý¸Û×·Ãû¦á,ñ3Î~¯ÝM#µ÷Ž}RPú¿©«]G9âGê§M¥áÚXê­£øŽÊo¹?Ãô_¨æÏnCÈBöÅLý±ÿöÑ‘i?òÛÐ-T®§ ´;€óé=¿ýgQm]`Üs!òêi“á{›}†n Bí$2éñtR»zçµÿo'! YÈB²…,d! YÈB²ÿM}ÆT¦>gú8>gÎ ~ÞìèsfÈB²…,d! YÈB²…,d! YÈþßšøÍŸrYÈB²…,d! YÈB²…,d! YÈþÓS,0H2€cþÛ,øµ4˜L¢Ïñ×n½ã?Ýž…,d! YÈB²…,d! YÈB²…,d! YÈB²…,d! YÈØZïÿ³[²ýɦ‘ÂIJ|€”¾›Lâq8JÈKfã_R¶SWÊ Ô‡Ò0A#i4§I4…|TFó©žVÒjÚLwS3íýR{¥öK:$uXêˆÔ‘î0·ÃÝŽؽÜ}¦û‚ô˜Z¿…¨nDíN½©o0j¢NDÔbD­¤“G £ö5¢ET¯5ňºÞ½ Q QEëhÿR}®ê©&këXëßµþ“¬¸Ÿñ÷>Ä81©µJ{R/ÔGР¯Înûµ/ó“9DŸlüd#®g}x"QðolMis•6.È€I4ë˜#=µÍUþe]§_v–R,%aü3(QªižÐD´pˆdÑEtSD¹˜)êÄ"Q/Vˆõâ\qž8_\$®ÛõYúl½Ê¤›LbÉl²˜¬&›)Ld‰>b´ÈãÉ"~2îöÓÑ% i-ø7Å4ú}ãšF[ƒæJ`ƒ~j0%ÛOú^}Ÿþµþþ푊zð_92ñ¿ oŽô“m8oü ó\Öè}0ï¨þ×c_‡£ÿcÀÓÇèןoúQI<ÍŽŠÉ§,Quì ¢6´nÿë×-yË7žµ|ÙÒ%‹²°îäóçÍ­­©ž3{ÖÌÓËËJ}%ÅS‹¦Lž4qÂøqcÇŒ.,ÈÏ5Ò›;âÄá' š3dð ì>½{uÏHïæéêJŠqDÛ#ÂÃlV‹Ù¤k‚zx +ÜþŒ ¿)Ã3fLo™öTÂQÙÆQáwÃUؾŒß]as·/éEÉÚ£Jz¹¤÷pIáp§á½{¹ BBë^0¬I#›]ÞÖ¯§TVû§•ä;ÓÒÊ å±ü–<¿Õˆåž/ÛLç¹›z=Úx~‹ƒæTdEV{ª+g”úõJTjÔ ÏöÇdù{xòý=Ö|š„.×ø{yò üY?õð „ßœîð¸$4Þ³oo{OeÐcIwüHRÊ.&ä+MhZˆþ¥¥É¶œ×â¥9HøŠJ9í¦9Îy³³ÊüZ…ÌyTå$ødNƒÊ9\½Â“&UAEð{ż$Ãwï^}ã;ßÈwûõŒŠ9Uó$WÖ4zòóyÜJJýÞ|oe°¯M}³Q¾²˜/‡¡¨ÔŸíYì÷Œâp¸å3˜_\jT VóÇçù©¢*XËŸ]/Ûå.h¬ÈçÊXž¢Ò4 uOÓ@·³y¶±2ÙbJFAciu­ßUá¬Æü¬u—:ÓüÞ2 _™§´¦L>%Ãßcn—fÜѨ…¾UZ–=·¦ÛÜ¥šS/“O w!.žQÑáÀã2’ò‰Žî.NRÅp—` ©ÚÅABOÏ#³tY5oŒ3­,íwšä ¶Éœî·µ‰å€ãp›ø>¿Ù4.-ÔÃ]P“ߦ킚ƒ F븚‹àQÃ&ç•¥§cå§!Œá’O1Éí§)îRO§Ìƒ9äR*û&ÇÚx¾ã‹=ã‹ÊK§œ%%íRœŸÃ)?¥![%´<ÌÁÂ,§z¬Fz´‘>œsTöX•í‘íjl¬n"=]Neg“0„9ï¼2ÿä¬2N–'M¶³w¯&E¦•Täa­b»óVzðò*l¬lim˜ÓØäõ6..¨˜7 ë¢Ñ3¶ºÑS\:Üi4~jézçyïX/Æ—ŒB(F5yÄ9EM^qNqyéN‘ûœ’Ò€&´¼ŠQeMÝWºÓ€áÕ¤W:eÂ-2ÒT$lFyçN/Qƒ‘k2FºªEá³)Ÿ ª}¾Q†q#/NU-&ÎñªÒ&ølìkàÒ݃¥mÈqÈœû /22ÙšH°7Üìµyü‘š]ÃJWžûQ6LPs¤° gbN5Ü-¢¡)ÌëÜiDš,Ù€’Ò×p؇–Ëbmá~Üqß‘øÊK›# ñ+JŒ’†Y˜4sï“wµœëÊæ5V”É݃1Wñ-üÂ3‚üšgZl‰ô‡{jFù#<£¤?WúsÙo‘~+f¾HxØrÓm¬ð`#ÆŠ)%§àµ¦Ëî–ÖÖ’Ò´ûÊÒ°–få¥þ°,¼ÜÌéãPn´DÜ£ý U•²ä+•u­éc«Ê°.U@ëC„°`”(4êÈõ†JU˜k•C­£¡Ì_–%oZ:¿ÌX¯?ñ ó[28¦9CÞ(»¬1ÖÓߨ|°ÖÃÓÏ–†¶Qq){œHâfeiîê2Y Mžbìe¿YH´)$_ÓFðFÇ *%‚)~˜þ¹í“ó' %pLïÃgtE+ œþ:ÌLUD>w£Ûáæ‘£òh‰ <¤ÃËÓ³N.š†*wéLv,¬h,l”GÔªÊà°ïä?%«]H¬ Ƀ@²;þ†)îŠ2wަ¢¨4-͉Õv×âœê©”¯‚)ÜŸ)åÆQ¥²QNqÂI¥Ìé·âÅT[YãIÃÄ/w }ÙFSpÙ³±ÑÓè7Öm! #|–ÝXIø^œå©¬‘GèZy‚®1ꢹÆèÈhÎÖr ÜÆXbà°õÍ‘—ªFy@ŸY‘…‘ˆiŒmtmÄ<oSFÕ´ ¼ªäÉm<êJ'R„±2U†@\0,]ä% [³0«i¦5ýˆÇø^”Å…mFT´lj©Š*b¬')–dùµN9È”SËKÕ>¥Ëì±^/f•SÖvûµ’Òàã1ê•Uêq5xŒwHp}~Û¨÷Ð 'Æô7ýx9è#‹µg´§(‡\ÚÓA~Ÿr´wɧ½~ üvß¿Þ ~üøUð#à‡ÁÄc“ö Jý°ªnvf:‘E ¾ xíqʪåÀ¥€eFÞ-ˆ(È­¹-,IŒÃ=C‰Ó•8M‰%NUbƒë•X§ÄZ%Ö(±Z‰UJ¬Tb…õJ,Wb™K”X¬Ä"%NQb¡uJœ¬Ä%æ+1O‰¹JÔ*Q£DµUJÌQ¢R‰ %f+1K‰™JÌPbºåJ”)QªÄIJLS§D‰ÅJLU¢H‰)JLVb’•˜ Äx%Æ)1V‰1JŒV¢P‰%ò•ÈSb”#•ð*‘«Ä%NTb¸'(1L‰¡Jä(1D‰ÁJ Rb ”è¯D?%ú*‘­D%z+ÑK‰,%z*ÑC‰îJd*‘¡DºÝ”ð(ÑU‰4%ÜJ¸”è¢Dª)J8•HV¢³IJtR"Q‰%╈S"V‰%JD+¥„]‰H%"”W"L ›V%,J˜•0)¡+¡)!”  ­JRâ ¿(q@‰ýJüK‰Ÿ•ø§?)ñ£?(ñ%¾Wâ;%¾Uâ%¾VbŸ{•øJ‰¿+ñ¥_(ñ7%>Wâ3%>Uâ%>Vâ#%ö(ñ¡(ñ¾Uâ=%ÞUâ%ÞVâ-%ÞTâ %v+ñº¯)ñª¯(ñ²/)ñ¢/(ñ¼Ï)ñ¬Ï(ñ´O)ñ¤»”xB‰Ç•xL‰G•xD‰‡•xH‰•x@‰û•Ø©D‹;”¸O‰íJlS¢Y‰€MJø•¸W‰{”¸[‰­JlQâ.%îTâ%nWâ6%nUâ%nVâ&%nTb³7(q½×)q­×(qµW)q¥W(q¹—)q©—(ñ%.Vâ"%.Tâ%6)q¾ç)ѨĹJœ£ÄÙJlTâ,%Ô±G¨cPÇ¡Ž=B{„:öuìêØ#Ô±G¨cPÇ¡Ž=B{„:öuìêØ#Ô±G¨cXª„:ÿuþêü#ÔùG¨óPç¡Î?B„:ÿuþêü#ÔùG¨óPç¡Î?B„:ÿuþêü#ÔùG¨óPç¡Î?B„:ÿuþêü#ÔùG¨óPç¡Î?B„:öuìêØ#ÔiG¨ÓŽP§¡N;Bv„:íuÚê´#ÔiGä5KÑ¢è2Â…3s KètNè2 ÔÀ©S™6ºD‚ÖsjÓZ¦5L«©#A«©y •L+˜ê9o9§–1-eç’@ê(Ðb¦EL§p‘…LuL'R @ ˜æ3ÍcšËTHÉÕpªš©ŠiS%SÓl¦Y\o&§f0Mg*g*c*e:‰i“©„©˜i*SÓ¦ÉL“˜&2M`Ï4.à Ë4&àÍTpŽœ@ùLyL£8o$×ó2år½L'2 ç’'0 ãêC™r˜†0 fÄÁ2 à(ý™ú1õå`ÙL}¸^o¦^LYL=™z0ugÊäÐL鳓‡©+‡Ncrs=S¦T¦&'Sr y¨3SR y2¨S";˜âÙÇËÃy¦hvF1Ù™"9/‚)œ)ŒólLV&K ó9йdbÒÙ©qJ0‘A¢•éQDäÔ/L˜ösÞ¿8õ3Ó?™~bú1Tú!T ú§¾gúŽé[Îû†S_3ícÚËy_1ý_2}Áô7¦Ï¹Ègœú”SŸpêc¦˜öpÞ‡L°ó}¦¿2½Çô.y‡So3½ètèÍ@§i 7˜v³óu¦×˜^ez…‹¼Ìô;_dzéy¦ç¸È³Lϰói¦§˜ždÚÅô—|œS1=Êôç=Ìô;dz€é~¦L-\r§îcÚδ©9˜ §ƒš˜üL÷2ÝÃt7ÓV¦-Lw±_‹;9ÊL·sÞmL·2ÝÂt3ÓML72mfºƒ]ÏQ®cº–ó®aºšé*¦+¹Âœºœé2¦K9ïŽò¦‹9ï"¦ ™.`ÚÄt>—\½7ß¡SëÉÔƒƒugÊdÊ`JÄÈQêÆäá˜]9fssS®—Ê”ÂädJfêpÌ%³@ŽÙ D¦¦x¦8¦X®ÃìŒfŠb²3ErÉ.ÎÎ0&“•ÉÂ%Í\ÒÄNIcLämžã’8]å:]íúú°ø|?Ã÷Oà'àGàøÿ|¼ïþøøØÿ^à+äýé//€¿ŸGÍu}5Ïõ)ð ð1ð|{Àï#ýWð{À»À;ÀÛö“]oÙû¹Þ¿a¯sí¶g¸^^ƒ~Õžåzxx ù/Â÷‚}¡ëyèç Ÿ…~ƾÀõ´}¾ë)û<דö¹®]¨ûâ=<x[Åõàaà¡È%®#—ºˆ\æº?r¹k'Ðì€ÿ>`;ò¶!¯¾Ðø{#V»î‰Xãº;bkkÄz×–ˆ ®»€;;€ÛÛ€[#z»nß Ü„:7‚7Gœìºúzèë€k¡¯A¬«ë*ĺ¾+€ËË€KK€¿ ÞňwQø$×…á“]„Ïum ¿Õu~øí®³ôt×™zŽë ‘ã:Ý×à;mKƒïTßz߆-ë}ëEÄzçúñë׮߲þ½õÞXKø:ßßÚ-k|«}+}«¶¬ôݯm¤Zí,ïpߊ-õ>S}|ýòzý‡z±¥^ä׋¾õB£zG½»^\î[ê[¶e©–NYÚ°Ô¿Ôt‚鞥-á-­6/uv){×-µ; —øùoYä;¥v¡o8?g®oÞ–¹¾Úœj_Í–j_UÎ_eN…ovÎL߬-3}3rÊ}Ó·”ûÊrJ}'¡ü´œŸoK‰¯8§È7uK‘orÎ$ß$ø'æŒ÷MØ2Þ7.gŒoì–1¾Ñ9…¾tžR)îÝ!0)-!§Õ×éuîq~ë4‘Óï|Ô©ÇF'»’µÑEÞäÎbQçS;_ØYNz9Ió&õèUÝéåNvú¦“)ÎÛ©GŸBJt$ºõٷĉ%…çæ3÷dôÕ•èÉ(ŒNÑ ®­à›±‘tá‚„¤ÛPf›Hpê ù~fâ"*Éßb£©ãý¶)Óýâz±¼z‹Êý–süä+Ÿ^Ú$ÄeÆÏ$øãå•é³6m¢ÔQãý©Å¥}óæÔQeãý R{½†n•šP¤,kÖ²úeY¥Þ)fOÌ·1zÂ#Ž—Zt´ˆŽnÖ¼Ñh|t”+J“—Ö(ÝÕoHa´Ýe×ä¥Õ®'zíðÈþeFN))ŒŽpEh¾ÜˆÉš7"7¯ÐÑ»oá¯úÙ,ûÉwÎZ> —YË–gßH•‰z™Ì’^ù½l9Òò«ÞHSÖïÍ^[®œË¿Öÿuvþû’gd«v&Ukg§§ À©À`=°X ¬V«€•À  X,–‹EÀ)ÀB 8XÌæsZ ¨ª€9@%PÌf3Àt (J“€i€(Š©@0˜ L&€ñÀ8`,0 @>ŒF^ œ N†C`0 ý~@_ èôzY@O ÐÈ2€t àºi€p]€T pÉ@g è$ @<Ä1€ˆ¢; Dá@`¬€0¦‘­¸ê€€¨ZÀ'_€À~à_ÀÏÀ?Ÿ€€ßßßß_û€½ÀWÀß//€¿ŸŸŸŸ{€€÷¿ïïïoooo»××€WW€——€€çç€gg€§§€']ÀÀãÀcÀ£À#ÀÃÀCÀƒÀÀýÀN ØÜl¶Í@hüÀ½À=ÀÝÀV` pp'pp;pp+p p3pp#°¸¸¸¸¸¸¸ ¸¸¸¸ ¸¸ø p1pp!p° 88hÎÎÎ6gQõÈõ/°þÖ¿ÀúXÿë_`ý ¬õ/°þÖ¿ÀúXÿë_`ý ¬õ/°þÅR{€À °ì{€À °ì{€À °ì{€À °ì{€À °ì{€À °ì{€À °ì{€À °ìë_`ý ¬µ/°öÖ¾ÀÚXûk_`í ¬}µ/°öÿì}ø¿ÜÊþìü—-[Öæ`&-i¶üUëõD‡.i÷û)Sh-£|m¤Mt =BïÑ:ê*ÚL·Ñä§ÇèYz똿éòìÐjóBŠÔw…âˆZ÷·î;tÐbŽj㹩8“ûˆ§ÕÑúõQ¾¯]Òê8Ôb‰¥p£®]{ Þˆƒ­ûñÊEºu°LkgCG5¾³^èÞC·5ETNÓiͤ ªDÿåo’ÍÇÈœLu´N1R§ o.®µHÍF)l/†>Rj-–Òrª§øZ ½,˜’yKŒ´ü©•´ŠVÓZKëh}ðºÒð¬CÎ#½ Ø@§âÉœF§J1{Π3é,<µ³é:÷wSçVtç|]ø›zS»ÔEøº˜þ‚ùp)]F—Ó•˜×еGy¯0üWÓõtæŒÌ» ž %s¤§h;ÝC÷Ò}ÆXVaÔxDÔ¸Ôc¸c°=<£M‹yüV­ è»ì[c°§«à?½MÁq”%Ï@IŽÂÏAFYÔH\„>°>Ò#N]fôÿˆ·í¨üžWǵmFæ#%ÕÑÞßÒ—ÓuX7â*GUª› YÝ`è¶þë—Ýl¤o¦[èV<‹Û ¥˜=·AßNw`mßE[h+¾Žè¶ŠùºÛxr~j¢5Ó6<ÉûhµþßËëÈßô{vÒýôfÈÃô(všÇñ¥<Á÷HлËðqúqziYŠSOÑÓØ¡ž£çéz™žDê%ãú R¯Ðkô:½%ìP¯Ò—¸¤WÌŸRÄÇÿû1Î×Ò¬ãø=¾ÿ…™“)6·þܺ²õg} ÕŠ ·â)m£óñ‰ý”#%…‹ÂMSøÈñÝK1C³|´û£~}ÞødûŽ:TäÙQ7H·lªÓcre}oX]®W³nªC¤Ü¬ä—²^ÊÎz) a²úö+1i1â£4«5ÞâéÚG”™1xÀ€þ#´A3<]£4Ã7pðú€þ]4=^yFh2-ô×~)×'´h<¹Ó˜»$GÇÛ-f-%)¶÷ðtGñôôá}R­ºÕ¢›mÖîCFu_WÐõ]kLjBbj¬Í›š˜c=øž9jÿ÷æ¨y¦º—ê–fävÓ¯ ·i&‹¥¥KRçž'¤ç0EÄ9bmÖØ˜Èîù3nLH‘1R8ÖÁ‰NOë~Ós¼ñÛ°•㾓ºµ~±-Ò!&xZ‚"£¥õÛmJ„Cx“¥JwȫݸFWow‘.³{Eˆ‰Ý<é?DFD&uMõ„ÛE¢)’"‘Ú½žG7§Ð YÑf‡˜þ¶9Úà½Ívƒ¿nŽ4ø‹f [ÖÃø EI"›Ò(Cô Ä›=iõ}šÂ¦a‚ïÞ'!²?2†Æñæ.L릴¤‘Ý\——Ñ"zm«‹+dj=›ë…õ•ÿc£51«weIÈ!‰²´™–„àl•ó8!¾‹&§µS¤f¶Å{g¯»áù '_þê©9 Ê 6³n²EØ¢úO^2yÚ¦ê!ƒª.š>qYÑÀhk¸EßáHŠŠï‘é,¹å»ënüåÞ îžÎ¨¸äØø”¸°ÌìÌ‚­[ûЩ#3²3,1]äoÜo%2]ˆ} –\t¥1ojnšˆKÂxÅ90Xqñ©¸X S\Æ(î­?¶–dÑäàˆl7ø'9¢ÉÁM~ùÃ0¢‘¨"g‹Èh2—Pî¾ÜÃ#¸›©_ߙΦ( c䶺¨"³,¨CQ [®±È!Jëš1(fààië@Œ—'F•éÂi·~{Û¡¯;õèÑI¤ßñÅuEÛ.ºkã½MëîZ:T»úŽ·NuešNÏttóWÍß~æ¸_bF4<†™‚žëëÐó^tìwSrfpžd{•ìUf°W™Á^e¶h1Þ°°8wœKn6¯½!C<š!^É–ÎòðØ‹2AMî/¶²™K–¢ÛÙÆ*qp·ûËÙ“aˆ¨ÃŒKÔQÛÞÙ{‘EÔY‚À³gÍ N ö£aÌ ´˜£¤¾În·¼DŒVk³ÛÌf\YDÀ†µc ƒž¤ ›=Ü4:ÖkãA²Å:ãc1¶C Â)q±Éë¡~¶§üýõ­­ûõŒW&aŒ—5.8^qÁñŠ ŽW\p¼â‚ã‡ñÚnO¥.©Vô¨9.®³¥EtoîZÔYn@Á·Eö®˜¡mF%NÝ^‡²]eámuFil3‡ß ¿ê³ÚôÕ¨è%è¿õŒ}4´×ïNNêoÈÞ]q)èì«Ã™çŒ ;ø™Õn5›q1Ý##ýžÞúµi•ÙM¹ô>¯””è$¹>’äúHr ÇIá‘R¡¯IrnØé‘LáÎôfVdê™ÑÁQŠŽRtp÷‰î>ÑÁQŠ–?»ž=P Ä2ßÖµëÐìˆp¼©ÃEÀÐâxì-MÙÓälÂÃ63¸ŠfÎÜÅ n9z]eŒûêdóÈæ:óÐðÑc[ÝÐâl)P—=§Õ®¬˜¶#Ún<$FN3¹OãŒ!6µÙ¹L¦U&[¤5òXùø6ÊkÏyiF‘4z¿ß/Û²-YòK~I~J¶e;¶“Øy8NâG2! BB„P(-P½P¶-K·½Ýn“8‰ ]àþ6¼Zèöw7mZz›î^ ´¢Ð{iH”ý¾oF²ì8áu£ü4ãÑÌ}çüÏ9ÿs¾oÔ¸áεۿs²{ÿ÷fZn­ËŸÕh(ˆÒ_WµrmÓúÍÓ5üù;«'¾—{ ïŽ™n«œÚ sè¤Áêàà½Ïì<ðÜá.‡ßçõH¥œ]›×Yƒ¯™øÁ{GûðG›¬¾r«·€Àó kƱCÈš¨ò(ŒK¸ç‘‹º–‹º–‹ˆ”‹ˆ”‹º–C+±†ÐˆGÎÙF¸…L˜,&Â0L|ŠÒsÄ,—,ø Hf!¼wBÜ6hÄüfÐÓ ŽäyFïµù*LÞ/Õ{,€R½×lñè¥øOiÎä±ZÝ:F©Íâ?×0vèª4''î¾´¯ˆÆ×¢Á+½ôODRÆ2”PZM—._z̪tC‡w¶`¿Bºá6¶íj#”Ѩ)‘W›ÍÖùO²¡jœþ–•CŒË!Æåãrˆq9Ôµü)Õ±ËÏ¥,àÌ_?¬0›”sM5í*v­*‹¤p8ÐèÙ°¨S@.Š{šDk$‡LcÃee˜„5õìÃ!‘”÷-rzÄ)ð8Ô>R?–ê]“G'%òqRapè N½‚ȧq,f òJÛVwÔo–á{%ø…Õ´ìPÛt¬µ ljËGG9CR ·Ò÷hñø“~ÖZf»8F>鬰(d:‡A°`u¬{qZ­Õ޶jq«DÛ÷ Úõ¢ÚõHíNyuu ª=fVÃ7pbŒcá8%Oá0g㈼Z¢,0"Âô‚tÕ|…–#qȺUK.0‹Wt*¨@Ùg4–Q¨“4ŃÁxS•«²Áòù ù­îv;ARËlvi¥•ÖGÈåÐàMŽúXùEç²ÝZiZh°Â ¿O|¡9óHßÅ+‚ûûe^¹©Üué¥Ú©‘¡"ž,¤(– xý‹Ùv¬û&Ê<~ZÔ*-‚™ÁL‹`¦E­ÒPe&ªÔ‘ìàX%žu¸Ág¸ Ó`x¤iÖ7+N†Ù’”$(´4WÓðì“<8ÝÏŸãÑK³’oi*¢Jè ùbjï»å!™Îcþ_aÅ ÛvdËO6MT~óëƒ[züäC›¿¾%_]„PcJ®ß76t]­êÒ…²ô@[úrŽœ’x°^¼L¨ ÚAá ¥@»¨ ´åÄ-‹¶H+íóDe*Kéôx6–Ò€z!æ±63¼Ö]ÝÆqð \bƒÀ³=EÔ@?aî§‚-âV/lO©5xc«ŸÆCXHWÁ”BãnÀR Ïjà<³î5h4ƘÖÛm’òQ#ÈGÇ$(¡˜šÓ@^Op9ªª¼ÒТh;ÖP WÿðЂ§y$µŠ=Í#¹(°G!¿«Ã¢è…G C¡‚¬¦¯BÅirªsï·'ÚwŽ5› ÕIUñ»û':ý±‘m×o‰7o{pexl EGSI+E¤k¢©~E­56zÝõׯñíë¾23º½æ€ ”’Œ·ÌçlXol®‰·­Ü=4|Ûê*µÅ¥ShÌ:-`èvŸÃíÔ¶Äâ­£»aÎSôÿ ß+ä¼Óæä¨÷9°‡}bW€A[sù¹“à3 ­…Ì!¢=†GÂï#õ>æÎ@§Zĺ|Ç(WÜž@lë׈Q-$8°'2Nò0⛈i}ô"¦7K5vN¨‰Á8¿9Gí¹=ŒXÖÆ*Ü ýÖ ýÖ aé†È  ŸZKi°”( ¥ƒoŘQT…QT…QT…QT…QT…ñ)‚Ãä¿ÄžKÉ€yp„±-`ÔèΆà7a; O„%ÊÈ’%ì©„ˆµ\IäÜ×}hþ¦í?:Ø%ðP´rô¦Þþ›†ÃHk ÿÝÍ?>ÔѶïÔ^ÒWÐÔÅ¿®=²¦ªrüŽ1ÒT8‘áDqQU`»ÿÇ„ácâð1qø˜8|L>‘À‚á›ð„Ä60b£µ#4„6Q2x€†ÓÅÏÌèCÔ“)dŽúz”)tF£)^_ß ƒôÔý Ñ+Ѓ4ÿU…Dò8F…ä„%f%L5–9R¡óZýåœDÿ=_.þñº ”üàçùûêö4'v7à7ËU€ò¨¬F80îÿG1?V&Œü¤ÙbƒÊyOÉLA78®†× ’e0à¨gY­cF»U²U`yHŸqKÄ|ö Ú„•{CØÙ’W°¡óüÂ5ë‹@ž¼¨¡ò0‹jh¯3$ôxz)9–OPrßîð©)¾bÍ!§ÅgÖ*¤äˆÿŽoi1‚‘’4+ËýIÆJI‰Ên ŸW¨'Á“ÊËáo³}¼}‡rcNàØ,ÊÁøÓ‰)0aàÑMðŠ9¾i6 ¯9ÎG·léÖÕ ¾Ò« šj¹‚k‘ïJ¥®ÉîÈ®¾wSmÃÔ=ÃÕ›B.ØŸ4º9gÅÊÕå·½|_ïÐ/ßÚy꽜¼Og㤎€£åº‡×l~dKÑ€;ù I‡+?¥w0Z«N‘½ï…ý·ýü!ƒË¥s‰¶Ô%ˆE°µÈ¶>>Ödò+àÃó˜©bƄ슂 ®˜`á3¬_ˆ#W¦Åµ«g™v§¸KþZjÖu뙼Õ¨Ø)ð˜-n½ÿ£w£dj*jã…|SaŸükçSø*ì‹#ÄûÁ ˜_ä `drnØýeN.þ®…o…÷¾‹ ~ð]Šß€ü7ZØ£á¯fjÀÖKî"‚ØAX¢‰ ¦ á/ƒû7b„ûûˆsUUÆÆø3D+ൠB191•RbƲ¯BcŸÑ5-T¶Üû±s˜¿KOº¢'‹Ùkõd@ÃAMK~YªóYì~“R’?x…®wÒj£Ûlõêd€ÖËòßÅ÷ÒRš43À IÀQÍ¥¿H¯PA¾ÿ)8J£´B¥QäoÌˤ*¥\ŒŽÄk@ f¬VЂâ‹QÎçQ£PHÑYC9CÁ£ÇyJhl´øŠ¨ïÄkœ:ïÒû¾7dÖÝ~àûä+…¯uñvFcì ™Q¬»~ƒ¹JCUÈ<_Nɼʈ¼ªÊ[+‡i0oÝt•QA:‚ÓŽ­œhˆb!¦M´Â&6@>àL(¼-=½Ð·YÚµyÓµº6Fƒd†Ñ¹M·–!ò_¢|e»VFæ%­Ûbqi™ ™wUzÌ2¼œÂc¬ÅSnŸµøð¸÷âa–%iM¸xOñè‹^7l×\ª%^rVXno—ï‹4cYäù-|°ÍNEçñŸ(Úëg&¯É-¥P}>¥(ý| i˜Z…ŽŠ^èš.-"ÊÕä‚1É÷¼Ö]A.ÿvÙPÇ œÑØfíM¯‘æÃ«Ê ü£µv“Ù¡¡;¼n—‡Pô?–õöõ÷y/=S:V©ÚÌåýÃß)[µjuþ`’,_!‹˜½ü.Õò a8îg1=Ñ ÜÕ Þå˜WWÏúæqõ1ɶETâ˜ÚŽÎñêY ü?Ûó‰’ WD'JÈÕÕ~ÛOöï?}kKÇ¡Ÿì¿éäÔqOß-ããûú}î~°ÝŸõÎ;þ׃ƒ]wÿôÈÁWì:òÂýãñ-© ¯{dGsÇ®‡!» Øxð À‚é§AôЀ/ßL¦ } ‘°¿¦Ù­¥mœB–„@ê’°†ÀßxtÊÇ5nŒ&'ÉÔCÁ`]W;uÿÌÑB¸šq¥¯ËÝ´.å=ÑÑfˆ¿úæÞ ñ¯£w¬‹ä,5 ͰñÁ™¾ÌfD’ßájèl±žzØ"€%°(;ËÜš |γEáoãhÜ26 •mœ­ƒŠ69#df!+k‘ÎóŒadã, O=ÁÃsÍÉ3BB#”èEÿ£Kg…€ðh4âKö,­RhöM yË ì?½¯h;m Þ[wK»J•ÿßE+ö‚í­YïzƒÓPÝšô™ü]_üÙ‘ƒ?–¼ûÅ÷n_ë¯n7Тüè`Õ¯®Xÿ5¾¥c×C¢UVŽ2-ÄF9a˜«ášZøèu°Ñ-µ=¬y³¹Ù”øô9!*j›s1XÝ$~…Òƒ6ܬy“gºãÅs¡+›bžU“W6ë uŽ ¨¤¤Ö!—v›Ç 'W«ýÑöÚ-"fÝx׺¨£.[c« x¸5ræÏ†hê᯴ Æ,:#R¦Rüµ¢+bÍQñ3#س¥VAœÂM•ýÑj!~çk [ò?´Dà/eö^~—¸ðÑôÒAhOkƒµ*|ÒSP¥LÉm’ð,ßšSn]TGè@\W"FeP šµFJŠ $z,®Uò‰pÛ]®ƒ×ŸàuxqÁ½Ï-´3%^NÒîq±yö+£ñÉlÇH°REUϦ–ªlƒ+ܳvbmº¢výLÅHg }.cdå­#ñPªÒ\™^»amºõíªÔÚ윂3pz‡^æð9ŒåÍÁòÖH "Þ½©=µ­¯œ3ZÔ ™ÓÊÉê°qG¸­:TëÚã¼à« àË5¡˜QN'ŒjŠ9ö„mZ¾Ul‚yÿyØû¢lðƒ9}²Ðû¢¯ÞújS«òçdZÅêÒKóç Eñ6´5ù›€çâE«”j@aÓ0Œ0«úMÄσ ¢¥„šÎMè@46ú“òà 7c[ÅÉB(> ?€-¨…(üI[Pä»­×cóäã;›|ÍVNêëžL$6ty¤:·ÙáÒ1øc{¾¶­1>óðmÄ®…¸ôø¦™./¨ÑljE‡c —?¤ðÝ[°}¨g'‹ÈY¬%ecóø{)y k2+>ë'Œ)™m˜©˜‰úd)•4!)ôb´ KvaÌœ°¯MhE‚±ôºÂ J·â\é# ³B ©¸N\i"îÁ¦õ&¥´–9½a³‚ü=y–bÍe.wØÊRùÿÃàÚ ÛéÑ1ä¿&¥ZÃîÕ2äyüÿ’R×áð¨Ú),7áXâÃKV ÷U,ù¼Ñ¦„ÌP~ñûä¨B *eÿQاT6Ðì[݈úViÁ›M  S²Vø ~3cœŒu͘ií ½µÐ†{?ñËsÜÐè§‹}Lna¨„E¦óš,ÀÈù9–Q½Î€AF]$þ’é}vo@%Qàç‹ØÄCB? PÞ(þ ©‚¦(µöÜÖ_ΑIò§(Fÿ ÙÚ­îpuD:H…ÌT˲ø@-ìÐÖÂæl-'jçñ¿§TX(¤Æpƒ=\¬Iœ‡hçÀšÄ†$Ü¢‰‹¦yBšÒkLÏcµ\-Ñü\-ŽÕâµµÕíó8€Á/¼¸×K9Þ©îký-;@a‘ÂÊ 4>±{ÃDa]É™ð†‰„¸Ê"0´a–R*Lx­éyÊó"FóâF Ȭv¼ÃW÷±­¿å¡\s¤d½šŸx »}uu% 4^'&Mñ…œŒ" ¼d’³Û¬.UóƒÃ釫Úöü×mŒ5ƒ‰ÖM½5¬”•QŒ­cõlí¦/® >ñå®éךí;[Í,KÓ,»6Ùè™mÏîê ôÔ®¨³èJ}µÅaõ9t•«®a7\­†½÷0‡–Kò” 3Èëë<”¤ É©`Ÿ­‡Ë&Àî1É MÀ¦DqrAó¶ÓÂuAxaJÆ —Jൠx Á hÛ”(‰`!Õ¡Lðä³d4F#JË¿ŒO=0îíé Iµ6ƒÞ®¥AýŠ!­´¬?“)Ûü¥±²jW§Üm©îP×ζñ þÖMOîћʯ— }©¤±@’/ýky£¼óG7uß1ݪ­èˆåk™ºFëµ@Çnòe¬{ å;ššî¿›íoÏÁ&û2‹‰Þ]¼ˆèò;Ââ"B‘RFT¸Êò–+%Wf\þyœ˜Óõ‘ªs<2e61éc²˜ÍÃ9ôV\úqFœšL±.Ë[¼ @%œæu}5äŸx(ä$"ƒRŽó²!­£ÞæòËÑh!‰Ó‹Z›nBÂXZúÇ#›™©kßýèšðpWYFZ¥:Ô²ªiïmžÔDKbu2ÌÂóÿ¬±h”–€C›ºõÄMw=»¿™³zÍ*YryÊ<§8vçxØöIuA«ôo$·`7ag Þ29|üµ¾hÃ0fŸÇ/œ…&õOã0)`@Š”u2œÛ™N6 5Ñl*K4e›²éäÛ±éL 1%_;€ÙIoV•µ@½‘} °:Ë%ãÂ<ãÄ„X×À–BìwgÏžÓÝ÷ÎpŽß™V7¹š,Ëe –DÒ§“oó@þZt–/Þ´ ¥‚{ Û„“qaÆD ¡:"si!ˆôË,EÒD-¯~Ã"#MÁ h.Ò@Ý’¾¿í­ý^¨Í.=cЦkÚní–¢2Z'UøÔ­c6²•2Nˆ¶Z•¬@¶‚–mîC–Mñ_4TêLúšÙǶ•w×{•d}oëì=›.ýVª€¢ê ]þñU—¾T8Bý3A¸ê3åɨJcÕ„\N¿K°±ÙØÀY´¬%`Gh8ü?nI0Œ½³²ã†•5F¡RŠžEŸØ‹½Ž0°z0µbÀL÷‡é—.4¢O2zÍæ3«èüá%ö÷FìÓíŸÂþø*Fkñš-^ƒ\¥Î?…ïdåV+IF)ÃÿšW.Å@ãªÈgÁÀÅ7ð›åJI2 kæòO僀 ɳ’ØAaô¶wÛ ‘i‚+F/¤ñÖøxíÕ×Â`jÜ›!¢Ó©ibdzdzrì­¾™Iè¹²âæœª5¢ u²j 3'íAMˆX),Pq-,hŽsçbÜ gÏqg4Võˆk„À¦¹i$ÿÀØ[<¸Ã èJÜ£ÕœãÁ]ªàm@Ϋvæxp+Ô/Ȉ-ÆŒE³ƒhSX›K`@‰4qI^‘{|bì F}õº;FG¿0~r ÷fCÉo7H%RšdTöPÌ–žJ9÷ªµ”LÉìµTu”—uT[œQ™„вÊ@c1x}iZà2„É9[W¸cçHuõêÛWm`4Vßwîž”Ée•Yëô*• &ÐãfüC·ÔlL_ËXƒÍë©hŽ©´–Rè)B_šLtiØ=Ùrñã’XK`?AœÔ•lƶd¢ ¸V ×±$ ‡I@J“€)Ã"B¦Žˆ :"&èˆÈN#b‚Ž@Ú#×yz‰RU@Âbî´–:¡@q$‡HOrÉZfõ¤ä… ÍðÊ9Þܧ‚×ÎñèbXV"Ò³¨GZÊ,§/mÅ,Û@>Îhìzø\AúÑuS÷•Å6?89tgŠÑ» ó‘=Ùù…®$à9€÷´{ZS=!KæìX=pç±Í{ž>œîî$…X—ºÃÙ| ÕuÇ `<5@»@»ÆÆj±wv+"õÉúõ¤rD.øÕy*á*¡J¨ÝJ¨öJÄýÓ¸p²+üD˜€KîOBYK‰‰yú[¶ù§ ¾=žÊQPÄsþ §({ä·Á>ó;U»T„JöŽ}@li,¬¨¨æaA¶Ž–i¤¼Tå‹üÍHF0ò[À;UæwxLÅ©5©²ËÞáí‚$±ý‰â|ÜU=üªG¶`ÈGC–KÇ=»†SÓ½Ä$bWýêݩ߽¡©e÷·¦®{xcÕ“ä¾½­ëÛ¼A„<ý·¬®6X ŒÊ¢UêÔ¬ÂbÖµíŸß¿çÇ·wwÝøõqÝG«³3 0.HÙ°û"šç6r`"biy¼­Àßm"Á·‰ÀµÁŸ¶‹Væ/ÿ"¥å4x6 ÏÕ§­Á\4ãÎr”cÿ„ÏÄß8dNˆ§4õòÎŒs¼x.Je±ä³y¥!ªØà_ˆKÂ\q„a…18ËmZ·êe©B&Ѫ_†3HL·qäÛ·ù2;ú|~¸üA­3©$2…ÌnÚ,DŽ‹*$Ò „‹‰ w¯.WªY Fà M@S3Ø-ÂÊ¡¹Š ƒ?YcB¾~ü=èaƒ!1 3ƒëjKÈwïZOI¶ÃjD¿©ou3×—®nÊuej³þ —ÝSZÔ Ej›3qÄãçbÂäœ=-HÛ„ÄYy(¯Ï™ãÄ®¦_)Ù³¸ÚA*½VÑCÓŸNÓ&‘\¥H¢›@€€ERw™T ‡†¡d´,­1µ­¨ÖüXH?^j˜Š±#ÖÆx¹IEâŒÆm…Ÿ•÷ezC›ï+ûU­íÝ¡Î]mãËUÔó$ Œg©i¸ª57NYUN1 #—ÊYùÇU`ÈÞÒ›$û0-ö-ì=dï£Gw~ ÚùäÉɾ5Spo§ng2¬€ä@ÖçîÛ ^°yžrÕ9tà[™Çs÷õìš:;’¹9»-»&ÓM*äa*ª‚fj€Íé((¹Gs–4‚éDÄ„™º¸ÈAH waÊ_œ(w+sßã9ÞéÈ¿ô^M*”š¢ðv)ohÍñà–%b~àfñ%†Å‹XR&>5¦MO:')oØ.¿Zè¾2p;f=ã¡ðý»rübñV(DkxI:ÇKŠÑZ¼II¬^š?¾ ô£=D~™œ¾¿oÍ~T+4HÍսѶ[» àC2ˆT5|®°|”2/†?A Äc¹ìóÆcÈ>$OLé°UÂz·cÝØÓÄõ˜s8°jØ Ñd¨Vgr-iwe®^-©Ï³äüg_årŒœ‹½ñþÙs?‡ˆ0gZr<8¿¾2Ç«S…+ —Ÿµ¾.X|É*ònQ¿Õ×Ä‘§dÆÓ2Éå¦Ã2Ê´×°Bz[—±Òo“ÓL¡±ìÝMcµP¯ØƒPBÐnXd2KࣚkéSÈžR™\Á™5n;#e@|¶™JÞG=€G~ºbEÕ-Ps••< U;OÉ+Á+á‚¡öÀú*”÷:[·dÔIçžÜTz}f<×ÛSåNä:3ñlAÙżë«•ºp?G~)„Ü”ˆJYS{r<”Ö;žã¡¼ÎDŽ/J\” Á…ÖW?±M®aj™œx¥Í$ïËL¥63!›!¯Êoÿ8r\¨Ü‚Re0i•˜TBÈ•ŒÖ´u7“Z?±I¯æ.Ê+’è2GqYräÓÂSls]]‘¶Ì3øl%f ‚ùAdŽl0óJ¯•¾yb$e·l_Ù˜›NgVæ6d³m™Š,ígY¶ë)¬ *ŸÅ ŒBðû±s%Œgº1Ç1Væø+™$*Óåº4,Ójý y–†ƒ=P³×böaôÇø.V.Õ¡N«šÎßU0+!eѺ¨ñã³m]H̶’ÝðèÅ^¦÷)[¶ÉúéÓ®˜iíÀ‡Á^fžï"ø“÷ON6ooù6¨bnæ›zØø0¦€Y×¸ÏØ ^94oÉf+$÷öìqånHoÏlÉ÷´D2¹‘LG¶.ÈhŠªèÚɤH£2.0õâœ+,ê ‰¢opåx(||KއâG29~Ñ jUôôä5ZëŸ9.ïö%©| ؤv©Îk]€ò|6"ÀáçS×ât*äðºQMr8¾¶ã r• @q'«X¾ÿ™*œE!ii._©ÂÈ'@ÌXmE1ÃåòõÈa>XañÁ| OÄ#ý=º\2íŠKFRHÅ|{¶ ý=I¨vÒ¾B±ƒÎ7Iõ">ýYé3ùÄçð\Ä“-Ÿ“‹Ñö_€oîžmMÉFG£—Kôvrc$Ò²Õ0{'£P‘ŽžTË @‘³éÉ̺\¶'êkÉõdê³%]p¾¢Z…(W#fT÷ =³@ÁPJv]އrzZr|‰$ób/û4 ÿì~%ù—‚ó‚ó>§óƒ••`Ë w,Ópªš¿P?@ýȇÿLõÁ’…ñù°Ú™¶g<ŽÁHkäØA89d˜He♦&cUΞîÃØœ1C£ÉhD`­dRÈ–ÀŽg µ…ÇJŽM"V¾(Á^•ã¡ #›ã‘³h@ALxñŠ¡eLSÚà-çc§£ðë®hè¦wôzœ,C’´T"ÕÃþoÜ­Æ‘ÂjPx¼  ժߪÏvN¢dœ³Q˜erx9•Œôß8H¨«Ðo/{{ŧ¶6ë 쓟 ­_¯äUÍZ‡¬ûî„.V¡Ü¡´‚Wè6¬&Ú—á37ÞX3›[•Ìdr =wÚT¡\MÆ“Õgï8ÆÀÄèlR¨2Kf¯,3ý¢äU³9Ên‰I¯ åxA>opœgŠ«db ô¶ð¬õ)Ùìr6þt¦§ß’q.`¬žé”óZìöõC]Nà”‹¦ð£þ4løû”2šÑ;Ðð(ÎMJ´Ü›z€šZWjÐkûêȲ`Û0q÷X¹J% ÃSWÇ|ò².ÿyùÖ† b“¸Ehƒ¶* çÓR¼¹9žMÇ“ó—ÏÃI˜¤8›¶¿??J2C`7¥TkñìRGÉ8ÃÀ¹/ÍØ<—R‚ª8c³1ñ* Îò¤já4Ï8¼Å¸›—WR ° ¨£ ÙØ÷:;ú¶Á°±‘ücK¦ÂÝñZcߺ×ÜCâ[Iá§a~%LY„ã¯Âé@!|^r¯†Áÿpá Q/$—í{g †Ñ·y(¼…ü#Å7v¼Æ7ö¹×½Æƒ[ˆw%…EqÜ Å ã†hPM¨!±P{5Àùíú†Â,·ÑÊ&¼6X\(& †B*Rü‹¼G§¾ÝgMl˜²iMíõêÜ5R]»ýÉÝ;Ý\ÉyjÜ5‘XÀå¯]{¶<íÂ9&ŸŸ™ˆ¦#¦™u5™ˆitrøîr³ìðÍý3m6rÏå‹ Þ2Zé0j«¾jBNxZ×üÚ¾<Ž«J·n-½T/U½Uïû¾«[kkëÖb©µY¶lGÞ$/ØŽmÚ¶Ûq& YL2ìØqÂ2À Lx0^¢Äš†Çç& ðH2É{ó€!£ÀÂ2nϽ·ªÉ’ã™ï{q¤VU×rï9çž{ιçü·£{fC&XØÔäínk´ZG]ÛCÁ©Þ±»×'• oùÝ­·{Ú†"›ö¸[‹×¦Ûó¤ÂšŒFL=}Άn¼ åãÓÔw‰.hù<ŒWê²®5H›Z-1€V45'1Ñ6”í^ã¢ý=hïŽäðüxÎ?jy‡Ù&®.èAúí+XGàLMÝItë\)9ìG7”%ÿ(cy§ÄHL× àjóôâ4íæEev¤¹:Äo\[n-}¾”ùÀº£‚"i íë/ìèõD‡ÕåæèàªÁhe5é†çà§·'Tz“†ãjó7X ¶®Ý£»£¹7ö็_xpPìˆPŠÉÒÊò{x :¿ê]úh_Í¥OC«òsÌ,‘%>ƒµv¾ Äj RÒògÚ”„>ç[³K…ThMZ…V§UxaZ…¾c‰ª†wÅp(øRr80P þÂàH©þÀ«ùÏÆ¬IóU–ª—‹Æ¨þ}B»KƒÔçEßîéçDƒ¼’µ8ø‰¡Í7½Ö§*V¢©$qâúÀZ&M˜/ñ FŠÈûÇý‡ü” eÛ.BA0àÏ·– %ˆè—ÉYÂA˜Djš¤»LÒ·¦ ÙM”ϳî¼m4gå‡0 _[ˆKkòR>„îfE=W¯‚¤ûv¼a¹RC2‘¦€*t/¥!ÑÑG?UêPÉEZÈAC{,šƒ?¢Ü@íkuàws¬ÔÀJÅ£Ôå+oxuÝ¡°RÛŠ„Þ%´‰xÏ ê| ˆf@¦ c™ùëßǤÊH©”ò©ÆŸ8õ!s™ >B-QT- °Z"¹Z"¹ ­MH& DlQxŸŠ‰ 9ª.7v³¯@qMóxM?ûV…öSö‚ªþj‹tù-LJ V÷Ô\܇–R¬WH9bJ Ê;¨YÞ)­‰îòŽd…w¢&b­(Åæqó!3µ"˜EE‰yÌã +’÷,>õ_`t­­b«˜ïC=¿¸0Ÿíz„7†C<· [Ðï™ 0p#úˆÂS‡’÷‹ªžr¹ø§Ë•ѵ0ΆØÂê yÐ—Ö l“5Ý7‚н¬ð2øT˜<]àŒ´žáîdÛPrÔZ'-õ(Œ9 ¡ Z¦êÒzx×û…¤øæJ#Ã=øiÚÒâÇUÄIª¼™*\I7š¤ÊIà˜ï‹*Ò 0&úS¹Ãx5 ù„B¢/•;RÕ˜¨Rpòòѵmêoà“kG“w¹kºÓŸ[¢;o "zÕ„Ô ¤!9ˆQ J—”–璈꒔§KÊsIDu!ãÊ•fkD• FDR#ÊL3¢:#¢«ñ’E¸U—8bl²ÓŠöJã†ýó€”2|P½DÖJB=Ò˜ÒbÉ+˜+qè䕬fðÜJÉ+õfûá¯Þqè‹[r‡¿r~¶þ•½{ÿ84¥¼öüþñâþ~ø¿ÿú##½÷ÎÝ?‡áç=CìÌ5m{`lø¹¦éõž.Ÿ¦^…ÔCU*UÞ–e E=Xƒ8DfI,èÀ¥ßK¬íX¶¢cˆ_±¢ãæðÎ÷+èXFìV.è85éï)êäÏh²ëåÑѱµI”{ôW¦F\Ð1î¿»¯{S« üó_{p÷5ùËÝ­MÿsÅ´ü“XwÔ4úй£«îßÕi€¶eù“ë6vîºÛï§±ýލ{IÔì¼nU Ó8«®Ã`uGù£1¢QÄFI@%}Þ( n£Ä€Fœ?j ©ºânšO!ú؆ÛPþ(?† ÿåóG+飕ûl)œ'dæÛpú(¾wQúh­ìïÖm|%én£¼šÎ•ýÀÉm‘Uƒ1„Tm\Τ/ÏU( ®Fs~®’GºØŠÿ71‘TLÏ©Zñä3¸vLÌ‘™i!NÛø©$¾œ$×_}¸’cÂ¥:XPƇCœÉ3d%¤© ›>ñš½~!Ž/dKµ+-Ò,µ$“q9}ˆSF>CÊ” …Ù0YšÛýKµa°§=çÔxN5Mj§àÒ)•J…15ÚzíüúðÁ–þ0G)XV©EX k¯//Aš ^´Ó#ù‘ñ‘ûFÎ0uð}¿•`û° õ R#ÃX? ç^/¸E ?ŒÞ‡ÄV‚ðCé H3Ú_¿Å,2!Õ•ý‚Ï˫ϩIuêVöÝÝvÝŒŽ¡ú~‚ðô†…Ÿ‹ ¡ Ò'AôM!`´:ˆ¾:k¿lM½Qұóè(-%Áôýcô 3ÂÏ+ª¢ Їòpÿ+}äKÓ¬n˜\Õ °4Âà‹çok‹õgíáš k áèÄñ‰@±=j’SÐÚdeJ_ËP:Vˆš"…‰ ë a ]U‚Rb¶nƒ—Û=v½¿%jЏ}ñîÛ:›w %Ôz¯æ¡ VÁàop„›#_¬s=!r“9À"N¿ÚÀëÄnb+¤y1Þš D ÇO Øb;gåôìî1pœ¡g7=v?1v¼è^8:жuÿÀÈ;k&¶OÌLP©‰ÔÄdã‹¡ýÓ?;Á-X‹¢X£RœµêKxðÉá`ÿ+Wôb¤G/¦#òo"è/¯5/u/”ÄMŒ@ÎLðž Èü®ý/–àÛ&^‚ï³r %kQù(Ž>*¥9oqÍB\'Ú|õdËV&Kùeº)ëê[V?2HZ®vGp´ØuœÓ#ŒÁ»­©Þh¤¯Áæw*(THîk®gòÍE$¹æƒÝÖ¸^07l}pýÄ=ëc?Eˆ…•µ€Ô :•Š«„ ë+2ѰÃãZF:Úo.[í;V…d2K1Ô{hí¢r‰ZÞë¿$Ð_%Ú‰G±þŒ:RÒ IIW$%]‘”&¡¤¤W“xñÁ¬I.ø‹NÍ‚¹˜©…¬®"ÅÙ(U-\½‚Ëá£JðZsÁ¬Y(™‹òL}:nã¯æ!¢Ýb^käÌ» Î{Ež}¨ârý ­¸Aj·š£‚Q2‹Ã½"­o¨Å4¢ÿÈÜEì"Nci¢§'»«uÞºÚÊYü§Ù¸zWqzZÖZ½°±ØŠVOØâXbÔQdƒ’уâø(¼)sEŠÞ_•²1qØž“±qõBicQ|Ц$>F&,”dƒ …ëÑ“N[h—¦ãÖÎn^òS!2Õî/òõ¡¼y¼ÌoÀQôŵ•ïV¦«r¼Ž¬+ó€ºTK«Ças½j…$üúE¯nE&!<(Èê—Lš /!'ƒäŸ¡mIðùסD÷ˆ«ÅÓ=<òÞã.WœCZRM5Ç{Š||¡£¹hDSpL)Ü\…º¤qš “bŽhà¥Íñ…RG¡¹4b# _$œ…sDê±oÊ_r •Bºrú)i·ýûÙÿ<½$)~™þœ:þR¢‹Òåb|õFœS¥éÕ8à?¢9¾žX]ì)vtxŠ E²¸Q_h.êÑ@ Žm­àH–¯L‰ËQWÐÒaµžM"U| Q䋤Š*6oÔ"BB2ê%2Ê·.þH¤yT1³Üú“pCÍÌr­é–ÓôË ¸˜w•{êNRrÎYžäà•µ'\Õ¦×þ¬e°º²¸Åé嵬Dõ:fèŒ:F³;¨à‘–¯/¯oäÛ¡¾ùñæÔÎ#}ˆSÓ÷‡Qð¿÷h/oC, …­ím B¨¹w-Cì>~ðøÁ½ìÂ#ƒ÷û¶酽¨j™¾¸ze€~®s¬Z£#ò/+F¥!çFç÷º%Å 'møÁ° ¥ê£÷N/”ö7bVnëDϾ$½@¬Q‹KŠy ´ïVª|–:·< êØ_]}”o§¥LnÂë€n®Z'¤÷Š+‹‰:nÊuÞð2Ò¡à½ï+à´"ìñiÞ¿Òh…A{‹c¹^x°ZÅËŽ’F”­ÃqNÔˆ Yш²8ò÷‹qä‹îîq¬÷g÷k÷OMí×RöÕ( ¤7ƒâœƒöuhÞ1ï+Žv3ÅxÜÓÖÐF¶ö…`‘F*À$™Š’È‹s=ÒœX†°ðà½"váG¹Jµgm|TmÁq"h_(‹&kSŬé€jVÁžû· ÁîzëÜ+hÜ‹É>§g ÅtØÔ” •Z!KàfL\Y£×§@>žF5©ÔתCw¾ ¨ÂÈ+ #¯,¬@‘[Ló8ˆ ~ÿ¼èߺ%KÎ-YrðówØ#F SÎ]q‘Ý’ßçÆ™ñ†äPXÅX‡ó€©¦ÖoeQuÓÄÈ‚RºAÀ…Z9êÅj5ê4¡–ÖZ]ê§åz§ÉìÔÉÆžÄ¡Áʾ9]lè>¾Jnt£|e5bxlÃêÎÛÝIú*nñµÿ7¾­/¸qy´~U¥ùú˜‡ WŒ¸~3]Õ.hî¶¡%÷hhEŸÁyAÈBnr„ Bƒ(Ú;@G;èH‚Îà=&0ÆK‹]è³ÀB2óøž“N£O ƒÍ¡Ó\Ͼ•¸æùqþOó½P䇂CíŸH€ú.àrùŒJêw2Ïãèdåçx£6jAŽÖ³ÔV“EËP Ns-E¾fP1H{ øn?ù-²À؉$ôïþë~¹©}Ü6G Œ±y°©àä‚g<»é¤'R…™J±ö3‘ÙÖ'Ø#Ôa)Žˆª¡tï°>… è ž)Á›S¦“%"ŧ~•¢Ô¼?b?SŠÌ²­O”ð3¤x¢„xTC&ö­ˆvTsÒëÁŽÈ‚Ýåµ§Ú#-îÈH©o½ÆÝ v&] ^Û±««*gûÈD¤#¤Ï&ùùÔj•¦!ùXjURðÛcÞ¤ó; F—ÅÙ2–þ°Zðáp iU‚´úŒÌ@„ˆVb+¦ën¸ &ÑÒøhAGܬ6qÞ7k= =Üx9R dçr $& ºÊ—8_¯c/”à••°unIIîR(Jƒ\ZM1‰Akò3Á‘M%-v/ho3m<“ÝÔXØÜfû˜Æ Ò‘Á¨?ëæ©÷f×ÄYÁoéTkPŠå`üU~1L¯ÙßìoöD[¾žJº›úàx@Rba¬D1!ážça9ÖtÖé{Š›¥žND>-?R¿C9/NÓÙ’“ó=UâfÔÓ¥„<òé’üÈ"xsŒô^åh]€M&²÷˜´´Ü·¹ã#ìí6Æ#!³J=OV.g#yïàèÈp¼'¤’Ë¡¥Þ¤ÑkX‹÷ÉÇÇd*ŽÕêµ*£ž¥½æí;¶oqú•:T[R„½º[¦ƒ£¼YD_¾¨´6_¡I”xû€UIEÎ ³ÙO©ëd='"áU¸‰."çK¬:û©’º^ ¥¹ìÖá ßmõêN–ÞÑÙ»%góôlËg&"rsTöHd0€VªÚ• †Rä?‰ìIgÒãû:ÇC!b4uS^—Jyšúüfo¼÷AØçƒp¼‰qÎMAëêá9»Ng̓ɂ™°NkµÊÔI/°DOyf•g,G*˜é³Õí5«© ·Öpºï¡Sp˜ÓÀNÁû<ÑS%ϬEy¦d9²h¹zäñ*Ò`Z$5œò ÍP>©öfBù¬—eZ_<Óê9s&<üÁþhP=L¯ê÷7 $Mجᮘ âԛêU+™SgfWÇ"Ó-ºs¤É…f÷ù=ð‚ÌA´·áx–^Oh…y°± ‹…|Š'úÎ gc‡‡µ‡°´ &À¾›½"&NO–Æ|gK1þˆWb‡¦~i®c–Ëo©åD‚HZFÉâfd5Uk5ªc2­Ýh‚Ì^­‚úiµ93œ5gLJ†dþA«gIÚs¶YNK9YO#þƒoZœKKëD³M¡ThŒEÄÀ{än8f;‰Qb ñS12¼ôB&ˆ84oÖ]ÊÄá¿ ½k¬»('Æ2³ëÁm…H>;Y{º`Zc"MÅ“\JNµ@óD­öN¶Ìz&ÁäÉ‚xH˜BUôÜIäãS ³¢Œ@#ᵩ…œsõÊëÍH„7z£ 29út ¾À´öt‰0ñðjuñd ¿å-ü––ÂÉz)ÐPu/‚ÒTCÍ€¯ŠóßžŠçªõÇ7Ïl¹!þÈoL”ɤ#r·F9¢×z»okv·éYMÈóDj´Éá:4RÜÓãJ„¿M°úº'iÓó*Õ×Û[íQ»¦½É·kRÍé‡ý–‘þx»Ÿ£b qKª˜µiÔ¬™×[Hi µù"}MNè¢{"=.MÚæï0 ¹xºØh—1–Ï6´êœacCï ”÷»\$m ~gñ`t}ò{ä½pF’ôò…ˆqÐA¨ 8s„C1k/Äg}̇™Ã•³\=$1¼"®½Pª^SÉ*Ë-ZØCÔ¹YVy¯œ· pÂimó£Œ8ûÈ*³PjsCûÚ¬@þSUZÛŠƒédùLå¸~þ‰EÝërPSí€ã”dþˆ3Ê ¢ô àÁΰ‹¨ Ò6gågp¿Þ¬¡D£D/Ûó%k»c{ Ʌᆶ·Ö¥zýÃÙL•›l£ñ5¤¼¾Tƒ·ÖfÒ"SÈHþºs¹¢1·K¢ÿHÿ&bjaŠÈ€Ç¦<0{f*szÀ¤Ñ†µçKuWÔeðÜ„µÞ#p1Ô»ÚÒæŒTˆ`õ{­Ùí™Eãuéô ]Âc´R>{c€vŠ´×Œ<ŠPâ¡]¦d­Or³þ§Dûºn¯Îú$43ÿSƒúýeZÈÏGÇï(ŽÏ ù£GW >λR±®ˆ}®Þ@½×73‘ ì;´690lv9š±&ç4jm üŽü l-ò vŠñ³‘ß„ý#aBNÛvÓŒ}†?Zu PÅB½k ˆ~AíÒzï]]ߣ¥I-U^Üèä§»l‰XÄ\@F+ð>[ãŽÎšwPLF"Fä ÎŽÇ•§±|AØá2hy, á Y”iH{ñ¦¯'SÈ;€v4ìÿÝRÿ1ŽüE§ªU´£Óà‘‚Á j ;i­&vÞ2Ûô)íæ¨hJçqŠOÕ”*×YbçK–YmÓ§JøZÑšÎ/Né©”-I7Lò¥æ´¨hàÜØ\ØÔfóölï¶&)‡Œt^«hP{µ*è)"ƒš«ô( +$ÙÔØWIð•Å5yàeìA`äHŸà!­¶±W³>Îäš1®eм{EÄuÔ„Ù+¥Ú÷·"7q«/ð2t‡…Š3é8hõšÅ ù Z¯ ‡öût­œ‘1*KÄYþÒâñ6莘´B¦Eèнä·ÀìE^Düúk"Ö?çIxjë<ØPpêØÉ·2¿Ê™–SÖœeOþwÝ÷u¤N8Å©ÇyŸZ ô^fb'KâfXÁ–S%|¯Ž=‰S3 ¶Ô1Â)IÂE(4 ÷>µ|&FKsSõ^ä'BŠ,ò›š=)·ZF1ršuFZ‚ÉîX÷P>êɭͺÃ6¿adB íÎBc}8£ŽÅ{“Ç©Í&AÍðzÎvxÍæH¡9Ü”j ¿Ñ© ¯‰Ú\~‹Ä;ù!½Î1Ÿ'²¢ñ,áw‡×y§r ŸµªÎÅŸ–‹:ê*.†¼òî·^Å^±É}È>[² ÕÙ’á<þ´äã%î švˆ–ú ‹| ä£sàœŒ\^nûúÕ*•J=&“¼ÅÇà‘ê1OÌ’Ñ2†¤xÁ¢RÈè­Ó „ü…1 †¦á¯aoâ_3YŽVé±L‹¼—1BQÜqJ釳Í&:¿’ŠÎ˜g<ç«NqÄTL—.P{Î×¹ÃUsf±7\ÞEwÉ{­~½YÃ4ìnìX›dÐÂ4ZyYkÎ[ŒV¦šªû›ÅS•‰ƒWVþ»Á¡t”*ǰO òï©¿…}ê#î™ÝoFH§Éy¨¢T^£¾Ã¨wuó(楓Ò]FÊ™±Ö&fj›ÐTÁâ1@|-¾²·ô¾úÝhR´Ô¨ã£T(´bx”º²RNÑ_ ¯Íî5)Ƀ€ÜM)èÈÈRNSrÍhvèääŸä@Î[M&«VF}˜$ï 1B3NÅiËZ*‹T¨`·Z]þTõè×Z^¥€‚H·M­Ÿ §•§TÒ’z7ô)÷@zÅx ²þžV¡`ÍóàÑ缂G)çÁc5+8fLJnFyu§dnTè†v;¬ì¤ÄÖ]e©@¥zZ[©¨|mSG°šŠÇÌNÐc¯ËÖe³@k—>M~„”霋‹ ÉiT´BÃ^ Ψ¦I¹ZU>J‚Ç嬂¢UÍÙu ׫¨5©ìK5;'SRëÍ«¢);§¤ åÊöæÕZLR,k+å åsôU©ú£|=›ö€æÄâgÃÏÞµäÙ»VxöH"׋çÚâåç˜`k<ÚÚŸ}… {ý·àufšQ"ˆsM˜ }ŒGo °˜ç™`#×ç—ê'L*T­rYl¯C¯Ádrèå: 0ùv¿I¡UZ#nwÔ¢TZ¢nwĪG+ù—Ô j½š‘©uê?æ¼q»Je{½I«JeM"Ê.\_çèm¸…m¢í&»a"sÏ«ølï>6–¿R±ÜžG' v´“„ _äô4­Ôè3rÎnì¼ èd†€Ãî3È•J!àt„ÌJ¥9äp%hF±üE^Wó,è8õ¿{œa‹Je ;+ËZ#°Í—¯¿§Îàñ¬¸K‡qž<~‰uù­£ %âjþ*^ÀDÍEç  xzS·´­#jz"ˆš¢æÒcÊãI J&<¾$úL^‹xÅ´6µÚ–„£ï,låAâ-(WQ±2´û1ÚdWI¨ño"ÿKY Fqã¤í ªbu0ÝÝ™B?Ó©Uðñëê•bî"ìÐzó㈘Ì'd‰|ãUÔÛçd¾:´ämWñV$‹ê*%T¾ÌY¼Tñ—*³ßbñ *™ÆÌ?̨õV=/°€)›—ù¤¢éÁ{m½L¦÷Ø\©¤õª‚÷<+/¬ðêCœ:F¾¼¨ª°¹±ÖU¸€k}@{ìHÛ§6µ*|bª§ÑòKå,ù2jâ#´FoAM¤bÍ~«Ù/¨ÊŸ¬ûvŠÆß >1a7l£å*Ú¤R¨•@wŸÍ VǶÒׯCN¼9ñ*)£¦‰)x§^½BÇÛˆ)ÔËò?’/ÿˆ{¬ö²à_ÉÖu³€sRVìgðÿs??†zcýû +€~Q7—ûÍ"«À™"»Žðˆ˜ÚrÕMàÝ8‘^¤U %´ F5J,Ê/Þp.¥×•§õð?ðç ´Ñv¹C!—LgƒôZEöÃç^%åÔÐVù|Ó‰ò3à7Ìc„_Õ…–½)”’Lá­Ø)“[u‚È£äq³á‹èؒǹ(2èDèÍÕ wS–mQàÁ/·MmÛ­Ӫ·ÔTËD›Ã›hJÞ!˜<Éì|±¼éµ–7O­S1púeö¼ü£7fg_ÿñn§e2JÆòˆwÃþ ¶ÐKô‰3…^\]×K–èó9ÔR=Þê\…³ÐÅdzR“Ñ ©Éˆ±•][ôÍMdXRúfA~æh[ÛB© 6½Í©ÌÖééišäf“C§ o?JZgßøÑË{…ŒdT:õwÁ3?| <ó¢’gakeôÕò8lïC×9ðmzR´AŒÉñàs„Œ°ÁßpN%©‹ Jz÷µ·ñ~Xà±%]OOã²ôüæèðè­qYôVƒŠJô&Kª7 › Îö4ý±¿(éÜ…ò—¿ÈêXHN9³þ+çžÝ:5wþË롵O1,ÂY¸¶îܺ¬HM7øÐEG\&õ„ž`àgcqó`ãx±}ð„%ý>ÄsQàs¢7Aª š²¹4€*ŽÒç4CÛLAÆzSðöÖgÏ}6’¶ñ à¶ çÀÆ¿PjY†‚¾Úú/ŸŸƒ2zâ: %òçpLß‹ÇøÝðøgøø><Æ£öŸ„>oÝün òP•åñž±ð1šß«ºLœß+Ž%gù Œ7ëõNff^³ÅkT‚òËÎ5„¨T ,þgå¯rfñ9Ê,M×ÿ•q3ÃÄzb/qñ!¢„Ç;t¸Éu—u³œ;ˆêËWE£\m|Ð?¶ë_¸Êú.:ÁñÎêõ«Ñ —JøŽþœXçÜ?Æíú—7P]:ª”ô̈¢#eëàÝÒº©æšƒ$žƒÞ^ -lüA#.ô«,$S•’”/%Wáö¡H.ÈǦNíÝxÿ†xhýƒS¾5“[FE-çÝVÁmT¼W²/ífY½ òZí± r±©}‡ûò³ÛG› ̹“î¡tÚM©LóPZ8âïßÓ]=X°7ݾ}S0ÛÕ—ßZ?05™hÙ8ºÊß=;Ùø@WÇέ[²ÑM›'#öUck¢V£„,§±¶•nŸŽ\jRa±Z]«Ðú;S¾ö¨Yˆvï¤H{[×@<ºªP8›£{²óZ¤é¶¼_猚“;vîHyòùuBâ}¾Ÿ!¶³Ä•êÂÞ½šíêZku¤è–Ñ¿![ –0’q ÖÃÞæñÜÆR-ÛçÉö‚‘»z÷¦¬ÌZÅà–ßrwìÚvnÜ—A›^w‰Û7æ_Aü†üC‰8õ‹ÇE:s®Q'I‚°èIÜ–ß–à³ÑÃæJƒûº2xOì.i»Çü•¸øÀdAÌ--õ² “£0˜( tMÈeE¡E’„»Í­›zq—ÞòÀº‰£Ã×ÀzŸÚê 0ÐË0»Š–亨&e€¤Õ³!Úµ©?´mÿm;žØ™~dG÷Ofôœ/‹vÇLw¸;6uŒ>ÔØ´mÃx0ÙáÑ”_ñäÄøhÈ×ÛÕjí-Å=›ãã£cÑ¡á}­~V=2…VíZ÷§ù® PZ,+¯Tª”®Ö´§)hÌl8BÞ¯ñ'Û|Þ¶†¤`KûŒB¤åÚ¥D±Å•DÆFÆ"öl&CþÄsù¼ì:äöCÄ“ÄYâ]±îgχOÒë÷­9Æž'Ó—vœbØnzäã—É8±ZVñgOlÞ‡ä jnǾwbßzÄ~ü‚HM@Q°2{NQw³¦'ÃðßæþúàٵǾ”Äf¢¨¡šH/¤±   bž9Çã%)ŸJCN’ ô§®"ŒMóðÎ’ñ`aéK~ô×%øšÍÇæòÙÒæõ=ó@}¡´™ i,#µwÙø—*á$IS4‘8FHKõ!H*(q÷,ZÐKR´8›8$n[IW±D$Ã~‘ÐT#Ëh10ÆÎÇó‰=‡Žv´nɶ|õŸ½û[Ÿœò›ì‚Æh±e*ƒßkÍlÿÄÿøõÉo€†Wf#Ã{º6~®`Û ¥4­tÆ›k_>²ÿc}Ç÷­ïôƦ Ñ-'oß|ßDðÚLtüððšÏ vÝux*дÿmˆõeš‹Ic{cãÔ`üŸ¤½ƒ‡6û³»vl 5?òÅ¿=ôEÎmiÜxçÃ[5²§Ç¯”ëô:­Â˜ÛttÕŽË_úøÞöCß/¿ò7÷½~ùÓÝ m2C«ÍzƒM§DEéc}CE!Ú5¾³­gßä{ßõtÝÒ–ÛuM‹”M<Úµ·Ù’íè'ôäbÐ ,x²Çœ¹î"ˆ K}Ûõº™n\¼ÿëÞÿuïÿ*\ä¶úçpÙvãþ¯Â\‰ÛŠŠa…‹%øý-ïÿÚÜyüò½\º³}Þ?gëÅÀè]ëÆG£ÇÖ†ßyjóÄß9ZBŸ§¾sßäÙ™BçOMN>9 ?Ÿ@1¿ë etÚjØÿ2«æIÛ³„N­š½Ï:¶@·<Ÿ¿vïÒ‹ü0uÁ¾™+á¯PLrÉ*!]¹L&ÊRkPZ="e¬Éc<&ÕP†0Ú ÄhµZEn½ÜªDñ¹Š¥6V![Ig… è$Cäã"{r'´Â…ôíþÚ¾¾­âÚûÎ]¥{¥»IºÚ÷Ͳ,ɲ$ï‹ìØŽã8‰³;vö} !H¡mh)”­ í¯ík¿¯ ibB ´¯P\x…¦-4¥´…Òö×Ò¾—ו¦@,3WW²ã$úú2ùéêÎ+Ïœ9ó?gÎÌ9ƒí-i@ ¾í±L&¬i ?ò(çó59Ç@S^ß()\)ÂÑÝ|œ^…!¿.´n/©X}º´\¯îêkJª¯p…ò;4zéD¾eCžYh ^RYm[ùtJÔªÔÑœfÔ£+R¬ Ÿh2*¼ŽÔ F`™=œ1ÙÒs3íûk9šÓAÍN'µ,ßÓµôÈHÊÑ}ÍÐâiÀR}²S†œë±Y|v“þ­k{üÑ|Òá‹úh¨óŠhC[t`Wový¶}½ßÖ—¢u/,‰ý>ÃØ%údð9yãàÒªÁ®ªÁÁª.mºíÆ›ÚLm¶Æ1`˳s—&'jîJÛ°§Ö•ÜSg›Ä’œjð.mk–›Tš=¦¾=W}ÝXXHNÔ@g›Ú!ë®Ó|SqÍûðrqsÔ`+ûßÊ9—¢§ÅCû[ö<¸«çšåêœB¸ì¢]=]º5‹÷Ü`ô$¥¸«»¶Í‰:rƒ¹–õýu,­gHœÔYZ–^Õ1üÑᄯ}¤¹cçâÔÁ…ÛÚªx½œÑâQLvöG|öe™ú¡Ž#:,PÑÁŽõ±9õÞ`,H‰NEP$ÞZ“K®ímÛ¶¨Ñ€Su‹vÂQ˜dÉ:2‚UcIm…(éÇpúÑ(Ib‰1œ:¥ÄEÿHÒÖ“â*v-¹ ÓB ¢8‘ñªS¥‚"*ùXA+jÓbjápè`°bŽÈU¸±QDB¦ ð“u&ùûèR›ßÄLŒq²‘&Ñ”ü”ÙŸðÓþû‚RÜŠà'³á—Ñ1}”ÎÀ¼ Ìjöº\"Ñ©G›ùu‚þüǃDø|!\ßäYâ¹™×£"\ŠpÖÂY/‰pÖ2ÂY?ÂÏ¥ <üµ±Ú«>xÓ×6žakÛ¶°k§ÛÖª^=¸\ÐîªgÂýÇÁ¡OíêhÞ~÷v…£ãˆ_ c°—–FG wäÉ#q0a6“‰IcÀ\Æ‹× Ø•ÝÔÔCBMr¡Q™)xp¼vz&4dü—?Õ#‡øÍ™‰•Œ£iH^À_€„²M¶ùdú:^Ou#ŸFÈŠ&J'ü?UH´J6‰£Ÿ*CâùzÉÆýزÏÁ^i.­Ð"\ÜñX*–Ã÷?Ê))’¿i´z¥˜›†ˆÚQ×Óà¬F%O`Q:wNµ! F.:¥s ?Gé!æx‹À¬`¶þá´¸nCÛ†¹uFŠÓS¬Ò1|MÇŠ[WÔØ»÷ŸÅ³p ÏÀŽuƒ½¡yþ*¿Nr™~%´WÍ-t5lÚ®À¶@ì» Ò`¨Œ}i¼?oœ¿82?™??’'xç¾b_‹Ô")õ*öÍY\3éóQsV*WŒ}êÛsêKØ·ØW3YP€R®ûêÿI軫}ïƒ;:w5 :šàúÜâ]Ý]»ñÅûçÝéÅЯ߭_va®yÝ@‹ŽC&(†o^rͬáŽ@àn™µk0qdèÎ- GàÍnKÈéxíK3õ+¦`/_Ñë«÷ ì NE²Ê>rNÁ“]¸ao’%~¥áÞê)ܳj¸Þ‚¸ÇúG¢Î$ù$B3{™KžZÅq°ž,”JBÔË|`Ô#~e–ïÒ™üêI]ßG3ä± Î’f_Âë¯õ w‰Jñ‹ Ø ¾;õ~øw ä|ÿž½Ÿ_[Õ¼ý¾•ðkÙ~ Ì2ùx•ŒB½®¹¢×™Fýª^×9êÚ¤=ñ,ÚóŠºâ„©uP­ÛT: :þìe´ºòî,ð* ÅŒÕkÑýF´ hfuos&«S¶y%Ʀ.Â!’®=ÌâF·U²Jò~F_ZŽƒõ[ ±+NÖa­Ø®2v­›Òén.ëtÙÇ2 ÜŒºÜqzKI¡kº¼B—Íë ¥7:§Øð’:×tiu®á½Õ9VRgÔM|”·iŠ•/ueÌÖĬdfIG\O£ÓúHT?]nÙ‡Å{G¾ ^•¥^É!ëi8Ž,»Õøb÷®á ¥Ææ 8Ð9sF³d=nKÍÜMÙ×]þoQH“ßÂ*–ÝRƲƋ°,ŸWÊ`ö6²_#Ú¬¼]âÀ÷F±¼]ƒ±·„ýZ…°Í”òv™=/Ä/ð?Ưpsá³ëVÏI P3p\uת¶æ­_÷¶Þ=HØ‘¬QWÈ.»R™¡ž €*6@‚οnaÌ“H´®í ê¾}‹¢Õœ©Í>»Ýcw¤ºªª{’vš·›e;O9j{b¾†˜ÍpP¼Ý$XD£àq™ƒ³Ö·×-•` ªºkÕ‘ÉóÄ­d«Áj5½­Ö/ŒÉG£…¥ÆÀ×ò‚uÖž#~Qdý›ØÒÁá)¹ aØ–!zÕž+L+j+—… VÞ»ûÞgÜÂn•…Ï3f¯‚¼ïŠ5PdRHÖƒ£´Ù÷’ãçáp¢‹_À‹ŸÛA‹?ðFyð JtÛd—ÍÊã 9ž-9ÿ\Ljv|pâ—Ç'ÿ›XDz¿¶—W/ë!ŽùpK^opž“6‡ÿ1ÂòÉy® m¦Âÿ¸z‘—E¯Õ+ïÙÞ³sI[DŠÜ³cë'‡c;êÖw ¦dgnaCǤ„Ë7>çÇêýw/;ð½;ôìùÛ®ùâ†d{áÞåðšh+Ü GÃí!j ‚%±e½M>å‘ ÊÆ™ä1,¸)&½¥aš¡Ž«ƒ&©¦|•ȳX†–Þª À´ÉjEkË\Vk«¡ô=ñÅ¡`Êõú¯%‹„³YÐQ¼ìm™þ6ŸvÊva»,ÛEÿûÍz x­’5š/$6Ø2ç¯29‰m‚­Ê¾ ‰é‘÷êz8æ“pÌÓÄ‹zÔW͓g ,]¿¢rgò¿2îLBîŒÿ¸³$_ &á°Þä·Ú¼&¦ØÁ‰hß¾žhÙSãõ§<üa£¥8†¿ F@µßÿ"S:Œy‘\6³Û¦ðõœ¤zê'¶ùð¹?)ÛM(?fƪ°þ_ZðäK/ž:±Ž1ÀžÐÙ_ìqj™Æão¤J=…v1…žC »ì²Ör±é¤ãà·>tà侯¶ƒß<ô!x=Q½`oÿÐõýþØà5s—_?×¾÷o__³ìkç¾ôésÇÖ,{ðÜ w>¸eÞÑoíÖ®eÛ e…26SâP?.åõV‰3p®EÔR´}á´gi.¯fª{þ%&ƒžì½‡# «¬šLŠ(KÙb)ÛŽÝX–®#SÒõ¦SœÔØT°IUÀ.F¶ê8½dº€9K8Uz ‰Ù¤&f«b¶ ŠÙ%ˆÙKMLW`8As†Æ²áä¡m ’«aa¶ym_Š…m¦pZ/5/ÛÙ¾úöUIeö‘«Nã)4mè—]&=#z³Çj5vå]ׯÇç5Q8sp[x«Ä‹á#·ò†žöw>²ûe½¬EŒ"öC*M›;d/’·ÃÓæœ:wø+’›‹¡æÏily¥s®4wø«*x£¹WaܵìÝß´û¡ÝW/o’taä¹ìàÎÞ²Ñdyî°«b4Yן1j²×”[¾«säè”Ñl_xÛ–³ÇÇÍ%'3l&È5 ›I æGÛôŒVYý!gjÉÞÙpÆÞÄáTÝ’’Íä’¤Ì*ºmžB7cÝÞUÔy$´†Z‹Ø¥—¸ÑÒÜËs…i¥/…qÓ¼â/k9!Íòó:³:‡ÐMü¶b9yž2û“ž`ÚË?o2!ËÉø*8\å-¾^>’øiÑm5y]N KgÔ—Œ'?óã/OÌ-ÛN —™°8àÔ ûŒ@ár ÆrL ø¼µ 0-#ŠgjF¾îf ÁhCßžÄH€—Bù´…uŸ7ϧ9¼Ãë›h/Mù4ቼžõA¥'(´a^Nhf°8†ŽµEw¬ÿ6Z“Ï³è ‹±‰g …ÃÓPX’âÓx<¾*¾J|cÕôšqÕ'x*Ú£³ Óá÷ƒiˆÒä4”&‰çRW}ãð _ݯ-|ã¦áõ¼3Þ:¯véö6ÅÓ¹©¯qi[•Mßvï߯[þµs_¼çœzýúºÏî[Ú`üø7 Ÿ|á¦æÐ¬Õ{nÅÊv ˆÖI`P{!ò€„\ è!Ùr¶‚˜Ú;²¶V&:¤`ˆøXL‹ÓHÓb Ä4’Ǵб1( x ½dãÐ''!Ïxø ¼ž…¿)¡às¢z_Î ý„¤v|㋪Ó1\ÇSh¤í”¨?ŽVÃÇ¿«Ò‹Où¯*«B' ð'hôSX})eè_nÂ"^¹—-™°D$¢H@½€~÷dÂÒ,Xªœz^íc¬Ä= æFÑòˆøVDüõàìÒp€‚4qÓ•¢K¿ù(B^c{^I<ênì<¾ånAÁ#R-@Úðȳ(óˆcQq5ZÄ™x9²¢*Qhf$ Ë…\mZG|Pa½fõª‰¼þ'm}pjaj^º+?ÝÖö¬¸csƒÅíåUcŸË†òzi]ÃP~š¼^ÑP…Ö8ªTcŸ$ä”ä’k5yÍäw@ÙU g#?.Ék îÜÉ7£! ­¡(E@Ø"NTá3la+ˆ( b3ˆˆ2Iˆ!Ä@ÅR¹„¥ Å¿(>Q‹™_Š•ÿËS(–¾+™Ç&Ïçݰ„ˆ†½ˆxJD_D$E´ÝM|—°(F–”„ {R E“dmê½ $Ãh±úâu:×^§FþŒÿZuƒø12YšKå&þ`P»…óý—(“§ÆãO{Ä»$KñK%uãj¤ø§rÀ Ò¢ÇfBv%BF[—)¨sœ6ˆÿ~¢Í(ûðqüçÔïp†¤ ºÞs‚ø àZê70‡Öræâ/â[Ô2Œ–Ó ßZ¡æè´œ0þ~’zæèµœµx?Ø@_ sd-gæ¬TsL(GÕvbøÏñÕfZñrU½ÜN /·g„ýÁg¨y¹=SöSÁg ðQYA¿¥ÿytч–-½q0Rµ]Dïv¤ºkêzªMÎÚîx]w\þæÊ{¶7å¶Ü»føÞíÍõ[îÝ´x×,w´ok'¼º"}[U¯—ÉZp->§¼gqà‘GÕIÜ0º®§nÔVÀK9mÜôhA}TY§ÞËVz­Nv"çæ^^fÑÜ™y‚à >›íFrŒÑSpë|ï ƒ³h¹ÑÈR»‰´ÇÖpîd=¾Ò´¾4K~Rôo'þœwŽá]yVo}¥j¿!û q¦Y𵩴5VY_)ÀÇDö™,`+ŸðH©»P§[A/»œ£à[ |±š“8ŠÐÙÏÔµØ|K¸%áC§!´\ÝÒWÕ±¦ÝkL.Ÿ³Ì7w¸=¤A‘DÅ$q÷ÕÎÏ×ÛR­fÅL VQqÊv ïkœŸö,ÝÚ½I_Ð ¹fláü²å3^Ê{ç„zC½½¡F‚·¿æß_W}ÕÇ~äù¥÷x¨Ú§;÷+ßÑ ùΟ7]Âòéì÷T+`у+|­³öéBç~JùN…á´HM—Z¹©Ÿ æõjE¦ÇDà+â ®éM 4øÐlŠ5оºY±E¶t_mÚ9L2,Ó³x¨µ-Ô‘öCå '(CMK_¤}U›{þ¼ªž¬ËÒ8Ôê3Hà VYqÉf©µÁò‰4å¹Ù@wµ$ëMV“Ím”zƒÕÌ»²³ã}Eœp§óêé²µøI¼{† gœbhà†=²»ªì†}_ü-þÎy«Ãõ™²‡ÓgHØ>Án1³àÍÀØk†#‘qÃÛGn~g ´Ù ï8tø䓯É?ã›ðû¦:/›1+Úý˜pÈÁcÔÍpXí†ÿAêé3OkH}¬ ¢‚Ç ðá•®i᛽;úælíòú»wÌY°#ïø¸è¯³~ÑÛQ•ñÁìyWÔ%—hpÎálýÈ s—7»]‹»GrOËbHÑôä;àü.ˆÔe¤>–gU¨>ï:D¾§¡ªú|A}ðžû”*(}‹NrZLNIEi@|’Ô-¢Å.PÚ¼†#?ïÌg½Y‚ù,¹à@@(ÝŠa¸Ò³[RFé3!”N ˜&ó¢^´뱪CFod7!ÚÖ©ÄfdCx}¬ ÙcX¦‚×á‚×#[Ü`ЦrŽ»5\ç1Ö'¹¨JwŠ øh®3)ë”bsÖ7o̹ì¯M‚Eô7³É{´N4!È‘ÌfÑ,œuÝ1Ǭy‰A¯wDØÖ…Ø'Ëx=š7öÍ õ5‡úúBÍò°ýIÞ‡r¹˜X jœåØ^A0{½Ô¬C^30?¤±—Š')ñìnø_]×»xÝ þʬÚ ÞØ…ò/Pæ‡Ê,Xúøôõ=u‰â²ºxý…Ä›ÒÄ!~‹U}›ÛÙ°@ëõ:Wuku0íäh{MÖ\;g÷g›ÜÙ˜›&!È‚bCõ³"‹œ¦`ÆSÕSOôgÝz^í§,H¼h÷KÖÈb— &ŽÌ$‚)Ñ$Ï k’Œöx[Ø]sëHG•ÕÈ<ùÞ†ß®âøÆ)ÿpÇåe%êô&AòA Ÿ²‡§ |÷%W,L+{…Xnš†åÂò67W ŽÂ…Ì`pROÿÁ[ƒ6WÔÎÎeÿ…?÷½1‡k7²)"—ËÝ$D'Q‘El'™’3zq¯ô_B:k#~=¾‰rC=Ò ›þ˜“ƃ[(;̱h9­øGp‡ZFÑrð-Qͱj9fü0ÞF™aŽ å@NÝ„­&ÆH?æÅüªO³ìi,uúŒê•oò4fK9à}y7-¨àH(Ô×+VtVÓI³ôù?r"Ú»Îsø­‡Œ‚'õ"G˜Y#Þ.9ÍQÜ‹Üg¬®€Å@6£9%èv¸8]¼†Šb"¶&GÈùƒ ˜Ö*Š¥°¬›-À–ck°-Ø.ì:ìPyçàÖÂ’BãõZT]½·f¯oíÆÐF]߀aËw“ÝbmÖœ-Ø»q ;›íظ÷@q ­´¹ú÷웿¯ë†ƒ½ë¶ï¬ßé^íY-/Z¦,ÛÛév¶:É'÷ܹzY{2Ù¾lõ΃û˜Èæõ$Lê´¤ÄÕât¿÷@oÈä ÄŠÿ\ýòµ¯>`ÕŽ rÙL]T»š´«U»–Ÿ33îg^g>g” ïÃ3~¿ü÷ˆ3µÙlí=èã\&I‡Ð·bCü÷p&Îà‹Ðç„eà·TÊNøíÞÚÚ,îÓ øåMôÚϲµÙ$üÇŽ ?Ÿ¡~ÓºQ5~î0þ]ü1êOXF‹(ŽÙ"(>žh\^“~=M8ÓÎtÌ?.áëJAJ(€ÔÙR¨<“ö¦¿&Œ¥Ò²¼€ÊŸÔÅÆ :-8Àfš™~à TÏÔ¨¨ûLfÕ F§ÁƒÐSÝ1X3ÿ¦‘LvÕÍó;÷V‹zžÓ;8Ç’ÖÔ`³ÿêMî†TØ ZôœXâs«UÊl¼{Íúû M 0û<"#úÂ}Û{>öa½QdXƒ‚ðèp~üg8c¦u§°òýz?¦ÒæVÔo0§K€2"{ƒY™©q£Q"Çmcø­Ç%5’@)¶Ü™qqB¥‰ÁH`9^(—²• ­xN ‡ ººJª¯ ‚w²RñÏ“¢Ñ(Nz¢Ã6J,õ€ÛûKÅï_áM&ÆkvÃ:ÃýkÔŸ`ŸÐÚð}|Œú¼ÿwíþ‡ÄëÈKK÷”z;lÓ›êýÓÚóðƒj›¿«¶¹òC3õG¬§»èq¬ ¥Ci£c |$oÄŒBFÈX›Ž·:¨b «¦rÊ $BÉ_⬪è¨ÅcMÇ Ú ')k%0Lå“©ØsªŠŽxBõ‚B"=:å&£2J™OPì ¤%ãÍÕs6µ´®rñ²ž°rçÖº[ú¬‘Œ3Ô× w ×;sÉÇꃕ3·'êsÖh+Ôß&F›V´y¢Ži–Ä0¢À¶dQà Ers볃õ.`bYÅÜÍSœ;qD¼vø¬¾ÒŽ„´[¤Òî9•–Ë!íQÇÖ¨´Dcmõ;¨Éª‘F-:?‰x*#Nb: Aòˆ7H5ÊŠz¢Ñé‰Ój8†ÇáÛ3ŠØ¦¢1”¦gÀ[ñ\¦p›7ñ½Ä2œÏcq@I¶%U»Ô 6“/µ/þZÐëàXlF´+_÷ÿ®ÝóšÎ¨# ƒ ‰Ý2J`,Œu«1'BãA·Á »a]FåJ(&¨Vœ-iá¡ñÂŒ¶r •ß+¾¨QÅSQ›>Z¨mü€œÇfƒr›ì*þ¶×I.›ÍË °¸*ìŠGbñ%›€ÿõ8€³ÊÑãÈß“ÐIFü5ŠkœdéÏk¯k8ÿòb]%ë” Ü»ÄVŒêíãFTGÇ8½G³O=ƒñ¨ÑG,êÚ1^€/˜÷”Çgeâ”Ô9ÏŸ³›ïÞôïtòn¾cmúÎæ¡î‘‘Îå-rËæÏlª3ÙðoÙL¹·¯lØÐW5ñF g«Š,*Ò`5¥õÑãŽ(BQï3azÇø" ¡µzFËõTÍ õZb¤¬' äzªòÑ©ÊkÖŽrüŒÑ—ñ[Ê:1T U/å!Ue»N€í·Rz m3¥Š?{~ª_ß5Rì88'eJmc«((ó¬Î`ãEOÿ–­ªdQ‘ j=ª—å¨Ë%ØÆÀð ¬J@Æ¥(Ö6¤Øñ&$æñT%TçYÕ튗³Ú™ß,°`ñ&v¼€^8™2RS:ã%Ϻ âðkNN¹r|N)\‹&] ǃ¿†¢îèMƒ Í+ópçH½§Õñu–% ƒŽxÚjlió¬¼sC®iË'‡ Ú«EšZÂÉÒê²V÷oi™µ¥/Äóc€`æÔ„ÅïI6YtMÛîYýé­¢b…ÕžWñÊÛmWû$XâmÁ¾ö³¢õ¹X`Ϊšýã—°iE¦Ð3rÉIMS¡ö8áß7:ÎLߥJÆF'@Eß)RçhƒÙeµ: ¤Ž›È_ù J‡¶}Ò`Cñóe.ÀÏ!Çed·,žsP†JAÑX|¶øÁP’ªÜÁZJÖ©ã 8U:¬eÑ æ«&­á©Bß<×LETaðÌÙ:ñl©Ù¹K•¼ 6M¥Å•˜4ÁiÑûJcÖjÊ 2”ݽŽÂ̘œ<ó{ ‡¬+*¼¼ #Ú,(6ÇÔkõÙEú{ÄÙb—ûY“Aÿ6þƒÍO|“@1‰Hš„ߟ©ä¿ì°ÀŸ&þ‚e‡@SɈv¸•$.ÖV²ç2c`Ù‰¶*ó“`)æ“¢[G…¤=€zÕ>£ßÏh$¸¨ÌÌž/G≢¢ÒÅm÷ÖL$;Åø›z(-#²Óf^C&€f;³ÙÆ3©Ñì´˜¹/ .‡]‚@+8,¢C`ðó(#ymà£îîšìªâH9X/þŠÍBóvSñE”F‰Ì,/øz™WTð‡2c[³ó²GÝ@ø^…Y` ¦þ©;8œêÇŠµH/‘A_E^­Z3Õý÷ø £ >‰¿€0  ëˆ#Žx_Så*ßìÝ wöx½½» îêñÞh©é¬ÉtF¥¦ ^Ã<~zÍý»Ú ŸÛ°öþ]­;>·cå¡Á`zÙ¾Þ‘Cƒ¡ô²ëP]@=ÞHà0¶>j´: ®TB­H]*ƒjR ‚¶ ª|KÕÒ‚7¹b/+ðÜ#§‘5¼ sØýÊ}œl°›Ív‡_»Õ`öZ-ÌA(Ù‰f ü«aЄÏ%¯ÃR˜wT$&• "1ý§ÔÖ§þúÆDt_– %c">×Èí’‰7%דúçÛÒA½…áõUͳ«:×vxÍ™áÞÛÁ‡œ `sZ¼ž ürËf·8›æZì¨å‹E4 Ä£ó‡wÌ: k~|¹›ƒµžH·Ö¨•ì8à5o*o XØ ªU Ìå.|Q¥RvâMh°Tâ)!{Me¡&,—»`É=Rn™R^rÇç$ïëÍn 3,%ë™ÞÄêõÛ¯]ÌI"7XÝ“q‰\$Ò• é8=lm¬¹/vý öšŽpzA½·5ŽäCF“™Ñ™] “Í4Ø9{¾Éa3)ц€#0+vEvX8³0ó[ÖEgåB:œôgz¯z@_Lnƒ¨çÃB£™ó¡¦ë „[á8FaÆP?¥¦™æ€æJgh*œB ?¾Ø>èq»ŧ8A§F]¤:´ÿ/ W_áŒFî+²ß’À/$£ÈOœOó¨.íØ1(¶«ãÆÇM'«R[À_Á(´p¬Rþe¬¤ð¾ïà‰:[ÖÌêiv8ZVuw­jvl‘Cõáêz¯Q5„ãõ^7.8¼².µâðâ7£ëÍ+¶wyªú64lC×õ?VcOà ± Žçqƒu ÿé‰iãç'ˆ :àø ¿Ïø“—â'õƒþÎOËF†{€¶šÝò÷K‹$›-2x{'ÛÌ2½•$uº‰2µ °oáVâ*,‰Ep«_.ýU?þÊ =^SºÁµ*Ä3h…ÍW8Œ¬Jq/pÒÑš¦ßÖîÉT¹uÍ1Úý`Î.Æû›÷6åwÕ¢ÃáŽ&f·¦•T›d‘b’YOË’ÁY›ùós†šö¨5}WˆØ,,s"ï€òÿ…Ç •j®þwµ–ÍjšñN@C=˜‰«UNÅ›®`èÔ_zä¨ñWq%ܳ¾=Ð’ò@i$0®xsxþ¡uÝzÁÈvö]5>oC>ÔÁæk[ý7ÏëX´ \—œ[ïæ ª Ö°h»ê[:Å,ÎjR".Q¶È‚"›t´Iâ–/èâqÝäÏ$vo 6¨c%‚ÆŠ6õTy¨<ÿcPt¼÷h1M-¼AYbu8-Å_p<£û‡Î­ò/Q^ÿýG°/rTDzº£‚ËîáÁm<Ax18P<¢ÊŸÉëÈ¿`vÌ€IÇ ø“ø³Îü) KÎ_g)™¯H4‰Ž,_»){ôßì’ƒ|N “P·“"õ(kùã4[q¼@cªH*ÙzüÈTHü‡ÿÞý‘ËEýÅâr[ÓP³˜|‚¹¯eÞÂL‡Bñ!áAø-þ^|ßÄmÌ[›U‰øíR…Ë'œœ–—a¿Dº—¾âôæ…‰z^zéé‰93•t}Zzãý“~ÕEéåË%v„ýÙTâbZºïéo†#•ôJÆê)WIc0½=•øÃüù©$ _&}GøŽØ#¾TJÒµÓÒx)Éˤǧ’ÉzQúýå’ù~KïTRú¬Klëlë좖~xqrlt®t}Ù½ÉÓéÝá3_œüÆË¦¿£ÁñR }=|J‘»£_.§ªE•ô&J±¥/O¥ê5}j*ÅïªÉ]Î\:%nIÜòÿÙû𦪭ísr2'-Å2z´EJ9-…–AlhÓ:‘¶ÌBÓ$mi3ÐVC,Pµ"â@Që¬l-½Š¢È½ Öô*hÑ«T©ô_{ïs’´/wðÿ¾û|9‹¦{\{½k¿kí'âÇÅ¿AdÌá>$’PN$11qÊerxì–¤W“v—q»Æçõ,ÉùAÒÔ³¤ÌMùhÂÔ ¯M¼qâÞIÛ&÷žl9ucÒkJÙ”37™n:”Z’ú®fÈIÍÉ©©ÿbúpHBòsÁ’–$›ÓÞ HzLúšôué›A.ýžháRÞ£ÔjO ’1*c)–ÚŒµ¿#ŸdîÏz)»ãß“is§==}òô 9•Ar$7%÷•97nî#óÒæ½Ô]æËæÏš¿g~çÍî1  ’'<»à¥þOÈÂkBò¿AJþC)9]r®ä'}¦>Ÿ—Y õå 6$¥’Ò°Ò¾¼8B’„$$!ù?)ï1 3¬7¬7Fór¿ñ~S”éA^:Meùe{B’„$$! IHþËeHB’„$$! IHþ3R.-×”¯¯èS1÷ªe{Ågæñæ§Ìm]eQ/,q! IHB’„$$! IHB’„äŸC’ÿ»‚¿W6Z4 ýcš¨(ŠÀ- þlÂq •ET¸x_f¨âý|Y4FBE‰?çËÒ vµDü+_–S7H–ñeÅʼ|Y)jðWQ3eñe5uƒ¬/‡…Kå‚áÔ4Ã[œ–÷‹åË4%ëÏñe%‹òðe†ŠŠZÅ—ÅAc$”:j _–µË¨IQ»ø²œêÛo _VPQgø²’.ðWQ£¢~æËjªï€¡|9LÆ ϗéë` CÑèßZ]#±óeâgR&~&eâgR!~&eiP;ñ3)?“2ñ3)?“2ñ3)?“rXx;/?ï¤X*‘â¨*J¹”™2PÊF9á§ŒrA[””¿ê¡Å %+=ÊÂR:h+§* Ï‰k&øm‚ÑKàÕ#è,(•B‹‰ª‚ù Í:Ѝ\b©Ð\zÝxE ”ʱ%,üØ`L ÌÖ`ý6sÔXô­R-™ŠÃëëAƒƲ°®ÖA: Ôb~ì4¨U@+êuƒ}N?ž"h7c –+ÚS†ýÀRS¡^ =¨U½Ð#Ñc㑲x7ô0^Á»U0×[Ü0ʈ½ÆB{nË¥²Á&ä3žgÅ~„ç›ðU k"/ñ+Ë[$Œeq»ï©lv/€õ»À 3Ìt‚Ò03FböãÐÃO%Ì «â‘šq¤YpL‘È¿Ü÷h9YbaüÈ. îY;±á_õmp|ÓåÏgÞ9C—s²;‚À©ØÝ®IA@Hr[r¥Ãó0â³×ŠóˆþŠH ÷ô]XEò%¨HÙã…ä'#>ÇÌ|n!zÐH ÎþWæ(ÉâV~gÚ…1Ý**p¾3ó~FY= çKA¸a^îÊê8¼3z\6RÂýª{žë ±Ýò‚ çé*|£0ãÝG»ª‡6ä¡r!ôáu.ì–;GòÑÈÛ€`Í?s:]åiÀê¦#GÐÁö³y´‘}XCn'þ °û÷N8•W>åÐÎø#Çt!ûMX`â×"ÛÊï{ÆìàOá^AîEåü> <&¼²ó÷²‚ ß»õ§À=8å»ç³?`/üÒcìÈof>×ùX5ðwm+¶5øÌ4ãÛ¸s“·ñÊ{ å®ç<ìöÈ ƒÞ!ÇÃUë£ïj„Ñ=g·¸nÙMð}÷Ùü®ÀÜ ·`WàˆšÀI$ìa%¼;C)ˆ!vüþË‚ùVt«K±-&þ¤rû÷28—=Ãï¸G‰Åoƒ×]¹tõ^ >á Êà“¦+§ž¨Â~¬ü÷Q8 ÜøÝ%ñŒ)È#~Ekü²F‚Î×ïäc’ùpâMì’ÅÉml .÷të¶â3B8e‚ߟ çDO9¥ë,'Îd¯JyÜ=Ÿ¹ú+ì¨ÃÞ‰YjÅÚI]þÎ÷_e€p¾eQZÜ›Oe@mœ–:Ü’ m,dQôÌ„Z:´¦CK Œ(äûcðNÍÂçPŒ+ÆgÑ¡ƒ×<¨ÏÁ9.ƒbqÕ¦Ãø<Ð…æj©Ùx -h+Ä#uXw.´æÀo-?ÍHƒ–b¨£r&΂d½<˜EÞCdóg"±´ÚY?®VeãËr¡¦ýY|¯tgc}È~´~.çùíÌà-Õ`!ÍHgX”ƒk¨µ~À¸B¼¾c&Öæa ÐO°h±håx+‡ü3“ïA{„ìË  Ò`dakþKƒß`9ÒŸ ½Eø„ȇ™éi!öž–÷B›ƒkTd§Ò0äUäƒt(çÂO¦ßw:üJlÑiëê»Y¸?0ŠàÓð¯iØsù¸Fv# ׊ð^¡Þ8~/uG÷Uga&jñ( F\ègHf/±^`'Y#?ȲÚÛ`[V³¿#D‹Ð_Ìïôå~A^×`Ÿ » ý+_I3ÄæN6‘KHas͇Íi+s±i6‡ÝæÐ»Ì6k<«±XX¹¼Âådu&§É±ÄdŒË2•:LUl¾Ýd-ª±›Ø}Ííb-¶r³5Øì54ƒEš¹±ìõèWr«Ó[ìl–Þj°Cë4[…•Írh¢ ³“µë)³9Ø©æR‹Ù ·°üŠ0Æ‹²N›Ûa0±ÈÜ*½Ãĺ­F“ƒuU˜ØÜì"6Çl0Y¦I¬ÓdbM•¥&£Ñdd-¤•5šœ‡ÙŽàá5Œ&—ÞlqƧé-æR‡­¡g+m ÖÑ[ Åa.cËô•fK [evU°Nw©Ëbb6X×l-£`¨ËT 3­Fp€Ãjr8ãÙl[fһܓ“u˜…Ùkœq¬³R~5èíPFS*Ý—Ù*­îJ“F:M.¬ÀÉÚ6Ø d-h·XlUl8—5WÚõk¶².äk° ¦F+¬e+cKÍåX1YÈeªvÁdóbS<ËÃŒq²•zk kpÖ»‘û¬àd‡°8ÌNäQ“¾’uÛÑ2 ±Zœæ[a¸Ë€– Hz6 ’¬…Èc¨Ð;À0“#^g*w[ô?¯& KOD|7\„¶`||âØ.®w9ôFS¥Þ±áÀ[êgf9xÜŽš 6€o5›œñ9nC¬Þ9v‘ÍtØl® —Ëîœ8fŒÑfpÆW 3ãaÂWÝVîÐÛ+jÆèKgh(Œ´¸ zg™Í ‡QÅœn»Ýbâ ¾xvŽÍ «aÝ@!"+jFŽ0ÀÖºLq¬Ñì´ɆÚfè5ÀüÖÃ6š•f— Ô•Ö`TÁUÀ›C(”¡â.Ç<0º ®8DÇ%07Í€ý©ª0*‚,«‚EÍVƒÅ ÜXo³SbÍ#IX  ¿g-‰"à:ì»Óå0!…0]“°bͰ ÄJ%9F[•ÕbÓ»zOO\Ì8°}¨àvÙ! M&Sa²Ø»zòp— GbÆqRa.5»P~ +“Ël(Zɼ«ãØR½lµYý™BØ„Xž &k|•y±Ùn2šõñ6GùT#ò9e$l/¦Ž¤¦ç$ØSòzŸ‘ƒFCn^dLÈ5KHlØÝ]Ó$re—DV€6ljƒpƒ L0 ˆ ž1ƱeHz(D Ë3ò1ø v¦³¶RHvVä=NÔÏ®2HïtÚ f=âĤ,«KOò©Ùž‰E» e ùL}l$¶Èˆ³!Ù‡Çá<‹šƒèÇÓ Y/t[ÌÀS²6Òå '¬€ƒ!ŒC¹Ü\†~›°Cìnä¬À ªKÝ(x¨‘g À&”¢mv3ɨW4•<,I‚†÷46¢ªÂVù;Q¸V0Æ„mC±-‹L—@°üF3¼‰„âÆ–˜‚\«Í…B†$s3Æ„)|—³¥¦.‘«ê@Ë;]@&3l‘ÿäù= xËÒ²…ùE³4:-›]Èèògf§kÓÙM!ÔcâØYÙEYùÅE,ŒÐiòŠæ°ù¬&o;=;/=ŽÕÎ.Ði Ù|›[“­…¶ì¼´œâôì¼Lv*Ìˡs="”å³hA^U¶¶)ËÕêÒ² ª™š“]4'ŽÍÈ.ÊC:3@©†-Ð芲ӊs4:¶ XW_¨…åÓAm^v^†VÑæjóŠàÈ̓6V;*la–&'/¥)ëuؾ´ü‚9ºì̬"6+?'] Sµ`™fjŽ–, Òr4Ù¹qlº&W“©Å³òA‹ã­›•¥ÅM°žþ¤eçç!iùyE:¨ÆJ]‘ê¬ìBm«Ñe"‡dèòA=r'ÌÈÇJ`^ž–hA®f»ì AõâBmÀ–t­&t¢ÉÁƒãÃB „>ø'|úXàûX@‰B üw~4@v/ôñ@èãÐÇ¡ºgóÐG]?"¼ú˜ ô1Aèc‚ÿu@l’ïPTgµ’êéñ#Ÿ¢cáw;Õõ†»ü‹µj5 cDìÕŽ C㙨«ß«/ޏÚñh¼D~µã{÷Fã¥ÔÕŽŒ„ñð›BßPãñbøIÁ¯×€›£¨Ô Hd×SI8µMÆa:6¡¶ÃM- ¼¶ï…ÚÃÔ­ÔNZD½@÷¢ÐÔ;ô@ê/ôê xÿ<]@]¢çÒ*úf:жÐ×Ñ6:‘vÓ7ÑKèiôít1½š.¡ï¢+éµô­t=½‚Þ ¯ÏÓÓèôAú%fý3‹~Ÿ™MÌ, ¿`Òß2úGÆA_d–‰hæQ³QÍlÅ0gEIÌ9‘†ùN”Ç|/šÇ´‰*˜ "7Ó.ZÆü"Z%ÖŠêa7wÅ-zô*qÛ÷í€{ àÞ¸·î½€{?à~p¸?Üm€û"]@+w?À=p'î›w.àž ¸ €Û ¸oÜu€ø~Àý(àÞ¸_Ü÷€ûsÀ}pÿ¸/2Ã8D*ÀÝpÜ €{àÎÜE€[¸­€û6À]¸×î‡÷€sOWÜ’¼ Üý÷u€{,àÖî|À}3à^ ¸½€»p? ¸ŸܯîC€û8àþpŸÜ¿ÑCh5 {\@Çîñ€[ ¸ w)à¾p×îzÀý0àÞ¸_Z3à~p·î³€»“™& gf‰3³E#™"ŽYø ¢©€{:àž¸ €Û ¸—î»÷C€{'à~p¿¸î÷W€û<à¾Ø·<;÷À ¸“wà.ÜFÀíÜk÷C€û)hypÜî3€û:‚ÑépÀ=p߸'îlÀ=p—î*À½ poÜOîç÷À}pŸ‚Ö¯÷Ïô‘ˆ>(ê ¸Gî$À ¸3wàž¸Kwྠp߸7îç÷À}p ¸¿Ü¿1팂ù…é+Ö2C!7îŠ[µ$÷µ€{àž¸§îy€{ à^ ¸·î×w à>¸ÏR·Ò2@ÛŸî\Ž Çî©€ºp—î[÷rÀ½poÜÏîFÀ}pŸÜ?ÐwƵ¢kèzQ4½YG?/š¸³÷Í€»p/Üwîu€û!À½pïÜû÷AÀ}pÉœehæÓ›ùŽÊ|Ï$0mŒ†¹ÀÌÜ7n3àvîå]q‡_Â=pOܳwà®Ü÷ÀÝ­Ÿî(#ÍPtÀ¸ó÷|ÀmÜK÷=€ûaÀý à~p¿¸¿Ü?Ñn‘Œ^"BßW‹&îé€{>à^¸«wàÞ¸wîÀûàþpŸÜߌ„q0½˜eL_ææf#s³‰ÉÜ€»p¯Üî§÷Û€ûCÀýà>¸/¢sO.ƒ?±±éK½^¹„–Kíuxêìr)-—·ÕÕÂS×&CO›Ç<]*<,%Ýãy¸6=+¨«oð5Ô××Éå´\ÙØø8<>ˆ‡55mß¾~ýÚµxX[]]Ò€µ•Ô{RÙˆú¹„’KÛYòà9µä©Æ=uu¨G*¡¥²6yu]]5Ò%[ê:©˜–JìÈ,;n—£!0·×µ{<Õr1%s©m©èARiu}}‰Ç‰¦=‡Ð9MËÅʃl¢Çƒp5Ô“þ‘Êi©ò…·WÃƒ× “ùåàAf€¥u+®‹är)CKÅ­D ˜-µ{|\D«LLÉÄÄ:«A£7UH”TQç©óCÊîJˆ;FÀ$Ê#¥i)ãAǶ‡†‡ñ0`½¤ ¡ä’‚ô›F¿´bq«RsSSÉìÔT\Eôx< sä2Ø .+‹Ó­]ëÀ[áa€P)iÀÖÎ÷Èålª¿b—ËùaWPPß!Qr&Õ—š*f`3ZÙÔVRHe}À%õ”D•xº2¨ C,óx€eÿ"3e~f*h¹ê€ç€ç1õ ȳ—1Töb¨BB+dž`ŠJ Eq‡ÜÏQÔQR߆:Ä”8ÚIe=°T!¦ÀRž¦ šVøiúoñÅÔ_7žâ0J½J¢J¢Õ3Sö>v H-ž†‚€Dž†¢€„^q¶$ÁUÊ(¥Üxîrþ™:÷ú#!‚'”x)@%¥Uˆ¶Á± ãc÷‰{òƒ?dз QÔ§ÜÒ®j»‡ƒJB«P8ñ ¢iUP<üAUã¼Ñö©„€¸rD¨d$"PY "ÂZ(pS $ø˜Pá˜P)h•*nëéTÅQ3¨ÕÔ xwº‚ªö`ZT× G·R"RÊY„ð½èñÊwø«Õà>eEùƒDÆPj1?nojް£M(£L"C÷1‹JI©”jxod(Hªç@Jõ¤ªä´J1DOð¥ê‡ º²}% ïÊvL0|Äê„ p4UâI¥ÀÅÔ½D§ÄM©d Úë?A¼ª0ZÕËå‹jˆmˆ­ÏªÏB‰b…|…Ü+ÇZ|žz:O-ˆd¹kÁÇ 'l@u-,ÂÉ‘kT2JOX•ç²GCM¡ðP/Š.2_¥ptÔ2Z­ q€¥¦—ºÜÿp¯ž‰¨7c"ÓC½ð{J ÄRUèîê1ïÒnʽ^r®QæQKiµ<(ÌjÕ4­Ž3\EËÃ_ö5ã„#º"+•¸’’NNTU G® ¶ |¿äÃÍÃ_DP‚‚ü„ΈÔÔv$k% h%W£…§¥NK‚¼*'^EneY5-RKývD9-’K<äÒwj!îÔ—ÅDÒÚ‹¡Õ(î„Àƒ‹ÛpI<yjŠ<µ’V«Iä¥S±{@#{iÔd&ñà•‚+¨ç9s ׫kÁ1b~:ÄŸH$CQ¢îÓgDzzm'ÊV @:Lê@Rî€j¥V…SáÔµX< žßp– ãD­ ÕÊhÊî)¡|AR-цu‰j„w¾ ç€§Ñs‰Â´º„ê¸õR á‡§G{ì©D÷A~z‰Ïî‹ö¨å°pcsKˉ¶'Zš›Õá´:¢uPë ¶ÉGãNXNXå´´4­=¸¶QݨƪZ}m¾£¾ - Í oú}|XUGsccc31[2¹Ìçk­.•¶T«å”ZÑx°>_ÞsÀîT+ô½g,…dˆÚ/øò¸´Y*]ÖÜüÎ’09¦DóNiDÏ™Säþ[†••MÆý <“Êqù$tiÛš›ÁI¥“ä°g“KJJÚKøGúïh†g©oÌXÖ}‰ÆÆ0&öù(Êou˜ŒS B3¸³íDKK3?&èQ¨iE¯S­_qÍ]_¢ýªqmr.—MVõ9…‹üP¾Mh–@Wðê&ävõÚjt|KÈR°n~ðC8¥G[kˆ*ßdÜ”´gr[Ü­Ôè]z«j ŒÂQa"Q˜4€ðCZTH+(ä ”#¡&oȇ•µ´´HdT˜¼¬¬¬¥N"…D`±XNÔÓbZ"m‹@ÁÂY‚2(r¸—øõÃÌ0y zÂTtXØŠXVFM¦R| ! ƒor ¦!ï;Á}jeJõ t¸ai3øM*UGq%éd)´6/Åá„ëÀ¯‡E2¨fªÓ#¥À .±ïÁ–A°‡K[‘‰¾6¡ f·ý” H¦j‘:¢§=ÿg賑Ñb-çËñNRž‰Ê‡¾4ŽÕ8*­qlZÃÇfšl‹ñ«^&(£¿YÇæè]Ön4¶ÆvÀÏà-ð»1iðFÎ;ø~©â†•Y+/„Ñ2Qƒwp-4¡L âRɨpF4PBqz©r”¶Î›,¢Å …Ü ..¨eÐcC<ƒ`CäãÏÀmøo¥ ¿31 74H™¸Ï6æö]ýiæÅè76Lzv‡aÆÌ·7x£Š9¯¸‘ó2»-EŽ›«=ãi÷@³ÜÌ…ù­…“‚⪰™L±X)*.Lˆäz£Šá:n8A4¨GD…äËœ—ìaZB1^ºíJ‘—¦©Ýªá×>y¸.¶Ïø/+n–ÖÆº5«®yòáI¢’­»3^P†=ýø±° í×Ï>:èGç‚N[Ç ›F?ðóµÃë~ž±ï«‡fÍü-÷Ècã^>­?RÞGÔ?½}uß̆ÑÊ{©g¬òM3¾²ÿ³µ£¾i\9ö…Q¾{~‰Ù,åì)Ÿ¾ÙäywZɦ[¾ø¬ÑöbýÄÌÏ#T»uó—H ?þÔŽ¡Iu}ºªþôg½–Þßåð»;xKóã?ï)ˆÛ2·eîúàzo}±¯ÈtÖº¿?5z•ä¾5 îN^«Ø²¿¬ÕZùakô“Ÿ¬ôÖÛÿÒ¯ÌGß0&?æ×¹§ÛÏþ6\üóbí>·ûŒN}¹3ãEo8£E ÄÑ6/­H¸ÁàÒÁáâ~â>¼ñs➺„^g¬??å„_ç‰z)0‡Gqý<}†'µÿE—aWžK½¸äâ¾Q{ÇíëÅ¡Ñâ\n:—ÝÙ ]™Æ¿Âà°tûRŽ}±µŽá¿ÞâãßF´‹x•ñ0„›-•C`J Óân—%Ô9ÑÊÉüUUU=-`rüŽf‰ì½N¬æ”‚JFÞ- Ä’Mó¨Sßo˺ëË‚ åëGøl÷îOýtÂq¹«ãžœ3%Q¹¨¥c~ñ&.ÿýNõc+>¹îMñDù…¼/é}ŸXÓLy­7Ækí#Ýïç›óûUï{ç¶)ßx:wï3îDÝÉÆúYý:ýb½¾ßœÞ;ªø-ºù|\Œì»ã915û/L6 w[Â[§Ž vwŒ")5ùG³­q¯I{äÄÈ¢?=™léóè¡jË‹žZU½-Ù¸Ÿ^wöãÔ;öŽ(Z/™û×;öÅN¿æÑ$ï]cbK’#ΗüÀë<ùiâÅOÇnû"uÜÐ×’ç%VØŽœõ5­7Ü·±îÌ7m{DÏþra~ǧË“–ýiÆÇ×FŸÕý•óJiHc JcM[Ý~ëò‚¿uâ4Öì5¤±eH²ˆå®'AÜo4±…ærüåØXô­ÆœÍ’¹”„„D$‰d³@•sý!öñýÌúÿa6ª[óÒˆFÙ½›=5};®/épÔÅýú÷më6d¼¸íÈÂÕc&Žr_õ¯KwF{éço=2ð5æpÆ·o=xá¢xð+”ì[(¿ñ­˜¨Ó±Ñ?‰×k g¿x¥ïÚs‘›Ç}’b/²M:»[«à²ì¿—{P}dÉÛœô«zï®W×”¯`Ï yrÜù[ÞluQÓ×¼ê¾oW_ºû×Ý%u7¾þrô3¥ßx«voý3ÇŸu¬è⸿þù–ug†tž½eñ‘;äK\­3²>8OÊÊÙ&wzNØoK>tfî+~:¾¹Wô=O|YÛÿÀñÃ[ÓËÚ¹nìÆ¡Y‰íoŽxŒznáá;­#ç-ÿ>ÅêùñÕ³‘ªo…lä,%éæ:”nü'sŽœöG*”®Ž/­}·dÂ7åoÎÿЫ»^lŒÜÄéPwo1ä¢í™œ¶ûI“Ä%¢ª$rTâXŽKHeHá’JÇ™ô£“&”&NJ›2:eìøÄÑÆ”q eúÄÄqIe†.)0Ëj<] 9æ}ªrò°ç+Ÿ<ì=påØc†²Ù8 ]€ÇÀb 0âïBô2šKÍ¥à¨JÅÜV‚R ö. dÁßYÂÅ©‘á‘4Ý)qT·pf¼"š’ö‹>9ëÍ‚CÃó›Qýѹößþüú‡¾ó¿\;ó\á!s¦äæ#g?ïxpÞ {§Äú$ÚÈÖÍ5u¯•í:ùê·¢âá/Þ8¼ZSùLûyjîú× jQ;}+[¾÷ØU<"-êù¬Õ+þòþ#7 øKŸÕ_ݽ¥bD]Ť=y×%šcøíáûûæŽ}~fIþGÓ^N¹§3þã½ ·§-~»úݽ¯.¾÷NË*ÇSß<~qËÇOè0¾]9E~zé{w¿¶í•ÛÞÝ0sû­³\“YúþÐï;&7%¨.Œ™b|<ÙVRpÓ‹éõù ª»ö/›ýóÁòUú“nj:´öˆ-ó3_üús{ÞÃUž]”ýäß6,9ôº¼éÒ¤Ÿžq&KŸ›ùî€^ýiýáUƒ~ð,¢ó_¸v¹sß±yÃnš8;êÓºïÊ›²wŒ9uÝ]7.8z6)ý¾Á¯Ý§^âò}Ó‰Ñ[Å¢{²~ùþcÑ»ÌcpÈàøžJ}¿Š$œûu¿Â.ÄéT©Xwýêûˆ3Òú1ÀÆ„\ÿ. ?Y†£HÞÈ›:› ’'P×\f6è]&VãvUØfW Jî\2—ÄMH7–›É=1WÇr¨ú?w‡þGù}ËVËÞOOf­»aéâøŸ½þùo=8cxÁîw>ŽÊÑë»÷v¼—³Ûű½¿•}Xô@ßìõ×N]÷̦ùÜõ¥}ÛëgWËz]oj[Ý}dìˆUüð÷òAq·}U7ø›¯ò¶m=0¼ððÝ¿jßU]ðìÑ=SÅýò„åþòbOeîYyôtlF|ÌÓ+ó‹uê/™¸‹‹êë9ëªçpüºìøÆ}_ݸ¬ýýÈå/Vêþ¤­ß’EMË,ë3²ìÉ_“.ŸöØ/µ;zgöQx·Ôž+®¾Do\ _AEpç^üdxÆ«M£‹¶<;¤Z“PÕòЧ“î¼«^ôüà°½zŽ~gØô¢Î_$o²*!¿ïìàzù3Ž„càWP>ïñv‰Ò÷à^b1ðo%!UðgB_µPÜòM$7/¯ç–ßíéþ´·$ufÌÆÓ×EvÜð™²ð9_nßjØ®ÿÃéé¨ÙÝoë´†Çwç8gÿ]oâ È¡ÍÁ9ÔÖ YyÓÕß‹ýÝè[î(•ã¡(è@Èâ2¸ô !培#iÿ¯˜ó k*Yã8 Mš¥H‡„’(" EŠô^K•Þ–€4éH,‚´¨ ½ˆQ–Bè ¨(¢7à.pÕ{ïÞ÷ÙómÎ<3gÎ9ï;¿ùÏ<ïûµ×¿¸&}k\l»%¹²äñÞÝ€Ñg=-@Ü×ÛÂRú¬éJb-|ñVœ»m­ ð‰67D7s,è´I}…éMŽ)N@dY}àÆ¾eÀêtS"ˆ²+^mzÝ€yL§4ev>Þåehë›Ô *ñëäÄda~^¯wŸf3á´ï©§½Xµs\A>iµxéìËbzt‹¶–ò,7¸å§©ÙPz‘—ü‘²">à®E/Ù/×A‰6MÂú«Ú“KÚ7B:$D¬ š—‚ÁŠW |xV=õ–€“ &º¦Œ·ç:šÄÄç?\ìÕ3^ÈñJu+“Ö|‡i¾Ãd [»•;CÀfÛ-{Ê »î­®D˜û°|æö¯¾µÚÞ|Œ‚þàóúqÞæ*JL B¥Öå®<Å/¡žÐ\f„ã‚"£[W./OŸQ„X¿¥Ö+:8Œ ÕVã·6_4^+ÏÌé9çÙ&äKubÕŸ§9 Û*dø ÊE6ïosÏ)j¾£ºÎ蹋r«þ<¡×Ç×íØ˜ÃÅh”«0K¬å™»_Ùcw/ÐrP®[–ZYXJÈO÷c{ñãGýzÌ#ß"N 9-¢‡ghé”N÷ÍUõÉ÷Ïpp—s×ÅbÜ3$ì ]‡…å°;~ø£x®<܈ŵR°‹ÀR!°”¶¢€.i`äßÊ€°èÿËTŒB ¾:$ì¯8ä¡"@’°!…BHH…†ä~‰Ø+þíŠ üžÀ=vIì ù\éúGøÝa;X­3uLyòÙ…]‰æºwj©¤Ø(ÔëBÚiNu}Ì8 ^—jˤªì’~ `B*¾ˆ¡ÅØG]Ký™ß­"W=›èd50‘eP m¯x]"Rt¼âUºYÏÏl”DGÿ”¾ £ø|é1Ýçåôð#8¹_©Óæ÷Ís–x–-•ºI)û2{‰À¢|;z±þ±=3NMûÒS¨›§mʇ4¥Ê®í̈˜3piCoùL2ž«Q·^YQJ}¥úJ$ûk¹ª8ôBŒNÛ^Ül6IF¬ü´iGÜgÔ ¹lUuEŠÔµœPÑ·ÚÆÉ<íÒö!uÙôwâ‹x²UGÿÞz½O¿9.5ª¡…ÇWÀšú W*%!}IòùÕª”r¾âÇe.—)¨zŽuô´ú†œþ£û&òüäëýAâ/ùf¼Ðôz*„m²©†2 Öz´…™ÐÈ>h¤1/§'ò©7°Ö*_½8ÛÚî4é3Ï?Ѭ’Ù±ÖÆa2¿¬¥Ž(.M˜X¶È«ø4Vé8ÝŠ »²2´¢1¯+†@‹Šƒ/‡¾‰µ ´®xe’mÙ…þ¾âÞMM¼pV§uêºṛ㚃…Jâ¾iï=¶¹ME!èŸÓnÊ霎©Œ>9ž«½•^Ù ’ï–109wÀÎ;‰?Àß!<¨K~:hÀ¤ 9"3ØîQ"SøW®~壊ÇG왤ô‰R{j±¸ÙÏsaþn{[¨:ùZù‘êÿÓ¦ÉoI^KrÖQb8mBícÎêæôºí#˜Sük˜ûýû"ÂòöÏM†C„¥"Â’>œŽÿóq@Ëéÿ&³ö2ÏÞÌÙÝÆcçõ ÜÉ×qá  âÌ)7'™&Ù^²ë½8*ëý8ª¯qwRé—?"â"áÜœ?b—7" 3& 1lðþ—y³Àé'¦ìR2Óƒ04I­ÖpQ¹ívŸ~÷ðÏMò  ™fÕ’‚MçQ»f^‰BÚ!")ø†Š®Ñ0MÊÕ6 ŽÍóŠ7ôû*w]gä¨á°¬7²ì…ƒ÷9R¥§‰öÝʲA|›à¢$ßðø­'‚@á¶X†úÛ%”4Y+NàiùÂò®¦êv\Ç=Ì3Òg÷Z7UDÆ?Éô5J¬y”ÏU­ômÒUdBqZt²àc1C\í(Öéõ±g¹÷Ô¥AAmï–ÏU¿eŽÖ»h*…òb ©ÚÚ=ÇíœQmãäáY\ãÛ~’ª •ÃÊC´Á-­·S‰!žÌÁ‹ýç.;´£õm#Û9í$q‘#›Û,ø›BSO q}«h;… êì(9ªª~ª*?.¦&›ûë¿=f§hšP褃®Ž;ˆ/ãÞá-ӇɆð*f›¸Âãj ™¡\}d°Žª¬Bù‹§$ܺ•ÄûQ-«tG•/ômîv³knzÉ/myñl&†UãËÏÉïMÅÇO7–À¡‹Î2Ÿ+š ~îvɲý9ÆÚ:Í¡&¼øÀ(ž 5P•üί½·Ñ­øè,ocmµ‹-ŠÝYþ P5×]L^k£»»K·þ/Ú Ý§H,E%KQai7¸~¼xx8’öhoòùÈ“#iŽž¼FqX#éGk™|‡ )¤©m7U¹8aã÷¡0Æ X£{Ră%¶q„ý‘&4Hc„a¾p(ô‡é ¿Ï  åÿ·žmx9Šû6S`dª Eár=Í…¨F‘Vúâõ=jy$gPy€ª¡eóÙ3ôg^8òQè'3/dÜdqö±-'ÌÂa t* ç¨U·Ç)ö£m±NkÈÈWã÷zî&¯Äé…x–(vjvWv;¢ÈFæësì d:Ý:­wˆ;uÌ}8)·ª5Õ¨}œ_LdžN›ž2^èŒ>ÆØVä–™=·ÓsØ>ž¼Lí¯BOqæÞ$¥ öV…;»%jô±2Fµ.m E¨1;†&IÓJ¸ß±/žkÇñй—¼C„õÍs¹V1õTežÁÕ`j›%#̵c˜k8/ˆu¾ƒ”’˱³ºË1gpÕf\Á_µfw|dnûyÎÎìyÝL5ŽÖùÑ€wÚïoý•N>Âô~v»œ‡¤4˜Ó¬­Èe„ÆoFSÆã*ÁaK9I×ì^´B?ñöËÑÂÂÈ(M0ÄtXQtУþL&bI‘¶%œZoêÜf©™fu¬?`­¢ ¶áW‹”þ-^™ìŸx“ô endstream endobj 80 0 obj [ 0[ 507] 3[ 226 579] 18[ 533] 24[ 615] 28[ 488] 38[ 459 631] 60[ 520] 68[ 855 646] 87[ 517] 94[ 459] 100[ 487] 104[ 642] 122[ 487] 258[ 479] 271[ 525 423] 282[ 525] 286[ 498] 296[ 305] 336[ 471] 346[ 525] 349[ 230] 364[ 455] 367[ 230] 373[ 799 525] 381[ 527] 393[ 525] 395[ 525 349] 400[ 391] 410[ 335] 437[ 525] 449[ 715] 455[ 453] 855[ 268 252] 862[ 418 418] 867[ 418] 882[ 306] 894[ 303 303] 920[ 682] 1004[ 507 507 507 507 507] 1093[ 498] ] endobj 81 0 obj [ 226 0 0 0 0 0 0 0 303 303 0 0 0 306 252 0 507 507 507 507 507 0 0 0 0 0 268 0 0 0 0 0 0 579 0 533 615 488 0 631 0 0 0 520 0 855 0 0 517 0 0 459 487 642 0 0 0 487 0 0 0 0 0 0 0 479 0 423 525 498 305 471 525 230 0 455 230 799 525 527 525 525 349 391 335 525 0 715 0 453] endobj 82 0 obj [ 278] endobj 83 0 obj [ 306 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 416 0 478 0 0 0 0 0 0 0 791 514 513 514 0 343 389] endobj 84 0 obj <> stream xœì||[E¶÷Ü{Õ›%«[–teYŠm¹w;.Š[â—ÄJ¤T¹Æ Jï!Óƒé„Þ,,°YveX‡¾lÞ.}—…]:ö±Í´±¿3÷È-„ö½ßoË÷ùXçþgΜsîÌ™™3#§†¢…‡ˆ„ëkê:¿h½-‰·nAY}MKퟳ×î%äõÂ~ж8'ß©¯zaöƒU¸g}צ«Þºñ!eaB¸ÔžÛøûO{æ B–½JˆØÐ¿iÍúGv½@HÅÙ„¨S×Dv÷¯}¾{+!«? $íº¾®ÞO‡÷<þTà¯xê»ô‡¡^õÔõÛvùŠæ¨ÿ‰’‘=]ý ›–²ÿBB<ë»vm²_¤û3´€>¿¾o[×­—;…?þê»6t­ï{Qö¨—»?&$÷êM·n×’sa<RýM[ú6ýÆ(ï$D—Gh,$åo}ü;åÂÕ Ÿ«ŒPzà¯{Ÿ¢øR㮤c…ÇŸTdÅ„%r`$°“1ÂUÜ|¬ð˜Ú s™FrÕI<Ó/g‡ón–f;üý=ù{ ¹ó›ôD-äföàA÷%²ƒÊ˜ žKÎúçôt–fi–fi–¾ØëÉ_Û&×ÿ3ûrÒ>l% €ûÿÕý˜N\ùÇ¿º³4K³4K³ôÿ&‰Ö“ÿÕ}˜¥Yš¥Yš¥Yš¥Yš¥Yš¥Yš¥YúO¥ÙïÕ³4K³4K³4K³4K³4K³4K³4K³4K³4Kÿ+â✌ÿ^üjPb¯""r1Ô“ˆ$ (©I É" IÙD¶’»íe¼–?}|\°R—ÐÖCÖ“-ÓÚ˜ñOÀ}ýxüo`²¿üëÓow¿Õ—Ixˆ…8¦u¨‰»šÑ2IŒƒÙÎì`ö1ç32— ó7¡ùøíѯˆÿ’éÛþ=3õ†“†bË·ë[¨î»«âOÅÇ %:âÿéßjåùCçž³më–Í›6nX9eÝÚ5ý}Ý«W­\±|Y(è\¼¨£½­uaKsSã‚ù õuµ5óüÕU•sËËJKŠ‹r²³2Ó¼žTwŠÓbÐiÔJ…\&•ˆEËÌzwC˜zÃQ‘×½`A­»»@Ð5MŽò j˜©åÂ?SÓšý'húQÓ?©Éhù R‘•É×»ùèÓun~„YÖ„òEuîÊ …²È+TÔPq¹À‚¯· ÔñQ&Ì×Gv Õ‡ëÀß°RQë®íSde’a…ŠJ(EÓÜ›†™´*F(°iõåÃ,‘©ék£œ§¾«7ÚÞ¬¯³¹\!AFj_QImT*øâ×Ò>“ øáÌG‡.Ñ’î°OÕëîíZŒr]`4ÄÕ Õù¢éîºhúžw,0ä¾h¦»®>êsƒ³æE“/`¢bÖÍ}B óîѿ͔tÅ%öB‹tˆ“a‚ö‰2¾Aa|.íË#~Ò •è`Gë<é¶Åˆ?ÇвaÚòèD‹1@['Z&ÍÃnªúpü³cÀìæ³2!úÂÇh磜7ÜÝ3@±«oÈ]W‡që FýuPðwÅÇZ?œ›ú]aÄZ†Ž`4ǽ)jp× x:k“¸YÔP%ីU4§¾Žö‹¯ ×a©/wGð)s¸·. …$Dû5Õ¤x뇂½ýQgØÖ 볟Ú\QÂrûBt–ÜÚhú›ð:—ðFÁ Æv‚ö„2¹Ô#レ ÑÙßwM4haº„*Ñš >ÈØÈ„¼%®AK3ü@…óÔ. M5­]`s…\HßÐ%[¼ObOT6Í—“}Â÷|m×P›v(¯ï«›ÖÁNÅñƽ¼Ÿ,EüÅ`!£Ó¹`¢‰óÀÎ nE %í|ÐÝç¹a ùÛƒtl4ÖÂü6/v7w, ³_%3jØ^е(qAóD…­…5Øà³ML«PŸ/Ô'« NhnœhvÓ~ õÎC—²m˜ âÚ BÑ6_Èíö¹]´ŸY™Ã2¢ru†ka¯6@ºs7t¹!Û7 uŒv ûýC›êÃå°/†Ü½CîÅÁ ›ÐùEÁ}¶=ô݉¤™iî¬W,©v3û;†ýÌþÅË‚G´„ðû;ƒ1–akÃ5¡áTh á ñ R–J©VxZ¡žAE&èÛŽø ZE‚@¨÷Œ0DÉ&d éaQ¦Åy…ùáòÒ3"Âÿ„¶d2” ¢vZ\[-ZÚr?aéu6" `¿Bì—ùå~«f!¤TÉý +gÈa£flÃàs‘ a‡å~ÛÁÓ¢¸æ hRÙठzNÕ¦9‚÷áÀS#, Vð/¶x#¡Ãâäûá†ê@;ާ}™pI颋›À%d‹JáHêïês»àìˆÒ܃ѧ}Å7 ± ¹‡¢ÂŽmepï… ×H>›|î®>zyî§wç>Á¶º+D‡z³Õ»a÷Xˆ%’^7}ô Ñ«ùʰ"¡J∠ù®„sCäíY†CŠžE¼0Õ]6¨Ai-ŽPQ¸øioÖû†WJ=Sá³Ñ‡Ê2Á+ôlQ0Ú>¡"ì$ZØì‹²æRh¤ƒg- Nd(Ž67Bxý°ªlÔš²ÁøôöÔÔ61aháôˆï¬ÉsfâZaƒ˜~­|Xΰ{bŽ*ç»aWÌ¡؉°#æ(ØŽ° U¶Æs¶Ä›6!lŒ9*6 ¬GƒÂ)1û<€ukcö€˜½` B?BB/Bt£ABÛV#¬Š%׬DX°aB!ˆ°a B¡aBB;BBk,¹`!ÖZšš ÌGh@¨G¨‹Ùjc¶&€„yþ˜­ ¡*fk¨D¨@˜‹Pް¡ }–"” ³b„"„BôY€vy¹9ÙYè,Í}h—méisPÓ‹àAƒT7Ú¥ ¦ Gp"8챤V€d[,© ÁŠ`Á63‚ …F‚Ût(Ôb-AƒB5‚ A‰ @Ç¬í²˜µ@Š A#ˆP…ËÀ ˜q„1„ã‚ó%Ö¾@8†ð9Âgÿ@ø4fY ð ÂÇ1K'Àß>BøáTyá=Ž"ü á¯A•?#ü á]lû„?"¼ƒð6ªü7Â[(|á „×^‹™—¼ŠðJ̼àe„? ð÷/¡ðE„ß!¼€ð[Tù ÖžÇÚsÏ¢ð„§žBxáרù+„ÿBá#üáhÌy‰ùEÌT ðÂÏc¦å"<‚ð0ÂC"<€p?ÚAAáÏîC¸á„Ã1„a´‹b_~еŸ Ü*?F8„ð#„»îD»;Ðà‡(¼á6„ ÜŠp ÂÍnŠ»nD¸!fì¸>fì¸.fì¸6fì¸áj„«®D¸áÂå1cÀeèóRôy ú¼á"t}!\€0„šç£Êþ˜1p:;ƒp6jž…^ÎDó3NG8 aÂ^„SöÄŒ“™Ýø†]èz'Â|ÃvìË6„­ø¾-h¾aÂF„ ë"§àPÖáûÖ" ÄŒÅkúc†3úbºv{c†Ózbj×®˜ÁFáj®ŠNX3œ°"f8`yL‡0³,¦w„‚1½`)Â’˜Žy&ÓÃùÎt",FXÓÃ1ÏtÄôp°3ím±DÚëÖXbÀB„6#4¡°aÂüX"œ›LªÔ£°¡6¦›PÓÑM9/¦ øcº@uL·   ¡2¦£«µa.B9BYLç(é2Jbº2€b„¢˜Ž¾¨_T€ÓÑæ!äÆt49ÙØ—,„Lì’»”Ž]JC˜ƒð"xRÜh‚š.ìpâûvÔLF°¡y‚Á‚šfvЈ`À~êñE‰:´Ó"$ hÔ¨¢Âš2¦]  ˆiWÈcÚÕ2)‚AŒš"ÔäPÈ"0Ä?8zc€Ç¿þøÈ>ÃÏ üàO?þ8¡Ûùwàzœ&ô:?~ø=àQÿ ø¯Ðö¨ÿøOÀïÿÈÿü”ßüoà·@ïM¨¿ü:ðkÀ¯¿ü²fóšçï_~øw {ð·À¿~êÏ> ü ðÓÀO? ükà_ÿ—úçêˆóqu†ó—€GÕ™Î_€ì1(ÿ\½ÞéT½Îùˆz­óaõ€ó!hyPç|ø~à#ªÍÎÕçÏT[÷©¶9ï¾ø0Ôc€Ã þ)ðO€ïþ1ð!àߥ<Íy§róånçoWîuÞ¦ÜçüÈo¾øfàƒÀ7ß|ðõÀ×)³œ×_£¸ÃyµâvçU€W_|ørÅ€ó2Å™ÎK×;/QÜè¼XqÐyÈ/>‡ó8ÏæJg1¥Î3ƒ3 Nì œvh_@¹Qî³íkÞwê¾Cû^ÙçO”(ööN=´'°;°3°ëÐÎÀýìù¤ŸÝï¯ì8´= Únؾm;÷ñvæÐv¦n;“»aÉvív~;§ÚØØzhK€liß2¸%ºE47ºåÍ-,ÙÂ(FÆ=¼ÅæhôïÝ¢Ö6ll l:´1°¡}`tpméšÀÀ¡5þÒÞ@ß¡Þ@Oiw «4X]º2°êÐÊÀŠÒe凖B¥ÁÀRÐ_RÚê ,.í,:Ôh+m ´‚|ais åPs ©tA ñЂÀüÒ†@= ž$k“ùdNK;К =!6¦&׿·½iûÀ&"¶¨íQ—˜äLbÓ¬Lm›•Ùh=Ýz‰•K°ýË¢’ýQX¶<8Ì0‡„ß6F ô×ÅBýœ‹.615Ä^Óµ/Ƹ›o¶×„š£ƒ´ì÷ åqZ& "BeØDjB¾U[·oõýU2¢{S÷Ž3>¢}VË&$0 ã ¬?†“ qjXú×p~M^IC‚Ú©féc\Í™üjÐÏQµw6$(J6P­lS²~eumƒ_™•Û0säÂ}ÛVÁcÕÖm>áµU!f;­û¨˜~¶nƒ:ýÙ.Ô‰ï ÕVoÚ6!ÜöÍVÿ±Äü«;ðoN–Õ«„?o–ÞDÈØòÝNÖ‘­d~Î%‘äò é&gAéZr3ù!¹‹DÉÏɯÈKÿË?aŸAc»Å뉊û‘=!ãÇÆGÇ~<"ÖL“€š^ÄOIƵãï {oìÀ¸vlD’H‚­šý HÿÎ?ÆVÓúx1­³çA9A°øPzÓØOÇî˜Ñ&ÒB:I€,!KIˆ´‘VàvÒA’•d5é"=¤—ô‘~²† µ¯SH„¬'€ûÉF²‰l&[ †ÛÈv²ÊÛâ¬ï"»É²/ާ’½PÞ Ï=Bé4r:DþŒI&ÏBôû!ê4曄çZx®™ŒøNˆíDdOƒˆÍŒÃŽxÆóL!Nm;Aó<˜3§Ù ó4á‹jOøš/:&:¢)ŽðÀ¤djÜ3­PozÌfFð:A2³õÄÈN/ßòµ-? ·ßO:'Ö&Jw§ü#rˆüJøœªO”î&?!?…\0L“{É}ägdd²~Ô¦Úc‚dBçäòûÉÂ*x„<*Ìÿ/ÈQAö”ŽÄ[‰·Ü/”#O@z’z• ý5™.ƒ:;[ŸEFŸ¥PhùK²˜Ü,&!‹QrYYå9*¸tvdg©õDaâ³TFCfŽ«\cóvØÚ€8æ³T%šËtÕLNï(“Ÿ_fÍY½jåÊ•:_™%G3£c tð"ûwö˜—ú6õ‰!ù¦¬`–=N˜X½›¡çù¹ L*?³¾€‰¥âß² ƒËjáÅì[ìñ6YFZz ó”Q¬³£¢D‹MÓËûìZÑb¶&ÁáÉJÞ’`щEV©J*ÃC´æ‹+5êÌß5“²; ½\”–üeˆ»Ã6Ǧ‘ëíFúw׎À=,Ê…Ýp-›ÃãÝžºªÒ 7"1$ÙŒšdwrÁâ»÷W‡ç¥ØødµZáö¸ôÉI‰y]—­n½íÆ¡µr¨èßm!Dd€»‰‰xÈj·# ­ô'šíÚ¬QËìézÉÖÌ ¬BÍÓù/ŒÒØè¨¦ÎþQD×+s|™¡ ?T{bô.aÑ V MnÈ U"‘!©²ë¢‡.;¦4hÄâ#Ò´äϺ}Ci”oˆ„/¼éÂþ3zê³ ì­M7\unß\‰ÆªãnsÙ º/^WÖÕàýò½ì¶µÛöÐu°n;Œ(“ì£ãñórƒQf4ʼ•Ee!ùÞtÁÀË>Lï5~Èe`9e‰e£LÎQ:sÚ§ó ´ûÎ;z”ÒlAÛtÙ‡‘ô^Þøa„ÿÊP©½or´œ+ß8³01~W¾‰ÛΊ¥ |,\Ŭ1BVSjdc×Ö3whY-;þ•^-‘α²Z¥\ÄI“A9ö”Cf°˜•c·Û$F«E¯’&X´4ZÃø1ñ‹0þrr½0þ"¢(WªòÔ¹y¹Ù–³Õ“”êIµ˜•Šâ^³:)UArr­•2;ÏS®u¦õ:ÄÓ…y²‘@ºü™* )èûùš™Q°4=¥ÄWÇdJ1IÅ*Fnä-§N ³Ç_唇ÉhK”1÷°Ã¬Üà²XU“ø’Ö&ÍIN”\#b×òž,ç€Õ)3Ã!ÁƒÛùåÙb¹v—LÂíýòüIù)¼Â’î<^Ä>g÷Z| ÁHŠšáä­Ä³Õï&•Yƒ3SêU;¤¥}ƒ”deªÞ‚J¥5¥×: Z3c¬ñ°Í×<^4§ ŸÆÊôÝ iŒ × É6››ãÖpÒ™ñ1™õÎ\PÅ )˜n&n¿<=©/É¡¼Ke5[¬D“ ‘ncdz‡Éi–ýDgŸÅ%¿Feµš•¬4!A+ëcä‰N³)‚W“ÂÛSY¥§}q‡×ÛÚ¶0åøÃÓããäÇR½Þ9‹e0cl“Ð,OÔ!Ê'f’CB4Vÿ±¾…¤ÂSEìŒ,¦ïO§¿Ý”žm!¿ )dXo‡æ{"ú~)Õ;‘žI§eÏxšÄì1•IE¢Ž¤²•ûïÛÑueoAR9”¶õ\Õ“ORùòy‹N]^™ªµ•¯¨iÛ½¼Ò£cª=xðÊ­ 9+Ï_¶à¶[l®Í ”¬˜—Z>ußN_éòy)å«w¶“Žnþø1î ì('É"»'rdµßÆëÓdò9ðcЧ½g0ëå2™fÎûæ^ÍŒ%/¤Ì)° „lÂX´˜7 ²´÷"qKóœ÷#'Úúâ¶“£wŸ¸œ´Ð;ÇëÅ,Êr ™óûÏ_zU#«4¹¬f^/f.Éà?M©±g·”Øcù¹úlýeíû[ªlœ+3¼bQeêØetž98]é÷76•f»Ì bÊR¦,o­+ÓT5´$·ˆ[¦¾zÓpT¸A…D +ȇ €sïÏ®ïëK˜ƒ°jLû<ò fÓÖ?ZûŽÎ¸}ÅCÎ}ƒ(¾)¤ˆf£A"¯aàP®åìF±4¯2ÿ”p“®E¡VÂl”Šó+ Ö¡ÞÒpѼ—ïì£-AĤ$Ô¯X_V±¤$‰34¬ˆ”Ö.+µÈ¤V™R&ÁƒÍ´&ù[*O9°dlÔÐQn­lœ)äΑªd'SIœÒâÏp•µå¸[ª3¼5ÁBw]j|–¹(Ìr9¹û$³ìÍKJ²yEŽ$0.Aã1~î/nòØ4¢¤„<¯Œ÷5ò-òø4à,Ðïˆt:ñvóhúv+ÌþÅÆÏ#'Qœ>]Âl Gò=&‡‹Ê¤›Yø®ÉH'¦ñ/¬,Ú¸ºùkã_·js%:û"Äéø3SAv–›*›¿1ÈéµAÈQ­£ž‡¸Ò»ÿÞ¯F–Þ•~9±h-¬ž³¤ÒÓA©²¢oJÛ/Ê.ô‹DS¸ (¾Ú,„M­·Ñ7IÓߎHOÈ4ßáë÷|rYpçÕ+Ãç3låK…R(ã'Ƽ¶ÒŠî…ežDS^kie-±[›®»ä´U%ÙÁÁަë.>}UINppY~{‰Ãר½q{i~{©Ã×Ô½iÜÝ>»‚{ÆŸ'ã3'YYjWQ±J]¤.²¨ÍB‡Ÿì3«Š‹\"iîçÞ&³Ú‹m‰meŸM°0ýôœ£9Zø®­H#,´¤ïîé‡ú^8§[HË>;1’¸îà1-¢s¦_™§-¶x„¡n2 ~Ĭ¦ªBS€~-·šáHd޳Âé ËMS §gËòÝ ®¢9Å›ÂÍ:WN¤®himž+=»ü²+.ˆÌKä}Icí2]tðø‹¾ûªd‡\Íó2 —îló-(L®ˆXz{C}~çº-›po³ÁNv7IÓéì‰ÉÄž¬a,~­?«)1Y—fŸ#1§4šq{Nî霣“¿Ö8BTߢNc;œ…^#'¨MÛËS[yêúüµÙÕ#q«e2½ÙaH -™¯k›Ú»‡á¢ß».suSÇÛa–H¸DfoK„os.^<¶ñ«[ö¶ô–²¸mK$4ÊÇGÙ¿BÄNvæ=Ið8™K á^á3Í…ŸÿÃÞ—À7U¥}ß{³oÝÒ¦é~›îmÚÞî--Ð-m‚Ýèªi’¶4 IJ[uœR*EÅ•m@ÄmDDteuÄqw\Ç×QQYÞçœ{Ó&¥ 3óú~3߯}lrÖÿ³žçœ“ ‘˜_¥;–J A¹€'+ŸFÿ˜J¤¥Sr^zöÛåQãŸvǹ$þ;X8œ£‚@wÌ&(—³•OK§´¥Ge¿m‹ç=zw¡|î.ü‘Karò®/ÈŸ eyê¯ZL$EÄ-øs¹(›ˆJ)Š&’£‹²'7•G ‰èd>/¾š³Eyúfõ¢Ü-rÏí£àTï¥![Å{Ý!ñ±7v †:}³M½Hž»Å6Ç{‹`ó#~Â]"¸}G5r‡àá;å½CPwQ±šøp^ˆ$£LŸR\“—£›SRØQJŠƒ£”¡‘òÈò4meFXJ:]Uš)¡Þ—)²yJmRDJYm|â,d”X2…/ðx1ÿ¬)I;©Í˜–›ŸZ93Ýî¥P¢ˆLXÀ·b›ÉäÁ¥RC ³$»É~BCd‘½å2Žî9ëÓ ×¦ ÑwѽP“¶Hþ‡ÿ€‚<‘‘ˆ”d£iP€ãÏZ°ñÂ'-g½MX¸Ö†bä°Åø‡º ÛÀx_¸Pd̾þŠ*’š¬K«ÊÕˆä”H*‘¤WjÊæN¡ƒ5E•“Z ÔÃŒ6©,#NRVÖœÆ_žV›+‰ (Ì—È„aêp±4@&TçÖå¥Î™Ý4%1"½P§N+Ž ŒÄŸön¤ŽðÛÁn3ÙçGåLyffIxêÔ¢Ø0m—ù`—`"ž˜Ë~VH›ˆ"’¼«\! ‰…m \s—hÌŽÀ~ÌÂ~æ£Ã6Ù‰bÆl¾Ÿó%³û Š[Ü‚åñƒ’J:Êן2cù¼kúo(¬(¬cT…å…u9*þ4íÜ™õÅ»¥bÅ"½}aaiþtcfQiA“å¦Åä)žäO'&k°gñyùrEž"=ƒ1p:ÎÏË â…¢¬-É‹¸Ãª3¤§èž1jýÂ9Î “œÛ¨èž±z{ê]èÔ›ìwèÄ1Ç£˜”xE) Ž PðÉÕTœ&(L. •(á'çVe„Þ˜¤©„ÐGMN‹ËMŽVŠù-I³fµ”'˂է`i€óai|!“£ ˜ªMRÇò³ “Êgå•'¦Ef•L­…8Ç/ÀVŒK Œ &"¥©á‘úw¤¤twÚ¢àÈÀÄ(AX¬3Ì/ƱÍà0g²<ïâI“î¶]tÆèéu¼ãµ N )¾P,IcÃËòÒ$¾« &µG`Zvn„*¨çø!á‘¡òÔòV­_f@êÏȤ•b1ô] {Zè;•ÍÍO ÒMä™dy`h>Ag*"J¦¬K ­L¥Hו,н3uoÒñ$*)}}„¯ÃG?!Ï÷„…ÐSÖÙ%Òu¶’EI±wÚ’"Ò×ÛüP.t¾ú/½§É¼ÜÂ1‡Ë.žX&’¨éŒèÜbMaY&±Ú45~RVœHÌG= ùÉ“Ê5¹eyšø)——Ff§ÆˆEü÷Dê¥\!OÓ¨bÔhˆvZaœ$ H$EF*ƒB³’Ãc¡'7%µ&?F(Uˆ…°övŸûžºZð" îélV “©dQ ªT~F¬,Œòƒ“ÍÆÖÕøÞ_μ;>ZhÞ öŸV‹&î´™éê›U½¤ñ¿´ ]qÂT‘|êž\¥QòyïÀA\ MˆLPŠÒ iPz|L¢RDöx?)Bd’€Œ0÷ÑXkP‡ïã¥3/àR‚Cd«ò\PyÌ8PùÒu]LI”Qa¡p£VR•&2R£»2…$ =.‚ Åb(ªèP)Y$’¡»œLĉä@*Ÿž*£é8q@€$8L!×$ÄBQ º’„ž|”*¥ÂˆüÍ0Bò¾xÔ7èÓßçA‘â÷m¾í£ß]àNúñ¹*ª„ŠeÁÒ³W(ä”@,”‘›dT´04<qò­“AgÞBvß%ˆ(;$ò­ýc-®©R¯AfV‡„„CfÖ“²pM8“À?›JJ‚"¡QîÓÈ[æ(ò*ÍÛB€¦ORÇù›ïQB \¨ÁÞ\H#®aŸÒáªq|¼*M#"„Aª¤Ç©áñ¢@ž2J)]2ÏuþQN.É}>77¤$8//èù\¤Kš;ÉHÖ/. Þµ`_£ÈuŽhÃÊØÉqCíJÝZ°Z<â%#Ž´ºýôòfHbÄüMÿ¬q2Ô¤óôdýãõÿ .r¼‘_(¹9üçdú͹ð*ð`yÌ8J‘l Gü3ºjÏÏ¡\Ì;GF£¨f£Áã-8®þor±t"ÿ×dã:ÄD¤â˜ÆõÓD-Ž –̃ÿ*·VA6ÛÍì>/z²¸˜Ë†r?Ž¡NEÈ7ýÐjÙY{QYL–IÐ¥es ‹5^Œºqœ;±î¬¼óWgclêÇ–f-ãñ¶w´7g˜¸¼Ž2€ÛsrQá›ÃØ®v.w°(®näòµg+Ö•®ËáõòXy¸lü¸ÎkéÑA{I™€Ý1ÌØ¦ngb×'ËW;Âg¬l†íÅv2áõ4žÍz9M­x¥ÙðšbWþù¶GsØ]'ƧùEðøè¬ ÿªm}×»óÓÜÞíÁž3ùí¡c5Ý1ÇÊUêHVö$áÍ•®‘S‰ïËvœGŒÔ”=£_T±ùÀÁ½²Z±å¼^ØüdÆ{œ•Ë-,iÃÙÿÂ1Êfq;ç™Qtï ±úœ8ºp¾³rvFY]ó¥…ÓÁ{úðZÙ?ªµØ3F\6Þ³×Ø<7v%¤ŽÉ œ§{ñiʽ¼j„6d¡NáíËæ0çÉiÜêÍ£'¯4ÿÌît‰»=£Î‹AÇŒDóhcýäöäbãv‘Ñè¾ØçÊ ïrÈsM#+ÇísNaýÍF…ãÅfl;çw-ÖÙÅí>Þs{fêäüìc6®œÜYˆåàÀgr#ÖÓ)Fbt—›Ï~_ŒXȈuGv³r¹ÞÌ­Uw·cY}÷L+>©»qlr2^Ø·PnñßçÁÛi>62ûÜ|×Ã%ã£7ïèñ³›vLvóÚ~ìl¾1XÇèí•kô 6ºjFw"¯µ„÷æ†nhÞºÅ'BœønfÃñÖå³Ã²R·cY,ÜNÕ3âKß\Âú0›ó¸¯Ûˆ ÞuíK—nUßžÕÒw§ñéQKôb;vÿ‹~ôî=øæÉZÆâ#¿"ž£vY#L>{‡ç"ù˜Íüf¬wÇ›ä—ÅÙÓØb\ïÔmÇ{„w—ñ½»y÷‰ñrŠÿ,7ά¯Ú9½Çßsð¨kD{7ŽR;FgWÑù·â5¼û›žÐáÞF¢j3a·lÆ-h£!‹6CÏ ¨UCk5´¤Àˆ®?{j&Þ‡ô0® ïq,F3¼6@}6Îq5ë¨vŒo,4WGÌÂL¶.´dÑœš)nºÛhï§M=àRVnd>;Ùe]\V7²¨ÅØM÷8@ì„·õJîq€B‹‘JFÐÍòBÁcê2º@0‹+k$ &yy‚>Hÿ`dü¬Ü<®'“íñsÇe4[º®…HìÚ‘íË;Q³Éf°[-SªÑÞ¤k]‡§Ëãqº'eg›&wV·wfLÈöô;.£³«?ÛØñ††ÂH[ÉèîpØÁð0j”™»Çé´Y!€P_=ÛÑ–ë§{ ”<(hQ32ˆ \ì±hi³Õí„@fëtY¡×C,ðnwZ\ÝVàÚû±VÞ°“Aü8\ÞBâ =_wˆsÉ£Ea¹æjÑ/ðSo—ÕÔå#Y/0µÚM¶X£Ò;ì1©Ö4vyø „‹IË®&ˆyð¿Û㲚ØÀô2ÀñèÅ*ÅHµX(¥¸Ð 2;zí6‡Ñìo=#k*ˆ0P܇ ='d³©‰ÆtYlN‹B~‚f‡#‡Xñzé²¶[=(O)ZAäZ5HdÎÔZºÝèYö‘ŒáuB* {V¯u¡Õi1[YWg6ªeÃÈy\nI÷â°ÀkÁŒŸ ÇKb/q#êЈ—‘™8@'dXS6HpØÜþé™Ò/a*MÈ9n¼”@o0fA`ƒeÌZºÃÉ-X 3²1Ø < ÓiG;$=;2Š'loœ]ºH £Ûí0Y(>`Aê²{Œl^µÚÀ2©ÑO[º…ËØ/§a‰Ì8+²~wη¨Ù'Ü´\¸!é½Ý6+Ä)Ëa¹Ø 8àE„4Ô¢œní@ïlg(äî  Û{Ðâu£F.J@ÃlPÜmA©Úá´²™õ‚¢² X²‹†³4¢·ËÑ}Ñ2èqÙA 0; —bYXLo€Æ1¿ÙŠÞ$6Ä!-¶øl¼v‡-6©[¹eÌF ×åîBûB»Åoå}u!ön“\4²]Ìh½éutKcMëÌŠfmh¡›šgªuÕtJE ÔS´ôLC«¾±­•†Í ­³éƺ¢a6}™¡¡ZKëf55ëZZèÆfÚPßTgÐA›¡¡ª®­ÚÐPKW¼†FØß °´µ‘F 9(ƒ®Õëš«ôP­¨4ÔZgkéCk¬Ð º©¢¹ÕPÕVWÑL7µ575¶è€}5À6jš‹®^×Ð [o´ÑºP¡[ôuu˜UEHߌå«jlšÝl¨Õ·Òúƺj4Vê@²ŠÊ:Ë ”ªª«0ÔkéêŠúŠZžÕ(Íx'ÝL½7¿ ø¯ªÕÐØ€Ô¨jlhm†ª´ln™:ÓТÓÒ͆dšæF€Gæ„æ5èXdjÚÏ#0ÕÛZt£²Të*ê«Möœ¥˜x<0ñxàŸ°íÄã_ïñ€ÿN<"øï|DÀzoâ1ÁÄc‚‰Ç Æfó‰Gþ ¼Ö™x\0ñ¸`âqÁÜãX›è{þ羃_5±Œï‡â¾µO©ð^‡¿ý±ŸjÞj¹œ„1¤çRÇ+xüÞKˆÆSòK„ÇϺÔñÁÁxüÝ—:^©„ñðN ÿ‹ÇóáW.!È "ŒŒ$h2–È${ˆbr1a K´’+ˆyäJÂF®"zyÓˆAž‹¸ n‡ywŽÁÚ4V`•Ö4Àj¬ù€Õ X}€µ°n„±Á‹,÷Á ¬Àʬ)€5°®,+`õÖ`­¬ €pÌ{h Ö>X€•X€UX­€5°l€u`-¬[k3 lƒyøcQ}°¢+°J«°æV`õÖµ€u `m¬‡á1˜÷g,Þ<¬Àʬi€5 °€õ[Àº°~X÷Öã€uŽÃ¼×Q¼‹%¤Xj04Ý>Y$B£X5_, ÅB±¸X?¼j•µ ýWN¡„Jçæê#s„N½~ÕªUܰáa<·¯zw`@È'…|gyù0¡¸œ zWLQb~y9j&HÉç¿ËãSbASÓF±”Ëæï5–O/o.¿eà¶a á$BR"âóùeÕCÃkº.*Šf™«‡‡‡q±¸zhhØ)æZ‡†¿ºtŠ’pÒùŠ'AâIä¤DA3LSÓü¦Ú@O£‡Ê‡ÊER$D‚„R)‘„HD‘!Þ§úDRR$Ÿ=ÐQ^ 4ŒUCÝb‰‰ËHLS*"Dbm®FSYÝ‚ ‚Yó±L"!!W—3bÆ#¥𦑏ÄÀÉ'ù‚¯ø|J*a7J¤4€V3©M©MMM«ô« A×-£‡hN(vÊD¤ äõXìXFŠ—ï5Ñ•@HQPõßXÆ#e^ý$–a‰e Rˆ$fÄMHb$¢Ásb¹ -“r I’Ô”ÊÊÁÁ¡•±ˆ‹Ë*+O õ‰e¤xDl¯àh„äòöÊÊJWÊðÜ¡>ÙhÏþÇ/$;â%bQY%Ïê“ó(¹Ð+='¾ÄPrZò@R„þ!ê²B[™ ýµí^y`ͳêgÕ)G·Ïß ?ûûR!%ñˆ©pÄ4û0$^€#6,‰îè8x –(HI Ýé^cR³tA$“©Ým‚ŸƒâZ¬É´oß¾ƒûä>}½ ,ñʱ9çꉀEYû|gq´|eŸB@*„jµšè'öî%¤@xJ ¢⎎£G‡ÙÝÀ»W ½’2Ûì\9ÕÍ–›P¹ÂÕm×ÒUý.›–®µ8âW¼º,PFÏd´tÑc¿Ôq˜/‰yÃoÌxeňYÍ ÆÜ*”¤/Ó/ûAAЍƒ1CÐ4@‘dŽŒ‘<*R@0F¡4CÎ,¢HþÆf:£õi‰¾+v š(ÃÔˆï |“G÷Ì)ˆ˜x0~èö9/¸v¼ùÃóUbùki[õÇóþ‘¸qPÝÆ ò÷1ƒ¼û7ò(’¢”y âÎ~Û½|arp-x'£‘‡`z±˜¼6¾PIµµä(™`T+¥3î.«½Óã°ç1¨Q¤5[ÌÝ»9'–‰F-Reظ_…ȉgâP?O©íoµv[2[<Æn'ÝTUÁĆ+r ™¦(§¨ 8?T‹}ªÌ’¿Šd F†úeJ~}cSsN “ÄVcíUV'z4ZÝ¢£u- “*‹uU™yLE~fQN~~N“Àj=®F-ìffÔøZ˜¼A2€v)5§˜{®Î{}ʯ?üák¼=Wi¿ÛfØž¾ôäkZÛÙº7ßxÿ©3ýÔзè±}Õ;/Kè(:P}_ñrçi¥wœmÛ1l_w_ÔËßüŽ~óÅN·&ÍþÛ[Ú—¬uþíæwþ™æà‡7ô¨>xv÷G9Ë‚>YñIñ+ï?–sóß,Xw÷°óúýž•ÑÓ{¦önùÛãOþ,|°àšý!ÝÏUm’ÿáͺæÏ^àlQ;®štâÐ}ýá%gÒ‰÷>uô–ß<ðvß®¢ŒRí—K&GÙËM;-·½¶õ²¿=ùÕãî˜'ëþñÂëÍíGŽ=÷ýÊ뤙7>Ñþésk–$m{q©úûËäýmöß~'™Iñ`m$%`& à«ø¡Kc>¥ó¿]ü­Úsëëõ7~{Ïo®¾ÇPL_ͨBòO½Ö\㔞,ÿyñÏ;2ÞW°#iEâøõÌeŒacíFݲ*î™´ÉeóEçB+jÍæ¾àÎq#ò"v"De af Ű0Iòë˜iŒÞ[g¨eeƒÞÞÞñX\Aö0J$o_ÎH½<ñ˜ÉCQ"ÚñÓiÃâó#]?sxúÁ“?çϽâèŒõÆê>º¶ípÌÝúDÿNóñ¤õœ‹¨^çN}ëë“{:–ÖÜôý6}"/¥DÚÓ»2]gLûq__"ñýñ­y{ÿ¼öú] þN·mOr¾°Þ”îZö[qDÇ­oß{6ú•ï÷~xlÍÃ?ß[“¡<øwÑ‚MÇž-Šxó*Wâ‹k­¶©J{Ç¡•y7PK~xÅW³.ÿb—‘¹ÛôÌa›² ŽôæNï[Z°äï½}õXV^çÊÒeOQ{>®»³~Ï¤Ú”Í ü&~Šå««¦øÈúÒö?l‰¯T=ôEàÇO_ó»µGKk_Ü¥Ëø,¡‚’Æ>ñIcû?YqêÊ%MŸœÃil¿¯ÕdÆ®ùU’E*“Ì.ú8ß~³…n±vâ/€cÑ7Ârp6+bŠsrr .›Vϯ"×Ï»@ÿ/f£áëOÜ'ºiÝ@Øéäù§]ÃÚŸþ±yõð5»6™·"{R^VìÍ}?]½-nÜyå‘È'y‡k>;°ö‡Ÿù1ß\+=§±oú¦sòõ‡©qßño«0}þþŸÂVžT®+x»ØÙê(ýüA„1<ýÔMÌZù‘ÅÏýà¾]Õûâõ»o{V|-}2öÞ‚¯=󮇸캗޼ù³}goøéÁùÓ÷<÷Pûê?Ú¾ê¡Ìx¹õç‚ן_tËG±ç>_´ðÈïÄ‹=ïM×ÿåkâ¾n³¨àÃÙŠ3WÿþÐGsÞ¿ö»ëãnÜòÁPøÓ'oˆ!Ÿ=£ßª¼%ou¼>÷Ô3‰w<Õrx©=mî’/‹íßîþ\)ûÌ›À"W³é& ¥›‘¹NLެTžOº:r¢}èØü’OÏu>sùK‡vß¿kŸr ÓŒºƒù‹î®etcwš|&UÊŒÜ<†ÉÉÍ03ùícf~I{~f~n^qfq^an¦¹¸ §Ã˜›[ßaòKz»ùÃ&Á˃÷…ivvß{¸‡ºýÂ)pÜ åpºq„p8†(†Fñ;½d2E™L1NFŸØÆÀiÅ'ê~‘7 ^„…‡‘#ÁábŽO1Ęå̤HB¨Š{cæ3M‡ïšÞ÷êÉSgžßóÊÞ¯Œšq²åµVðÊþ#Ÿ¿wzíÜÛç§îè”ï®ë~²ãþ7vFµ%ìšœÐWÑýЩ¯‰9·­½.ú¨äöã뢫™m÷¨žýSíÜï2ò¯ßpÓ¬¢} ÑÔzþ¯ƒAÛ ¾zHsè¦Ä-K®'%úƒŽ˜S²ÎÍäÕ?m_º1÷³Gwd7͸B¸=lå¡Ó.·üýW&¦ß¡Ûš»tÊSfzVœÝôìuŠÃ¦Ș“3·dÁ÷Þ=¼ðŽTÇ×ûút.üh{Ã’­‘µ7®¹§{¯=åà©”¸C'ém²í_¿ [wÛ{ î´.ÝTøj7}öÚWÎí{|u¡äìäЧׄnÛ»ìè—ƒOßß–X¥Þ©¿¶oÙñ_ºsjÄk¡+>¾aCWâpWé¶g’?Ç×™ÎüþÖ°ú¼3æ7¾:í‰âÏe½µ}ÞÝU Ÿë;¶}÷›–Ú–»îûôžŸ7¼y¢ä´ù¹î)â¯^ºýÁ'7ÿéªcw̸ûÊYGBjÛ_ŠÿòtÙþÙÙSÌ÷9æ7MÝU½ªq£ìú§®™õý³ËoüaÍþC+8jÿ¶7ë¶“Û¿˜éþ|áÞOîX|hxÿÙÒïr ™q,â/»¿»íðòèoE-qïxy®fê¤Yêw†¿èÜoØšýfÒõ“süóüê›cž¼Y¾xpÊ—ûÿš¹‰OݨÿñË·¨c¼»`Á&ð%» Hª®|œû£Çaçát*•Ü’¼âÖo´f2BŃh̉`Âý%#Á a˜ÁæÍÄѼÙìp@ò„еvXMF…®èñt9\VO?JîL“Ïäåää1%Üssp5AÕÿwgè_Êï6Ù¶¿ó†þ–ô«fEümÏ{ïX;=¡éÁÞR7$~ñâÖëô0tðg¢WZo3ÜUyËCk.g’_'þýª=Ÿ¯þÀ_óÕŠ£qGò—ßùÍ?:£µ§¯úx8æÓ6oz:¡åð ?éŽIŽÿæÇ®äßõãÛ­¯¦¾YÓòð²ã¦Öd¥<°¬±­YþOûó‚U«ûòog3wþt͉Õ;þ¿úšS/)¿ïjén~T·jƒž˜VÛœ’Öqïê^.™v×C[ƒkC%ƒ†N¶õ%×Å4‰¯%‚˜š“»ÞN¨Ù½?³uÃcû*rz®§té­›ŒÔÎÅöÓ?¬„|AsYë¹ûž¡eÞü~?Xd+8’q Þ|òù¸§K”¾cù|ˆ¿eLPÂí a$j!˜%kØÜ¼d³ä†Ð€ç—ÏHYýa’òtúߤ-·ÏþàîM¦»¿zxõ?¨Ú4mã=Ö¹gýC¤Ì²0Mì¦```ÚXµ±bÙÔK?t£o£TŽ7„VŸ AÏÔüO1çÖD¶ÆñŠÒ‘nÒÃ$R¤j¤¥Gz“""%€HŠ H[zMT@¥‰°ô" È"MDY:ŠtÛ ¸ ÜÕ{ïî‡ûì|;3Ï9gæÌûžßùŸ÷4Aþ﬉wžCý[«q=Lk¦´ë­Öär#³ï wë“•A}/YyÒ±u7ÇWC˜oÆzÚW›Ÿð±eŒ©MšÕ–˜g›à&‹,®Å¬Æô,(‘-M6ÄÓP¶ßЙ\1a1,Jšž¹qá¶ùmò*•tùl¢¸€÷ÇOÓ˜ (ýæ¡Iï:Nƒœ8wŸ”j¢B¶‹T›1ܽµ*{z Ÿêä!üCìŒ?LY‡¶}Î[ùk ËX ]ÜÊËjŽyƒ˜Ð6Y‰ó·çëBhÁ&>üK@g-ÆÉÚŠŒƒ†•¡ï5kúûS?;›WHIÏ|ˆˆì26}—ãìQ¬ 7°Øx3È^lùf–Ø ªˆ}‡2'/~…ö©dísõŠ7B*§nßñ•­6h»$È|ÜŸö*ö’¥–:k]EE©¾K{â+6›Ë8¿C0Ÿ‡´ç ð÷¨ÏJÌÖ®ëtI Á±zÇÅu„l,çL— F3r:/ÖãD|©Ž,ùó7fá›EÎ>*» Mô·{èEd)h¼§½Â|ñóu¸Gù—1ãöXÁçúîkÌŽ`e©‹øêiþ7•¥1g)NCŠ“Kó1E„T?È«¤k,~Òð;‡½V±Â„åðNþÁyÃŽÌ%äø&™ÓÅhÚv·ö·^s…iÝ0±¯ mVÖCú\Ä¡mé\Uè9v÷–[Ÿü¡ Oiÿ úvQ@þg€‹ú¿LÅpøæbÅ!÷Œ„ y8 «ð r»E°SüÇ ü=;À;ì“ØAò¹¢•m¦cÐûC^÷ðLú'jV™óç!¸ÄÝg-îUSÉC(5¡­t<#'ÝŸ0ѮȷdP•¶+¼ c…!ú£é¯]M¶ò(ÉEfϺžïË2)§‘l-yuWâAuÉËT‹N[嬳ÿ;8ê8³ôLÑa£çUè¡ÇPr¿"×µgžkŠÖDöu­šqyÇb/GYLÁQª_í§­©ÑCô/¬ó‘b3ô –€†dååS–L¼ú¦¢7ƒ|Æ™«ç‡ÕÃ^—Gr½R)‹E¿‹6 ‡¬¥-¦”¤Ș·U©|÷W+—•—$É_íËÁJ¾70Mä—nUðr 5©Éf¼T0üÙz yäM›•Tclòµº&~_aNÑG]"¢òÂé gäž_)KzpL°ð®ó‚ï… QdŽMÔ¤0ºŸ_Wõ¸ÒLUˆ|¥7ÈJú…à”7šÑX+ b 4QW ÆÛ 7±UÔs œÓQ 2Î "ë8«5®hN7·úûÌ5je´-·3»±  ‹âƬòJ>”:O6§á‚ugb…,¢…!.Ø·×í16åÒá/Ͳ­DE[ôl—ŒW;iØ<¡ý˜Z¯m _]Ú7eÓk Ãg.É‚¶MÉT1” ]Å1šk°žZZ§EðHÝcç"‰³?Àß><¨KŽîU`SÐñЀLv"ÔA§ÿ«ßAù âñ‘RÃÔf¥4˜˜+| ëŒ>X~ƒÛΪ!AŸ ‰ü[›>$¿%y-ÉY÷D‰ c‡ïbîüÌ¡#Ààæ sÿ¥}_—·só|¸4— à÷ JàÂÕ?º“±Ëü/™µóµéÉÜ<í|¼/C]}=µ½ÀÀ 87HärÙÍ=±ÙÍ=ù–«H*]þ=‹Êi/— ÊÇý#!æ²™Ÿ>~6íòuÈ¢M=2á”H é ¤Khv²Jªlµúôz†}iP}GÓ©Ô¨}÷ÖšÛ°C£€l~Ú)ˆî±µSˆTOég÷)•CP±¬·Ê\ù•ÜÉ “³Žʘ Á5–‚ß°ëÏŽƒµÄ[®3ÕÞ¾KI—µèºí M!ˆ«Š»›#x©Ý¼,ÓS§ÃÖ›â×´$F?)õÔË.{ ?xS"²Ø3²ÆP’!š–®Ï L»z8z·Î9¹Ò&Õm•û©@ó„¦åÉýoÊ_ ³EkšËÃ/‰@BËÖE¶F%ùÜÒË-¢]½.Vù¶ªQR‰‹ªàUYôi›*ôßOć»È¢YèÿFMÜéV+eÙÊí —9özmk•˜)2ñK~ZÏÚáô”Õ¡ìk*TT½Te~¼¬ vv•+¿>á¢h;ý”AtiÔIz!mƒh:$jÕ[¬¥åSëê0e`y{@bmeYùªš<²OúnÞÌ ØÖIá-ú¨-ˆ}Ÿ»Õè^¥›69ï‡,ÌÌäÔý:X!èê÷¶dûSÌ<-vÎM©ä°H¡76æçé¨Ü›cj`؈5 bŽÀùƒ–OÓ”©~¼ÓuÝLŒÊ2»dj £Ù„èÈò·¢Áê¸Ìk®÷ô¼ÐºÌBdô OQ à)ŠÁdd.åŸ×·÷ƒ#ÜãÉçw#¦&‡ÑŒ¼îb¿D c^e÷+RÀHS[¹Ã-Í2Ól¶3®rw:Ã>HòÝ€ã*t0Sà,A+úÃ÷³ßÿ}ˆx+ô=ûìÞ×v|b3ž Ä·Ä€Šê¸»Í‹(¡ŽâPƒ­$4ieöZ,2NËxè>†g€»k‰ ®B`â4Bu‰Úzf5nøD³‰hC(¥x¯ºlZù_Ô/à‡õÁæÆk—;Nót[òp±ßw†<ˆþœÍh™ÔÎåì"UpØlÓi›|`’Áþ*Îsg Ê¾ñ¶8¬¶i¤qÝî 3‡-ÄõÍ•+u= —^ÃOø»8÷*×Äu¾ŸøIÄÁlÉúyŽ¥÷av—ú ¹0Vê\Ë÷‰ü‰=)ñåÁò¨&‰TmEJÎÂHÉZ9l. £ð#:(Ú•ùÒG±E½ZÕïyzä´ar§'²ê Z%âÁ¢¤å‰Ðþ;¢‚áÁl¤SGvM3îâ?Ž´°I4ÀyÐ$i÷#†d¤Î÷®PÂw7Žå`²pØÎaùEú³©!6—ÕGÝWMG`4½Pˆª?I¦[‘ú­%¶í*ÕÉ©d·P×ÊÍO k=#ëº÷Lp Ÿ%•ßfPè6ËÁG{7ð4:8I?\Eð`Äag¶p¶«pw[éËåZã52jäâ<•™dB)˜SuŽõ_k.o%X,@¬‚Ç´~z²Qg§(„Ì»¥Á5¤ñB}\Àj«ïf("»·¾Fºåè,Ž%Ê#EZ&Ó#4xÐ#ÍÈèå¹s¿èÍãl†A A‘Gý¾Sxmv'¸ ô¶‰œAï~ë,¾úˆÑ/ùþP9õt„Þ©EŠCů\kY^@É‘a]å 5)0èbÒF(aÙ-z&Ñ_™tKnÏÌë> stream Microsoft® Word for Microsoft 365 Thomas Willhalm Microsoft® Word for Microsoft 3652021-12-30T16:10:37+01:002021-12-30T16:10:37+01:00 uuid:3E583F46-1127-4712-BF20-CB935514EA97uuid:3E583F46-1127-4712-BF20-CB935514EA97 endstream endobj 86 0 obj <> endobj 87 0 obj <<463F583E27111247BF20CB935514EA97>] /Filter/FlateDecode/Length 250>> stream xœ5йNBAÅñ¹p‘Ëu\qWxmLL4Áݲ·qÃÂg1Fã ‰•¾ ¡±°3¾Îœ?L1¿|“œ|'cŒ=ágïÐÇ |‹Ø¡H=‰ô‰È4ážE¶%rû0ù/Q8‚ $àTàW„?¢õ*"â¤x;sx¾#^:·ílϚن؅=Ø‚<Œu/ߎ'b0IHA2… €",ص•ãñÚI¡30Ó0 e¨Bæ`a –¡+° 5XƒuØ€M×óAŸ\½ѧèôþ͇¸{mÛâ>Ý@ô¯Œù%¿*< endstream endobj xref 0 88 0000000027 65535 f 0000000017 00000 n 0000000166 00000 n 0000000229 00000 n 0000000580 00000 n 0000002211 00000 n 0000002379 00000 n 0000002619 00000 n 0000002672 00000 n 0000002725 00000 n 0000002894 00000 n 0000003134 00000 n 0000003296 00000 n 0000003523 00000 n 0000003655 00000 n 0000003685 00000 n 0000003845 00000 n 0000003919 00000 n 0000004159 00000 n 0000004336 00000 n 0000004585 00000 n 0000020136 00000 n 0000043459 00000 n 0000043783 00000 n 0000044626 00000 n 0000106756 00000 n 0000202335 00000 n 0000000028 65535 f 0000000029 65535 f 0000000030 65535 f 0000000031 65535 f 0000000032 65535 f 0000000033 65535 f 0000000034 65535 f 0000000035 65535 f 0000000036 65535 f 0000000037 65535 f 0000000038 65535 f 0000000039 65535 f 0000000040 65535 f 0000000041 65535 f 0000000042 65535 f 0000000043 65535 f 0000000044 65535 f 0000000045 65535 f 0000000046 65535 f 0000000047 65535 f 0000000048 65535 f 0000000049 65535 f 0000000050 65535 f 0000000051 65535 f 0000000053 65535 f 0000203594 00000 n 0000000054 65535 f 0000000055 65535 f 0000000056 65535 f 0000000057 65535 f 0000000058 65535 f 0000000059 65535 f 0000000060 65535 f 0000000061 65535 f 0000000062 65535 f 0000000063 65535 f 0000000064 65535 f 0000000065 65535 f 0000000066 65535 f 0000000067 65535 f 0000000068 65535 f 0000000069 65535 f 0000000070 65535 f 0000000071 65535 f 0000000072 65535 f 0000000073 65535 f 0000000074 65535 f 0000000075 65535 f 0000000000 65535 f 0000203647 00000 n 0000203894 00000 n 0000240066 00000 n 0000240599 00000 n 0000292795 00000 n 0000293300 00000 n 0000293591 00000 n 0000293618 00000 n 0000293801 00000 n 0000315919 00000 n 0000319093 00000 n 0000319138 00000 n trailer <<463F583E27111247BF20CB935514EA97>] >> startxref 319589 %%EOF xref 0 0 trailer <<463F583E27111247BF20CB935514EA97>] /Prev 319589/XRefStm 319138>> startxref 321507 %%EOFpcm-202307/doc/LINUX_HOWTO.txt000066400000000000000000000001411445420033100155450ustar00rootroot00000000000000Refer to the main README ("Building PCM Tools" or "Downloading Pre-Compiled PCM Tools") sections pcm-202307/doc/MAC_HOWTO.txt000066400000000000000000000047771445420033100152710ustar00rootroot00000000000000Building and Installing Note: xcode is required to build the driver and dynamic library. Requirements ____________ Building and installing requires make, cmake, gcc, and xcode. It has been tested on the following system configurations: OS X 12.0.1, Xcode 13.1, Apple LLVM compiler 13.0.0 Build PCM and MacMSRDriver ----------------- mkdir build && cd build cmake .. && cmake --build . PCM utilities will be located in build/bin folder, libraries libpcm.dylib and libPcmMsr.dylib - in build/lib. Automatic Install ----------------- cd build sudo make install Install command loads the driver, installs the library into /usr/lib and installs the library headers into /usr/include. Also PCM utilities are installing to /usr/local/sbin. Manual Install -------------- Build steps are the same. To install do the following: 1) load the driver by running src/MacMSRDriver/kextload.sh 2) copy build/lib/libPcmMsr.dylib to a location on your path (auto-install uses /usr/lib) 3) copy src/MacMSRDriver/MSRKernel.h to a location on your path (auto-install uses /usr/include) 4) copy src/MacMSRDriver/MSRAccessorPublic.h as MSRAccessor.h to a location on your path (auto-install uses /usr/include) kext Signatures --------------- As of OS X El Capitan, kexts must be signed. So after building the kext, kextload.sh may fail with: /System/Library/Extensions/PcmMsrDriver.kext failed to load - (libkern/kext) not loadable (reason unspecified); check the system/kernel logs for errors or try kextutil(8). In this event, you will need to either disable System Integrity Protection or sign the kext. You can disable SIP by rebooting into Recovery (reboot, command-option-R), opening a shell, csrutil disable and reboot again. Signing a kext is more involved. You can't self-sign and will first need to obtain a Developer ID from Apple: https://developer.apple.com/contact/kext/ With this ID, you can then sign your kext with codesign. PCM Execution ---------------------- Now you can run ./pcm utility. See description of other built utilities in LINUX_HOWTO.txt Logging/Debugging ---------------------- Sometimes you will get errors while running utilities that may come from the kernel, and you can use something like this DTrace script to correlate it with user-land behavior: $ sudo dtrace -n 'fbt:mach_kernel:_ZN*IOUser*:return /execname == "pcm"/ { @hgram[probefunc, arg1, ustack(20)] = count(); }' -c ./pcm Various commands that can help diagnose errors: $ kmutil log stream $ kmutil inspect -b com.intel.driver.PcmMsr pcm-202307/doc/PCM-EXPORTER.md000066400000000000000000000034171445420033100153450ustar00rootroot00000000000000# Intel® Performance Counter Monitor (Intel® PCM) Prometheus exporter pcm-sensor-server is a collector exposing Intel processor metrics over http in JSON or Prometheus (exporter text based) format. Also [available as a docker container](DOCKER_README.md). Installation on target system to be analyzed: 1. [Build](https://github.com/opcm/pcm#building-pcm-tools) or [download](https://github.com/opcm/pcm#downloading-pre-compiled-pcm-tools) pcm tools 2. As root, start pcm-sensor-server: `sudo ./pcm-sensor-server` or as non-root https://github.com/intel-innersource/applications.analyzers.pcm#executing-pcm-tools-under-non-root-user-on-linux Alternatively one can start [pcm-sensor-server as a container from docker hub](DOCKER_README.md). Additional options: ``` $ ./pcm-sensor-server --help Usage: ./pcm-sensor-server [OPTION] Valid Options: -d : Run in the background -p portnumber : Run on port (default port is 9738) -r|--reset : Reset programming of the performance counters. -D|--debug level : level = 0: no debug info, > 0 increase verbosity. -R|--real-time : If possible the daemon will run with real time priority, could be useful under heavy load to stabilize the async counter fetching. -h|--help : This information ``` The default output of pcm-sensor-server endpoint in a browser: ![image](https://user-images.githubusercontent.com/25432609/226344012-8783e154-998e-48a7-a2ca-f2c42af9c843.png) The PCM exporter can be used together with Grafana to obtain these Intel processor metrics (see [how-to](../scripts/grafana/README.md)): ![pcm grafana output](https://raw.githubusercontent.com/wiki/opcm/pcm/pcm-dashboard-full.png) pcm-202307/doc/PCM-SENSOR-SERVER-README.md000066400000000000000000000075511445420033100170500ustar00rootroot00000000000000# Global PCM Events | Event Name | Description | |-----------------------------|-----------------------------------------------------------------------------| | Measurement_Interval_in_us | How many us elapsed to complete the last measurement | | Number_of_sockets | Number of CPU sockets in the system | # Core Counters per socket OS_ID is the OS assigned ID of the logical CPU core and denotes the socket id, core id and thread id. The events below are followed by the same {socket="socket id",core="core id",thread="thread id"} as the OS_ID of their section with source="socket/core/thread" appended that denotes what the quantity of the event accounts for. For example Instructions_Retired_Any{socket="0",core="1",thread="1",source="core"} refers to Instructions_Retired_Any for socket 0, core 1, thread 1, and accounts for the total instructions retired of the specified core. | Event | Description | |------------------------------------------------|--------------------------------------------------------------| | Instructions_Retired_Any | Total number of Retired instructions | | Clock_Unhalted_Thread | | | Clock_Unhalted_Ref | Counts the number of reference cycles that the thread is | | | not in a halt state. The thread enters the halt state when | | | it is running the HLT instruction. This event is not | | | affected by thread frequency changes but counts as if the | | | thread is running at the maximum frequency all the time. | | L3_Cache_Misses | Total number of L3 Cache misses | | L3_Cache_Hits | Total number of L3 Cache hits | | L2_Cache_Misses | Total number of L2 Cache misses | | L2_Cache_Hits | Total number of L3 Cache hits | | L3_Cache_Occupancy | Computes L3 Cache Occupancy | | SMI_Count | SMI (System Management Interrupt) count | | Invariant_TSC | Calculates the invariant TSC clocks (the invariant TSC | | | means that the TSC continues at a fixed rate regardless of | | | the C-state or frequency of the processor as long as the | | | processor remains in the ACPI S0 state. | | Thermal_Headroom | Celsius degrees before reaching TjMax temperature | | CStateResidency | This is the percentage of time that the core (or the whole | | | package) spends in a particular level of C-state | | References: https://software.intel.com/content/www/us/en/develop/articles/intel-performance-counter-monitor.html https://software.intel.com/content/dam/develop/external/us/en/documents-tps/325384-sdm-vol-3abcd.pdf - Chapter 18 Performance Monitoringpcm-202307/doc/PCM_ACCEL_README.md000066400000000000000000000142741445420033100160060ustar00rootroot00000000000000## Purpose: Intel® Xeon® Scalable Processors UNCORE accelerator(start from 4th Gen Intel® Xeon® Scalable Processor (codenamed Sapphire Rapids)) including Intel® In-Memory Analytics Accelerator (Intel® IAA), Intel® Data Streaming Accelerator (Intel® DSA) and Intel® QuickAssist Technology (Intel® QAT), etc are key feature of Intel® Xeon® Scalable Processors that can benefit the Intel architecture platform performance in the data center industry. The accelerator and related software stack can be a key contributor to data center system performance, but sometimes it’s NOT easy for customer/user to get/understand the performance data of the accelerator like utilization, throughput, etc since low level hardware event sets is complex to understand without the deep knowledge of the accelerator hardware/software architecture. This pcm-accel tool will sample the performance data from accelerator hardware and show it to end user in an easy-to-understanding format. The goal is to help the user to quickly and accurately see a high-level performance picture or identify issues related to accelerator with or without solid knowledge of it. ## Command syntax: pcm-accel [target] [options] #### the target parameter Notes: only 1 target is allowed to monitor. | target | Default | Description | | ------ | ------- | ----------------------------- | | -iaa | yes | Monitor the IAA accelerator. | | -dsa | no | Monitor the DSA accelerator. | | -qat | no | Monitor the QAT accelerator. | #### the options parameter Notes: multiple options is allowed. | options | Default | Description | | ---------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------- | | -numa | no | Print NUMA node mapping instead of CPU socket location. | | -evt=[cfg.txt] | opCode-xxx-accel.txt | Specify the event config file name as cfg.txt.
- xxx is the cpu model id, for example 143 for sapphire rapid. | | -silent | no | Silence information output and print only measurements | | -csv[=file.csv] | no | Output compact CSV format to screen or a file in case filename is provided | | -csv-delimiter=[value] | no | Set custom csv delimiter | | -human-readable | no | Use human readable format for output (for csv only) | | -i=[value] | 0 | Allow to determine number of iterations, default is 0(infinite loop) if not specified. | | [interval] | 3 | Time interval in seconds (floating point number is accepted) to sample performance counters, default is 3s if not specified. | #### Examples: This example prints IAA counters every second 10 times and exits > pcm-accel -iaa 1.0 -i=10  This example saves IAA counters twice a second save IAA counter values to test.log in CSV format > pcm-accel -iaa 0.5 -csv=test.log            This example prints IAA counters every 3 second in human-readable CSV format > pcm-accel -iaa -csv -human-readable ## Prerequisites: Linux* OS: FreeBSD* OS: Windows OS: - Install and load the required accelerator driver(iaa/dsa, qat driver, etc). Notes: - QAT monitoring and NUMA node display feature is supported only on Linux OS! ## Tool UI introduction: Common indicator(Column field): - Accelerator = Accelerator device id. - Socket = CPU socket id where accelerator device is located. - NUMA Node = NUMA node that accelerator device belongs to. - Inbound_BW = Data throughput input to the accelerator device, unit is Bps(Bytes per second). - Outbound_BW = Data throughput output from the accelerator device, unit is Bps(Bytes per second). Specific indicators related to IAA/DSA:  - ShareWQ_ReqNb = The number of request submitted to share work queue of accelerator. - DedicateWQ_ReqNb = The number of request submitted to dedicate work queue of accelerator. ![image](https://user-images.githubusercontent.com/25432609/224027332-8846dff6-f71e-4daa-a189-730e68c7e1b2.png) ![image](https://user-images.githubusercontent.com/25432609/224027445-2b08e89c-4653-4f39-971b-a7dc76bd7349.png) Specific indicators related to QAT: - util_comp0 = The utilization of the compress engine 0, unit is %.(Sapphire Rapids platform has 1 compress and 3 decompress engine per QAT device) - util_decomp0 = same as above for decompress engine 0. - util_decomp1 = same as above for decompress engine 1. - util_decomp2 = same as above for decompress engine 2. - util_xlt0 = same as above for translation engine 0. ![image](https://user-images.githubusercontent.com/25432609/224027570-e433aeef-c2ed-418d-aa42-18eef0f1b645.png) ## Event config file: pcm-accel tool allows the user to customized the monitored performance events with the config file as advance feature. Customize fields of cfg file: - ev_sel and ev_cat field for IAA/DSA monitor event. - ev_sel field for QAT monitor event.  - multiplier/divider is for event data display calculation. - vname is the event name string(column) displayed in the UI. Please refer to the spec or code to learn more about the event mapping if you want to customize it. - IAA/DSA: https://software.intel.com/en-us/download/intel-data-streaming-accelerator-preliminary-architecture-specification - QAT: please refer to the [mapping table in source code](https://github.com/intel/pcm/blob/f20013f7563714cf592d7a59f169c1ddee3cf8ba/src/cpucounters.cpp#L915) Here is the content of the event cfg file(opCode-143-accel.txt as example) ![image](https://user-images.githubusercontent.com/25432609/224027717-1dcdae9e-6701-4b6f-90a0-8108c4ea4550.png) pcm-202307/doc/PCM_RAW_README.md000066400000000000000000000261621445420033100156270ustar00rootroot00000000000000-------------------------------------------------------------------------------- PCM Raw Utility -------------------------------------------------------------------------------- Disclaimer: in contrast to other PCM utilities this one is for expert usage only. *pcm-raw* allows to collect arbitrary core and uncore PMU events by providing raw PMU event ID encoding. It can become handy if other low-level PMU tools (e.g. emon, Linux perf) can not be used for some reason. For example: - emon kernel driver is not compatible with the currently used Linux kernel or operating system - loading emon Linux kernel driver is forbidden due to system administration policies - Linux kernel is too old to support modern processor PMU and can not be upgraded Currently supported PMUs: core, m3upi, upi(ll)/qpi(ll), imc, m2m, pcu, cha/cbo, iio, ubox Recommended usage (as privileged/root user): 1. Install VTune which also contains emon (emon/sep driver installation is not needed): [free download](https://software.intel.com/content/www/us/en/develop/tools/vtune-profiler.html) 2. Run emon with `--dry-run -m` options to obtain raw PMU event encodings for event of interest. For example: ``` # emon -C BR_MISP_RETIRED.ALL_BRANCHES,UNC_CHA_CLOCKTICKS,UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART0,UNC_UPI_TxL_FLITS.NON_DATA --dry-run -m Event Set 0 BR_MISP_RETIRED.ALL_BRANCHES (PerfEvtSel0 (0x186) = 0x00000000004300c5) CC=ALL PC=0x0 UMASK=0x0 E=0x1 INT=0x0 INV=0x0 CMASK=0x0 AMT=0x0 cha Uncore Event Set 0 UNC_CHA_CLOCKTICKS (CHA Counter 0 (0xe01) = 0x0000000000400000) qpill Uncore Event Set 0 UNC_UPI_TxL_FLITS.NON_DATA (QPILL Counter 0 (0x350) = 0x0000000000409702) iio Uncore Event Set 0 UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART0 (IIO Counter 0 (0xa48) = 0x0000701000400183) ``` 3. Run *pcm-raw* by specifying the obtained raw event encodings to collect into csv file. Example: ``` pcm-raw -e core/config=0x00000000004300c5,name=BR_MISP_RETIRED.ALL_BRANCHES -e cha/config=0x0000000000400000,name=UNC_CHA_CLOCKTICKS -e qpi/config=0x0000000000409702,name=UNC_UPI_TxL_FLITS.NON_DATA -e iio/config=0x0000701000400183,name=UNC_IIO_DATA_REQ_OF_CPU.MEM_WRITE.PART0 -csv=out.csv ``` 4. View/process the csv file using your favorite method. For example just open it in Excel. -------------------------------------------------------------------------------- Collecting Register Values -------------------------------------------------------------------------------- pcm-raw supports collecting raw MSR and PCICFG (CSR) register values. The syntax is described below: Model Specific Registers (MSRs): ``` package_msr/config=,config1=[,name=] ``` static_or_freerun encoding: * 0 : static (last value reported in csv) * 1 : freerun (delta to last value reported in csv) Examples: ``` package_msr/config=0x34,config1=0,name=SMI_COUNT thread_msr/config=0x10,config1=1,name=TSC_DELTA thread_msr/config=0x10,config1=0,name=TSC ``` If the name is not specified the first event will show up as package_msr:0x34:static, with the name it will show up as SMI_COUNT in csv. PCI Configuration Registers - PCICFG (CSR): ``` pcicfg/config=,config1=,config2=[,name=] ``` * width: register width in bits (16,32,64) * dev_id: Intel PCI device id where the register is located * offset: offset of the register * static_or_freerun: same syntax as for MSR registers Example: ``` pcicfg32/config=0xe20,config1=0x180,config2=0x0,name=CHANERR_INT ``` From: https://www.intel.la/content/dam/www/public/us/en/documents/datasheets/xeon-e7-v2-datasheet-vol-2.pdf -------------------------------------------------------------------------------- Collecting Events By Names From Event Lists (https://github.com/intel/perfmon/) -------------------------------------------------------------------------------- pcm-raw can also automatically lookup the events from the json event lists (https://github.com/intel/perfmon/) and translate them to raw encodings itself. To make this work you need to checkout PCM with simdjson submodule: * use git clone --recursive flag when cloning pcm repository, or * update submodule with command `git submodule update --init --recursive`, or * download simdjson library in the PCM source directory and recompile PCM: 1. change to PCM 'src/' directory 2. git clone https://github.com/simdjson/simdjson.git 3. re-compile pcm Example of usage (on Intel Xeon Scalable processor): ``` pcm-raw -tr -e INST_RETIRED.ANY -e CPU_CLK_UNHALTED.THREAD -e CPU_CLK_UNHALTED.REF_TSC -e LD_BLOCKS.STORE_FORWARD -e UNC_CHA_CLOCKTICKS -e UNC_M_CAS_COUNT.RD ``` or with event groups specified in event_file.txt (with event multiplexing): ``` pcm-raw -tr -el event_file.txt ``` where event_file.txt contains event groups separated by a semicolon: ``` # group 1 INST_RETIRED.ANY CPU_CLK_UNHALTED.REF_TSC CPU_CLK_UNHALTED.THREAD DTLB_LOAD_MISSES.STLB_HIT L1D_PEND_MISS.PENDING_CYCLES_ANY MEM_INST_RETIRED.LOCK_LOADS UOPS_EXECUTED.X87 UNC_CHA_DIR_LOOKUP.SNP UNC_CHA_DIR_LOOKUP.NO_SNP UNC_M_CAS_COUNT.RD UNC_M_CAS_COUNT.WR UNC_UPI_CLOCKTICKS UNC_UPI_TxL_FLITS.ALL_DATA UNC_UPI_TxL_FLITS.NON_DATA UNC_UPI_L1_POWER_CYCLES ; # group 2 INST_RETIRED.ANY CPU_CLK_UNHALTED.REF_TSC CPU_CLK_UNHALTED.THREAD OFFCORE_REQUESTS_BUFFER.SQ_FULL MEM_LOAD_L3_HIT_RETIRED.XSNP_HIT MEM_LOAD_L3_HIT_RETIRED.XSNP_HITM MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS UNC_CHA_DIR_UPDATE.HA UNC_CHA_DIR_UPDATE.TOR UNC_M2M_DIRECTORY_UPDATE.ANY UNC_M_CAS_COUNT.RD UNC_M_CAS_COUNT.WR UNC_M_PRE_COUNT.PAGE_MISS UNC_UPI_TxL0P_POWER_CYCLES UNC_UPI_RxL0P_POWER_CYCLES UNC_UPI_RxL_FLITS.ALL_DATA UNC_UPI_RxL_FLITS.NON_DATA ; ``` Sample csv output (date,time,event_name,milliseconds_between_samples,TSC_cycles_between_samples,unit0_event_count,unit1_event_count,unit2_event_count,...): ``` 2021-09-27,00:07:40.507,UNC_CHA_DIR_LOOKUP.SNP,1000,2102078418,76,70,56,91,88,75,76,158,74,60,77,81,75,74,71,95,99,95,125,87,68,136,54,91,65,84,69,46,75,100,92,68,67,70,68,80,72,88,80,76,130,71,102,98,79,73,71,109 2021-09-27,00:07:40.507,UNC_CHA_DIR_LOOKUP.NO_SNP,1000,2102078418,1218,1280,1187,1310,1268,1287,1282,1331,1265,1267,1300,1270,1258,1307,1289,1300,1410,1378,1312,1316,1367,1337,1332,1317,1584,1519,1569,1557,1483,1537,1545,1520,1562,1527,1575,1540,1530,1581,1476,1525,1610,1680,1581,1657,1565,1613,1596,1600 2021-09-27,00:07:40.507,INST_RETIRED.ANY,1000,2102078418,705400,44587,45923,238392,53910,69547,46644,46172,44740,44732,45692,44864,46105,45352,45057,217052,46511,46671,46893,46459,53739,47021,114133,46339,61649,59027,142096,48048,98178,48288,162122,474329,48046,49795,78239,425635,105512,69933,49827,48913,71549,48451,294858,312316,149586,540477,49115,55144,46788,61681,82964,81127,116227,85776,453369,145979,81007,82269,83580,73595,73355,73751,72599,47169,47767,48191,48131,48359,48621,67664,48227,532184,49686,48704,324264,48539,48795,48609,60275,518368,116077,163734,526815,50650,140337,666605,47935,1368049,47243,337542,47153,46882,46925,62373,70186,466927 2021-09-27,00:07:40.507,CPU_CLK_UNHALTED.REF_TSC,1000,2102078418,3618636,384720,589092,2143512,766752,724164,803124,627312,541548,538188,534324,509964,535164,527436,529284,1366176,488124,491820,533148,543900,608580,577920,1145172,602196,919632,824544,1429344,692916,1092756,700644,1298640,2487156,736344,841344,1324008,1855476,1260084,1104768,658308,5805324,851424,766080,1909740,2170392,1313592,3986892,683844,986832,659064,642432,682668,772128,1076628,710220,2514876,1085112,715344,700812,676452,594468,577668,590856,574056,597996,525336,551460,548520,561624,569352,741468,623196,3124212,592032,596400,2265312,556584,593124,546756,766752,2547216,1047396,1280160,2704884,525336,1200444,3255000,497700,13643700,481572,1601040,515592,523740,503664,854280,603120,2305128 2021-09-27,00:07:40.507,CPU_CLK_UNHALTED.THREAD,1000,2102078418,1723000,183219,280560,1020631,365140,344897,382467,298699,257868,256243,254471,242757,254794,251172,252091,650377,232442,234209,253807,259024,289817,275179,545244,286717,437888,392646,680513,329759,520244,333662,618356,1184347,350594,400648,630580,1517122,599939,525847,313441,2765951,405441,364827,909395,1033366,625655,1898427,325614,881026,312798,305884,325245,367890,512845,338440,1197524,516836,341497,334581,322975,283138,275031,281300,273347,284616,250171,262581,261182,267455,271097,353013,296757,1487751,282516,283651,1076725,265489,282845,260889,365411,1212743,498705,611118,1287439,360493,571158,1549944,236616,6499483,229820,762766,245338,248648,239640,406676,287582,1714659 2021-09-27,00:07:40.507,DTLB_LOAD_MISSES.STLB_HIT,1000,2102078418,10093,1178,1186,2593,1184,1356,1182,1201,1187,1200,1191,1179,1189,1179,1177,1444,1218,1205,1158,1183,1216,1190,1789,1184,1388,1347,2207,1384,1566,1352,1541,3221,1374,1398,1580,11223,1690,1427,1398,1356,1531,1388,3429,3567,2136,2639,1354,1393,1181,1188,1457,1456,1801,1437,4698,1697,1426,1434,1418,1452,1396,1394,1434,1164,1349,1349,1356,1318,1354,1528,1349,18546,1168,1160,8935,1166,1172,1167,1194,4432,1801,2341,3152,1190,1777,4328,1178,4396,1170,1939,1199,1150,1158,1197,1187,12441 2021-09-27,00:07:40.507,L1D_PEND_MISS.PENDING_CYCLES_ANY,1000,2102078418,682630,81530,114229,363299,169260,134931,441644,183870,89947,95379,98135,81156,75366,77990,78734,178321,52738,53883,57241,56306,65514,94824,152070,227164,87723,80980,300491,70675,148506,70130,173723,628031,142178,161405,503099,383743,255465,317627,67134,1509172,105102,242908,300344,336683,157280,555052,84017,615357,526290,88531,117674,387708,192129,157226,451213,201430,103646,106302,112452,86251,83203,82880,80239,189044,72389,73820,75135,70746,84963,106517,168907,249006,117006,109389,320326,98291,168531,100734,206075,647276,167155,154684,495947,359092,257614,322235,78189,1473756,148139,278653,308380,343576,166510,556816,90475,306546 2021-09-27,00:07:40.507,MEM_INST_RETIRED.LOCK_LOADS,1000,2102078418,3462,231,235,1159,259,277,239,237,236,238,236,239,238,237,237,1114,237,237,238,237,265,237,555,237,277,278,542,237,431,240,389,906,239,238,385,3973,435,280,238,238,401,238,847,1238,604,1948,238,238,235,275,266,267,428,277,1287,399,271,277,272,239,240,239,239,237,237,237,237,237,238,347,237,4266,238,238,1174,238,238,238,270,1361,526,697,1101,238,615,2172,238,4276,236,642,236,237,236,275,299,2842 2021-09-27,00:07:40.507,UOPS_EXECUTED.X87,1000,2102078418,1152,12,13,496,46,17,27,12,11,13,10,14,11,27,12,1591,11,10,11,13,23,11,257,12,64,52,216,31,231,31,1668,5944,31,30,85,710,101,54,34,41,100,33,1852,1561,423,2348,28,46,14,23,155,57,82,172,2776,281,19,52,107,18,36,18,19,14,11,10,10,10,26,57,10,108,31,33,151,31,32,30,63,3700,361,509,4610,31,396,1814,31,5607,33,4175,31,30,32,47,78,471 2021-09-27,00:07:40.507,UNC_M_CAS_COUNT.RD,1000,2102078418,37565,33584,0,0,0,0,40306,0,37373,0,0,0 2021-09-27,00:07:40.507,UNC_M_CAS_COUNT.WR,1000,2102078418,58994,53007,0,0,0,0,25088,0,21901,0,0,0 2021-09-27,00:07:40.507,UNC_UPI_CLOCKTICKS,1000,2102078418,1300347171,1300351441,1200328715,1300297715,1300303139,1200283803 2021-09-27,00:07:40.507,UNC_UPI_TxL_FLITS.ALL_DATA,1000,2102078418,132768,150840,0,285147,269190,0 2021-09-27,00:07:40.507,UNC_UPI_TxL_FLITS.NON_DATA,1000,2102078418,298203,319302,0,293389,264875,0 2021-09-27,00:07:40.507,UNC_UPI_L1_POWER_CYCLES,1000,2102078418,0,0,1200328715,0,0,1200283803 ``` The unit can be logical core, memory channel, CHA, etc, depending on the event type. pcm-202307/doc/STARS.md000066400000000000000000000002471445420033100143520ustar00rootroot00000000000000## Git Hub Star History for PCM Project [![Star History Chart](https://api.star-history.com/svg?repos=intel/pcm&type=Date)](https://star-history.com/#intel/pcm&Date) pcm-202307/doc/WINDOWS_HOWTO.md000066400000000000000000000117661445420033100156000ustar00rootroot00000000000000**COMPILATION AND INSTALLATION OF UTILITIES** _For support of systems with more than _**_64_**_ logical cores you need to compile all binaries below in “_**_x64_**_†platform mode (not the default “_**_Win32_**_†mode)._ Command-line utility: 1. Compile the windows MSR driver (msr.sys) with Windows DDK Kit (see the sources in the WinMSRDriver directory). For Windows 7 and later versions you have to sign the msr.sys driver additionally ([http://msdn.microsoft.com/en-us/library/ms537361(VS.85).aspx](http://msdn.microsoft.com/en-us/library/ms537361(VS.85).aspx)). To enable loading test signed drivers on the system: in administrator cmd console run `bcdedit /set testsigning on` and reboot). 2. Build the *pcm.exe* utility: ``` cmake -B build cmake --build build --config Release --parallel ``` alternatively you can perform `cmake -B build`, open *PCM.sln* form *build* folder in and build required project in Visual Studio. .exe and .dll files will be located in *build\bin\Release* folder 3. Copy the msr.sys driver and pcm.exe into a single directory 4. Run pcm.exe utility from this directory For Windows 7 and Windows Server 2008 R2 the PCM utilities need to be run as administrator: Alternatively you can achieve the same using the “Properties†Windows menu of the executable (“Privilege level†setting in the “Compatibility†tab): Right mouse click -> Properties -> Compatibility -> Privilege level -> Set “Run this program as an administratorâ€. ![Screenshot](run-as-administrator.png) If you are getting the error `Starting MSR service failed with error 3 The system cannot find the path specified.` try to uninstall the driver by running `pcm --uninstallDriver` and optionally reboot the system. Graphical Perfmon front end: 1. Compile the windows MSR driver (msr.sys) with Windows DDK Kit (see the sources in the WinMSRDriver directory). For Windows 7 and later versions you have to sign the msr.sys driver additionally ([http://msdn.microsoft.com/en-us/library/ms537361(VS.85).aspx](http://msdn.microsoft.com/en-us/library/ms537361(VS.85).aspx)). 2. Copy msr.sys into the c:\windows\system32 directory 3. Build pcm-lib.dll using Microsoft Visual Studio or cmake 4. Build 'PCM-Service.exe' using Microsoft Visual Studio or cmake 5. Copy PCM-Service.exe, PCM-Service.exe.config, and pcm-lib.dll files into a single directory The config file enables support for legacy security policy. Without this configuration switch, you will get an exception like this: Unhandled Exception: System.NotSupportedException: This method implicitly uses CAS policy, which has been obsoleted by the .NET Framework. 6. With administrator rights execute '"PCM-Service.exe" -Install' from this directory 7. With administrator rights execute 'net start pcmservice' 8. Start perfmon and find new PCM\* counters If you do not want or cannot compile the msr.sys driver you might use a third-party open source WinRing0 driver instead. Instructions: 1. Download the free RealTemp utility package from [http://www.techpowerup.com/realtemp/](http://www.techpowerup.com/realtemp/) or any other free utility that uses the open-source WinRing0 driver (like OpenHardwareMonitor [http://code.google.com/p/open-hardware-monitor/downloads/list](http://code.google.com/p/open-hardware-monitor/downloads/list)). 2. Copy WinRing0.dll, WinRing0.sys, WinRing0x64.dll, WinRing0x64.sys files from there into the PCM.exe binary location, into the PCM-Service.exe location and into c:\windows\system32 3. Run the PCM.exe tool and/or go to step 6 (perfmon utility). Known limitations: Running PCM.exe under Cygwin shell is possible, but due to incompatibilities of signals/events handling between Windows and Cygwin, the PCM may not cleanup PMU configuration after Ctrl+C or Ctrl+Break pressed. The subsequent run of PCM will require to do PMU configuration reset, so adding -r command line option to PCM will be required. PCM-Service FAQ: Q: Help my service won't start, what can I do to diagnose the problem? A: Please check in the Windows Application "Event Viewer" under "Windows Logs" and then under "Application". PCM-Service writes its messages here, just look for errors. If you can't figure it out how to fix it, create a bug report and make sure to paste the text from the Event Viewer in the bug report so we can diagnose the issue. Q: I see a message in the Events Viewer that PCM-Service does not start because the "custom counter file view is out of memory", how do I fix this? A: Despite that PCM-Service is reserving more memory than the standard 512kB this error can still occur if there is another application that uses performance counters is initialized before PCM. There are two options: 1. identify the application or service that starts before PCM-Service and stop or disable it and consequently reboot and try again 2. [find your machine.config file](https://stackoverflow.com/questions/2325473/where-is-machine-config) and add ` ` to that file pcm-202307/doc/generate_summary_readme.md000066400000000000000000000005221445420033100203360ustar00rootroot00000000000000Step 1 ```pip3 install pipreqs``` Step 2 ```cd /path/to/pcm/scripts``` Step 3 ```python3 -m pipreqs.pipreqs --encoding utf-8 .``` Step 4 ```pip3 install -r requirements.txt``` Step 5 ```python3 generate_summary.py --help``` Step 6 ```python3 generate_summary.py -{argument1} -{argument2} ....-{argumentN}``` pcm-202307/doc/license.txt000066400000000000000000000030561445420033100153200ustar00rootroot00000000000000This notice belongs to the code originating from "Intel Performance Counter Monitor". Copyright (c) 2009-2016, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pcm-202307/doc/run-as-administrator.png000066400000000000000000000336311445420033100177300ustar00rootroot00000000000000‰PNG  IHDRGˆøeosRGB®Îé pHYsÃÃÇo¨d7>IDATx^í}€U½÷”;sgn/ÛK6=»é !H0ô**_àáDTåñQy”T‚DJ€J‰@€P B€ôF ©»Ù~{›þýΜ»7›Í&ÙÍ&”Ç ‡Ù™¹gΜù÷vØ--&co&KÿÏêºÁ;xËb°k†! ¼,3™,½z6 ý·/ý ˜€†Á§$pÃZœýB¤åY†ã8Ǹ\ì?_~}ÆŒ3L“‘FÂ¥Ïé­3™Œ,ËÉdÒçóÑ!Y¶ â7¶ˆÌÿE/ãO– †®(ª ^/‹?3‹aó3w)âÀ@fï ó[à€‘Ç‹‚ÇÚÄçyÆÔa~¿8ÿ¥Wfœw6Ç2"Ï8??ÐuÝáp†±™¦ UÅéÄˆÈÆ®odóèAÞcMÓp:yŽÜãçä÷8/‡‹8ð…ã „PCÀoCe,×…º¦Z–QZêúçK¯sMdq•· íï ñ9€1p=¥Ýza×4[dè6&³"kšžÍ¦=.—$‰¦noÐDZqÞ/< \h€= | Å8ÁºØKbXEɲŒY^zcá›§vŠ$ÐÕí.nô”x”Ó~ y½Þ<XµÛ´Q™t\²ÐÑÚÊa؆piEhd‘[ñ"jmu`@= | ÅÇ P’üÇÙúkrTZVÕäŸp8¸|ùÒ‰“Æ»%‰± žµÁç3ßÀ°år9Q! ÙBšŸl]»¼É°¡ŸÈú`U@ƒÍ×'âQ¯[64ÕÐ5§CÈeÓ~$ò”÷ °eÀWG?I±‡Ì@|(ØÈ ´Ò2N–ÄÖÖæP8(ËNhédê3‡ò@ŒG’$MÓ¨DƒúñôéÓ y>°¤QÃDp°:ÚÎÖf·ìS_=Æ4 âäÅ=ã0I0à˜.`ÅöK=y€î"íT,i#ˆ0 åÁFÓ5Á!áás²& ·ÛM9ÆÐØØ¸nݺÓN;­À Ø)ôãsœf¦²öãòphÒÄC5R±¨ìrêª á)ghýÁ‚üÙSc^Ã0“È@:!¬@x¦eP=„ˆ•”ˆôwf2@™¸?³ñ¿uœö÷ê² ‘Oh1:3.—+•J†‚ÞææfQt€úJ’«¿_÷°Ü‘À „Qa  ý|ðÁ¹çž[蜃oX¼ID:ðއìïù –½ØyÞp»|­\6åàYJƒvHàô¹l©T !Ç“Íf ʱªª 8«’°ÑÀå?Qq£°µÒÒヷ “N§++Jë ZÑH»Ã厶µêº¦«š¥™éxZ!2¶ù¼n@¿ìÓ©T8’%)™HÀ4›I§¢XUYžˆÇsÙ,κ‹F<ŸÍd>†¾Œ³xÏ@f€È³{í]Æ¢‚Õè°òu…˜Š=P ÄÛú1ìBh»pÀÒ-F7M¸ÄLô ª@‡ˆ¨9¶‡ *ý¶ì–—–¥SŠ$»£ñ˜(°Cj«Û›DÙåwKº’Q5Íö&QgG›šš€¯UU•h[[ˆ¬ tô‘H d¤²²‚º6À”JKÃÔeQÜŠ3pÐQh 8¶°}ûv À‰œ1Ô¬¦dt5««9UË芕 Ëgfc3kÚÆàþn¦ª*è§3k¹¤aeýá–wÆ:[ÜNNt»[Ûâ’(¸$> 1œsð‰D­««ݾ}'ÀrOh3ÍÍ-1pœªµµL¦¿*ÞÿÕœà@>رcZ*qŸ;ä÷„|n¿O‚hî¸óìŒh«48‚쇀ð3@w{½Çê¹T6»íŽ»†Öšþµ£ÎŸñõ›6É¿e(Œ©ŠNGKk  (vÆb ;@ÃKÂNl 2à Žãñ8Níí«ùE‹oÝßÌI’‰b!A3F›—ŽvïÜÖ´k[#Ù·ÛÛa ‚,ß6×äYA2ôuДlƨD:“Íå**Ëï¾ëŽ?ÝsÏGËWlÞ²Á+ ççÍb€u^¿×ãqU”W„B!Œ5×jºÝ°é1 Á¾ 4ð“;=¡Èúú%¾ò÷uWÄiœÀöcØÁr‹×ïÆysXF‡â€¶q׎²pî½l:eê*´{Š=Ä|Ú›ù’š]éTï±[ ;¾@I,­ù¼|óÖ÷¾yÎŒoœã 7ü\R¶¶·uŒœ4ã¿>ª%Öïܱ!–6·noú¿ÿuÍ9çœhÖ¬Y›7ož6mÚÏþó÷Þ{ï7Þ@Ü_4=úè£o¼ñF0 <‡üWþû'àà3@ƒäÐBÌ1,=úè£eee'N:t(¬¥Üø†Êqõ•ãë«ÇÕ×Á/6¦~"Œˆß¶rvYîÅÇMœsº"ˆR, …ˆCœð°¾P;m×ÝÊ•ËgßõøŒç@òùÅ/~±mÛÀ}$¹ë®»^{íµŸüä'»vízøá‡;î¸úúúGydÆ ô}€ ûâŰéržÞ“7B¯ÑB´¦1D€¸RxÚÖªV²ËG™Ú?<”`' $Xdád€Ž›Éä …™LUa‚4VSSƒž'O®;餓.¾øâO?ýtñâÅ/½ôÒ’%Kf̘Âd¥H ?~<<|@ˆC%%¡Bàkñ+gàÀ3@an’AÈŽµ£º±úmYˆDÎÙ[Û”Ï8€È. å$e¥'ŸrÊk¯.° “á-]V[S3hÐ ¸NâÉd*•’@ëÂ\qÅ;wîÜ~ë­·âOˆ=è,,wu.×ÜÜJƒ`‹[q>ï³á¾‹ Ð(Û –O‰ h@÷~Å2Xœ×ë‡árJÉxÂátÿñÎÙCÕ~çܧžvæc?u÷×PáàÅöÎøÙgŸý‹oüáxúé§Ïœ9ó÷¿ÿý©§ž>~üÄW^y¾Ð~(Á@b ޽^øàÌÀ_¾ØCqˆ®k™Ýd}›Ü¯_¿rÄ"‰´d‹L{é»{Ï\¯:1XŒS!z9x§n¦“Éíáp‰¦•lÚ´9“ÜYYY]Q5.›1~tÙ9+V}03¬cpÝð`0Œ¤…+VAŽ‚t̘18€ ,@± ¬ÚpÀŽ‚:îTüê_µ ÒŽFù"44ÉÇ{¬¢¢b„ Æ ƒh½w˜Ðöü=é¡”üB4£é0bŠ‘ŽK7J*jšZ;ôlª¦¬|âÑÓʪ†p&ôñ&ãðK'M<ê¨I“+++!ñÇãÉ‘#GÖÖÖŽ7ä  .¼ååå@€>”E0•~Õ¾eñ}Ð @ȱã! {/ÏÙ“.ÝŸApííHZy}À¼Ý­‘ÒÒj¤g†ËÃñ”â<©hòÖégžuú™ßRUÔÎöŽaÆ‚«€ÿâq¬¥J0T C7hoo‡>U2RS¼·8ûºoùôPržpƒ=Ê@¿'qÐàA™´ªk ‡XNT4+—Ig:;’{gSk¨Âo‰]ôÝKnÿýˆˆcƒÖ¾mÛ6H>Æ ý⫪*ÐÂkF}{¸d{0~¨øƒâ ô6H~ë!ÝãOê¦4rîP¶X„î B…’IQs9ÅWV¦:<Ðp"­N ƒÙÜÒZ^^™H¤XžÛ½»e„q™TÚ-»|/|Æ;vìOHg2ð'ÀO ë*8ÐsP‡8m“_+™ŒWÕÖFb1ÑÊn‹D‘M‰¨4R 5šˆ|^P}È6í®yÛÑJyM<™NdÝ^IuÓSÓõ\ÈãñøÚ¢‘PÈ1Ià<Ù†ªùÀ hj,™€ç›Çð;kÝõ:Ý”~¸áঀŽ_á·ðQà6Ø hâFQ J9¬ ‘N·ÉPTy*nÿKf bÀ¡¼-€&\R²kçN@B”1xð`R®H×w75Á°C-ž4Uí~_(€NüVQÀ+TdÙåB  ð’L%ãqÔ¡Fìpž *n[Ĉ:AÄ:êú°¬p ·ì”H5%Ó"a§AøëhpME…{Î.ÒD½êà#…©£Î“âöe#øñˆ_Ú²”K1F Mp )³*Kc¡d€TšÍÐë†Kø9£Æ@!¤•A?†…E î4g-=À4ÐhK@dË·4Ü>ƒÜ9 twò,@wp<  ŸN¡N…¤³3*8œp{\Ê{ mVggŸÚ½Å¡"ì3€KX0A>ÄV=µê«Рë°ý“ Êñ~¥ €8…rlÔwæ ‡KËË©ã péEž$áÍŠÓ_Q¾a”‚D¾²5"eu;zèüU  °Zd_;X9œkŸÏOêT&²v>8çv{=n_—’]@€‚:õe‚¯ø¸  Âþ³{÷n=h*(+¢âþüç?ÃK÷Ô“OBndiDÍfèuÃA¡ß$×™%ùŒ(núäÄ–¶´´<óÌ3À«¥‹?øpÑ"€;•éqIÜt‹ŸØ1í„nÛˆÖ£%D8(PR’#L€”äùD4»«;çÏÿ'8@6«è:Êæˆ]ƒ¤€ª-Gp¿âú¼þüx4}“Š+` Ö¿ùÍo h~ë­·ž{î9€)HAh Ùû{[€>¢>ܹmíÚ›nº 9è®e€,¢MçÏŸp§ú6p Õh6oÞ<ÜœWöÑ` ü’ÂFL fYè7n\°àõdB…üe#ìj‘ÃÙM¢ƒ. BŸ¨±GAh iâ0älá©yófÞ|3Ran¿ývtUU!´TIèu€À£¥ŠºE?ofÏžŠ[n™‰§àèà ¸R áóï2",0‡4°–Êc’Û \‚«náÂ…èdÊQã.½ôR¨Ù^=ŠTîá6ké„#ö¡Š±èhÎ-L×}ëï@°©¼JÑÿwÞ¹ð Q"¥½­mÈ!H [»víƒ>øãÿøk_ûÚ¨Q£^}õUúÊ•+9æ˜SN9eÁ‚ ÁÇ{ì~ô£ºº:´4!¦ŸË/¿|Íš5gœq¤¬iÓŽ‡Ö" ÆçÎ;yòdt¾eË0(¦qòÉ'1é K—.E祭6vìØ¿üå/èüšk®Aù±c§NE &„mƒ±\tÑE[·6O™2X΀<ž††ä6à‡¨U†z}ßÿþ÷qæ·¿ý-±âöœƒÂíä4a‡Š%¥¥€o8d\È‚²¾ûî»Ë–-ì>ñİÄ_pÁW_}5’*¡9М«®º ¡¬¢h NgGÇó¯¼2iÒ$Hù5•åeÀ7z¸ãp˜n¸ ù«_ý*äáÎÓìi¤h~ûÛßnjm½þúëqÌç»ßý.ž l¹iæM0[!fû?øÕª¡l|ç;ßyî¹§3Ù˜XÚO¯»æé§ŸÞºuË©§ž á Ú<0pÖ¬[€0Ï<3Èr‡êP?èG*ÞpDgàâDÀ}ii)@äÄp ” Ѫz_RR‚8À+Z˜Œ@ì!{š·nÝ ¨%L#—üAò C4¢Ûx[(1t èÑÔläGâ%­‘}8¾ä’KšZšp³×Keô¯›Æ¨úz¤ÈM?áø¯OŸ¾båÊŽHçaCc‰xg{dó–-ßûþ%Ûvl÷>¿ã„quç®FôO#—€`H|C:t0$¯T:ñ>¼²²<\ìè(ÆðQ@=‚A€2 8H]wÝu÷ÜsN‚6#Q"b¡A>W­Z F¢pkk+õFx≿þÝ,d“Q«°…ï¨3a§efù hpo^¡jR¤¡û"dþüóί®¨ªÀ÷,Bç¸çíEÿÂÓÿþÂó°VGEÕ††—.Áð`úÇ p@KÁ$(>“Î¥!ä———¦ÓÉ`0P^Qêt ï¿ÿo´‹-ÂPK%aZ[[0ÔÓ<€m÷~½bׇcŽ 4¸ ÜÐ_k‰b7DÐN\ºï¾ûàÎ耲üñ¿üå/!UC D^ ŸuÖYP$¾9¢ñ(ý‚Ñ'µ´â|,žÜãèú1nÆÏO8á_tñE±d gtÝ‚“ pûÿÂ_U[óÏ—_~üÉ'½wϽ¹ê꫇ vëm·7A{S¦N ƒõcFŸr꩟nÝ|Ùe—¢p,*ÈÂr´hÑ;7ßü«!CëP æ”SNÒuåî€á0ØÙÇç(öñ9ÌÀž²(…‡CÛƒ e‘æ+ÒÜûlûË#C˜YyEEG{; ôž†-v!Ó£øÝºµkA†!a¯X± ¨ä’ÛÚI¼tyIX–±d£d²Njý‘D< e•4ÂôdFvˆ.ž×Q5R”¹leÀÄ‚£j±hÂKÓ†.-…7Auz–Øûýn_,Í©I×w/¤²ææv ;,1 J³ °9E…„ƒ×Ô4%ÈìK¾tdHa툙«®ªV5UDÝÐáB¶+\0pœ)9 5‰I½ÊâöÅ›|èþä‘Ö‡€.ôp„Ad!‡ÂD [£˜(„ /á˜Ød›ŽÖöŽÝ- íQHÙmmxŽHMì9§¡GÔ脞Lƒí›¶$b±¢¬íl›Í[ˆë=ãê™ÆI "0Ò{gô>Í4"»'ݶ£ötF#¨;LBµeÂg }e³9T83iljÌdÒ-­ÍíímЀ¡½`~3™T"³#íŠÛ—rX ÕÞˆ²IÒŠí•1˜U6&"í'3A‹Çáuµx«àÄçQ3…„ÈsX\EUðé@#‰îh:uÿ“óÊ*«¦Œ7rèPUÖB. ¾™ÇT±€¦åÀ¢ï¬ÛèP£'Ž2™ãÌX²cÌ ~p?©à`üãD©j¥ƒ‰»,1 w†Î«e¤"½€„€9` Tû%K½¢ø5¢ÍÄ„^Z\]ÈйHZ°‰*¢ˆÃ¡¢D¸!¨½vL[׋nà0Gÿ¤e`hƃH &™fDÉ~´À` N”Ö†éžÙÁP¯ñt\ÂØRÆ–‚0KùUiqùÊD·åÀ=iÌœ½Nc¯8€rùó=×2DB1û0±I¼œøjvÕtS`4¬È1ˆ@IX;PºV‰¥ozò©’Au§Œ7¦Úp @ÑM/“ã—*ÏÿkÝ–2>=¶} ÓÒb°5MH‹&ï––×ÚSµr) ¦g–é±RÉçç%<‚“½ª¥qi¿ÃDuÆËfY¬)€àÔ—³å"“‘@R )4W´W!&ú"‡#‹ÒôR*b& VtRHÊ£ ¢?íH!“¬bÂ(È"5&› †|©TáqÐFàÁ†œ„u nŠ6^€4e BÀ¿€ïÐדt]ò}èú~Ïì©×ÿ®ù剺U(ëóù@ÖÃ_ñnl¨U3 ¾>!t5AVP™ òk9´dº`çá|ÞãNüÝÓÏ•U'M˜Ø0„ Eö°V kÍȩ̀šÂÑæµ·]]±Š 4tê‚.óª"¯@Ÿu%4SS8—V#ånC2Q/+z4bð ›ÓÍ¬Ž•wd9`ÈŠX9C’¨¿ ÒðAÑ5@$,6D¢°±ª0–À9d˜õ¢ÓrÁ=7VçxÐ]ÊøöÊü„Ö‹jªmmHÁOíÄ5«t$;—Sà°ÿõš¢ò‚Ã^uS@%zŒóh±›Ì ]‹ï¢’†v°¶çæùà¼}Œ v·HÙó9Å€°p–S ‘5 @8&†Í2N)¼.{xˈZÙœlJçÿuî‹/¿j 4jꄺ¤¶ ¤h|Z=kç“IØ+·Œrò[g^HgB?ùã 3•aF‹3V‚‘À_ì„/+Ëd¢ >"œª|9c!–S#ëi*£bD&PÆD“„=IN"Áÿ 9D°Ø7“õ‹¶oé¢ôÀHö]³½ö²~¢Áp*Q< 8§±&£äÉ,ƒ"_'“@eRˆ=xµ ð)“!7@*#Õ†9xˆP„?ÊÚáÏœÊ(èú]4ã´À7 8†ØòË^ÙœºOØÕ üj߃OIdNÀ¤\ˆ=…T™|#ÃXi&Û¾óÕW–¿õÑŒ§ÿ¾à™çr5U£¦Mªiã@sÓn‹Ós¢Ÿ9ÁBLçêõÛŽ/ lœu—ŒM¼ÿIòùjJºÀÄS–%'ãi&—ôUÖ0 ËøƒD$‡¨­ ¢à(-¤›Œ!ý¡Î(µ‹ %í袰L0@ŽS)v€9ß° ‘ýÞdtÒ›®I:ÌKê{'|¢+—‹h©,#ÉD€nM1>ä`lAŒ ³†§c…ì5x¾xû`†òúƒôÞ^tâ=ú@÷·ØÓ5ÕiŠíÀf€€™¼F`lúLÉv2²ñþýsÞyî•™OýãÁÿ)ª™8®~ø0* A.ætÍÖ(¤ž_¹a[ƒ¡¯ŸyU¬u÷Ùoü+qÉãH"w%Óyáûk !¦–‘©“ÏÌiœÃKÖÞtXTgBÓuØÍ‰‚ÛåöC`1dKÊÄ3@í¤H‚‡8„duv¢Ú(Ä!âç"2Ö=à ÆwoQ»Çû*Æê貫©¨”m[L·$Gâ ŸÛKòô‰LH?ˆ&kf‰Êé°ì¬:ÔìC²d’IŒ çsifP’Mq ÂØcßÖc¶A̸'ÖqÚK÷RëG· ,]í¶¸†€Ñ’7( EÔà55-‡S“ÐR@T¢-N?ÿê=³?üÇÂY>=û…—œ5US'ŒI<ž¨3g6¢:wÕÜEÉ%’s¦ ÇhnéÃO6,úxí‡Û:–7fš_‡bü±xËÂÖmo/~ëÙmŸ®…® 5”$Í’T^2.ÆJ,JËCÎWdX^Uâ†üïq–”…4SAh³Åꊑ¥-vIí‚É‹&ý»/­Àp(-88ÁAZ¨¹È1À1r\xbè…€/É úB"ôòššÅª‰QK)>Ðn†øÒtÄp¦¥-+gø]^·[S%jº½Dg_Z{%Org½ÇÏ隟d'ÆôG^¶¸Ž`œ<#ñDæ>Ì좃˜0µdÜR3Œ–‘µœW‡Æ¬†ô¬_S¡üQä# $)ð?, å"ª¨šRÎ(µÄéd²¨§EÚ;MÖµ=Á>øü;RÙ°UëV›™§3sM¯©ÍmZónKÓÎ,è§Î&5ÁÊ JÎ?Š 8¡@°­¥õw³f;vø°¡×ÿô§Ÿ|²i°WâGAŸßçö$cQËÔ Sêc ú×¼³–ÒÕª¦•'ÉÉÁ“õæ›ï<ô×{QÕÔ!XpDd”(ë0²zZ52Y-ýþ¢E?ºòÒt.Έ&ΧԄnårzæ_o¾ù“+/CÏŒ©ô±E! ÆÊ±°0›*V—¢-vËĨöjñ'Ùm¿ò0ÒøË;$Ì&{Ç—£­Ž? ]r9±~°ìàX(«¦îÔÑÒ°&w@yÀ!ز½ú—T¢¯,ë¨ÑLL+5\úŽh™T9ÿ­Õ'œ÷ÃÆöx6Þ4$«ìYi¶¿V_²ÁÒöÉœÅÂå„D„ú¦–“—¬&2%•«,¯Ü´æ“Ù¸kÛ–­Ùdö•—^6½,‚NåÏéaoØ-z¼N”Â’hdg5KMå,Åp r^]OWõŒê—½ö1$Ëç²”t[ÀÃ9ù\ÀÃÜœÈå\Nǃª›Ùºi•¥Åün&vÊdÆ£[.äh*OÒŠu„¹Ï?â0ØY·!…š3+øØqÇ6<õG,6á–TŸÛ@ë÷˜çžsÒò%o½Œ–k yYì’Å%ðLÂ+›·Ì¼þ…çãÌ$£ÇE.ë÷ñ~Ë[)C‰¸DÍ%é/'ò9]‰h¹N ŽGÝ„òVÜË X±ÎdYÎÞÙ v*G"!Ëé¢@Ü·(»ƒlAðâ%0-cO` ?ó–[ ³­Ïù˜Ou¤ƒ9M{û ”oð}ÿÄ'¼»øýÊáãVo˜­Dwo(›¦ ×ôíkKÕïnS Wf…*JG€þ#”¾(=*äÿ°2q¦E~DÞµpá[CëFŒn¨ß¼q;âpÎ8ý”W^y3à [–~´Òï ¬^õ1 ¥Ì}ä‰uë>©*«†]µ¼¬þ¶Æ»gÏþËâw?LgrX?gñâ¥ÿmîÒeˇ ‚¸’PÉîæÝÏ=óìÜ'æ")~ðà!N‡°ðÍ·ÿþìß›šZvlÝyá…ß{é…Q¤è‡ÿZSW»»¥ý±¹75·Ž; ^[0zì„…o.llnÅùD* Q¨¤´â©yO5zó¦-üí‘>ø+…?ÿü‹ÉDR–]ª¢¾¿øƒ·ßz{ôè1÷þùÞ^|1 £ÀÑ#>¼jÅꊲŠñ“ŽZþá’'æ>±jÅªÑ £½nï« ^[±|Å'7••–£¦W ŒÇSþAºJqð X¢Á &hkê<§"z9ØÛá—bXÞÔÎܱlÙîuŸ;ã?ׯYÇzü¥550É0}÷ éhUXkœ–Âkq'ÓÆ«ŒGLJl‡Ž-^߸]ûAǺ—Zš7ÆÝÇ7ù¿÷adr“ëØÒYÐr˜Váec$E4Þ4P¥‡Ô‚ŠÁiimKÍ™óø…]’{é¥Wmß–¸üòkq)p_pÁ¥•xûíÏ;ïâöŽÌ³ÏοïþÇÊËK¶niO&áðVT qJ¡ûî{$1~ø©@°Š´ ”pl°½]K%‘]9xêÑg\ÝïÒ)ñÉ'^ýÝoïÓ4ïûï­w¹ª°ìÓ5Wßòû[ØÒKþãÊÇæüÝHÊwÌšóÊs‹â-Ƶ—ÿ í¬›îºuæ=>GÕÍ?ûÃúe;×/ßù»™÷(ùî»ÓOEyôÓÀ*š²ÒQO<þò}÷ΓU±([]9ö˜©gÞø³ÛS±[S5:•à>|÷ã™3ïLÄÙå˶\sͯ9Î3ûîGçϞȲŠZ– ¦ÓX±3hš²iÂNPÜË ˆ¬NV“d ‰Õ%Nw‚þû¨0âwt‰1d^“àêbt ÖF¼,„Tvp´62ƒ¨’IAÄp°X9Êd&§TVÕ˜ÉèÙ£Ëÿïi#ïûŸÿ¼è»gß4ûÍ›ç¶Þùrâ¹eÙÓ-z]¨=ÔƒÛOµe+a›°«$sŠ5—Ÿ)­,m?zÔ˜Q6o¨ª ‰.gu]M"“Œ$ÌQcêÔ!û\S§{Ç]¿¹ê§×nÞº9•cœ.§' WÖ”,Y±ü¡¿=ÔéÌ(™•k>NeS¿üõ‘xÚí“T¯¨ܸ»ó®{ر«­=’Þ°qûÿ}ËO¯ÿù…_ÖÖ‘„´ºvø¥—]ùß7þÔ#¦M9þfÞtÜÑ'4mkר!cœ¬»¦lð¥ÿøš+®þÆ©ßnÝÕÉÎɾñë½– Ñð­ÿsþÙçœUR^óã+¯i;"­'ŸöoÍ8_¼ï.^rûï^·ñÓ±ã'L=öÄc¦xÁ÷Îý÷ËÆMš:ë¶_ÿeΜÎhº½Sá÷÷~øãožóÍÎ#¸DÅäT“Õà1„ %Þ”b;À¤Q³&ð€³M1qZÄeå€Åâ>ù–kb‹Cõ[èÒôT(XéH"‹Õ; ¯Ûè¦3©OTŸùtÃIC™©5mRúýŠr¾•Õš¡Õ}7]r|þçóðnZ.KT^ÕøŒ)e-ÉÐIέ"R§3»{úiGßx˵¡j÷ ^”‚à9 …Óä:3MY+šÒ#CÇTŵŒ%eSfgBëä2xëøÓNܾ»ƒw =« –È›Rñ±§çÊ~÷[ï½=íįw$“§µÆZá-dQë>lIkj¸ºD 8;3‘òAå‚‹IªIŒGeñ’L‘Ƨ5GºØ|t>e±†MÁåɱq´¼•ä‰â ¿o†‡{×J÷p)“K\f@RÁn‹cø¾ÄwOŠ88% †qØ– AèÌàŠÚt¢³¼šßºcñøá‘]»BÞºAµS–-Ù0ýk'•¸Ë¾mšÒ¹œÉE…ï¹&0}ÀëFêE§1RÓͰ¢6ŒñäSs“©h}ýÈ~vííwܾuûæt&‰{° ™=2ÉÀDD£V•U44Œzøá‡zèÈœMßqç/~oúôã"‘Žp‰'™ÊÕ ®Ù±së}÷Ý»hÑ[àjgœqÚŸîúã¬ßþáw܆ŠpOÇbˆv"fréx*’ŠbqÚ,ª^ —HÅÝ>'ŠcoØ´S°£qkIYØádcñNT±xñ¥¬]·zøˆ!¢“÷xå»gßùáÒÕº 5žT‘[¿aÍœîû÷{ÿÂÉp‰oËæõO=ùÂÄIc>^³òÖÛî¼â¿.TWéõ0™l|ãÆõí‘TI©ՄኃjY$” ³Ø|àãÔY” DiASŠPMøh\Ê)­!m€FC]åõ¸„[,OöùÛs ÂŽá B Ãöå6·E}X/þƒE9M­<ï‚Âi<î„ݙޤJJ´=*3µkWkj[ôüFxÛŽT[áö‚Öf²!;P©0£¡œNƒS’95ƒúm¢Kt<¡Ic'×UÕ¯Y^Z¥fôÓN>#ËøÜË~pù؆ñ ÃÇ ª\W98( yÂõ#Ç\þ¦ÆÖáƒF¸e6ž»ð?¿7¬vxëÕuß:ó,öàBA_ÐëNÄ£?»öÚ²ÒàˆÁƒ+Pä=“ùæÙߘ2qbýÈ!@¤O8nª`Y`ô„1Ò`¨<®ûKüõãê«×Ô­;iŠB–×Tà’¿$0ᨉeåáù/þ£²¬äø¯O›0vôØÑõÀÓ0jü˜†ºÚêáCêÕT¹JÃÁïœ7cÔ°!“FnmjDšÿI'N¯1¬¥¹qèàA×^}%Bµª9é„éˆË2‘¤faCƒ]‚³R© m­¸tHô?ëÐxâ^µ…L ¼‰ e5ä$ØÇ¹-ËWlÿdÛôÿ¸ð£µŸ˜þ`yum0Dtb;€ „÷ Cÿµ|ÅHÅ0fßÚ#G?7o—šƒeRec/.økm•Û­ªK7­ÜZzÒY1ZÓðšÀ×ÇŽ‡Gµ)ÓÁy~?›³â)Äjˆ.ÆpÁ2oÛ¼ 5 }|4ª"‚QkX¨%qiœèt:«;¦†~œJÀÂÊ|R*££ð-ñ%Á{Á±^7‹žx4UVáÉ!ØÙÅÄcY ¹(ð0CÇ”\ îH‘Ãꖲәɪnkþ¥‚~ONn ¹nDÿ#Lµ¤$ ]À±] Ò‰bxðôÁl—@… EGMDN …$ŽK4/‡æè”•…âñ´ßïÆÀ y\näºa ¤ä‘€MM­H‘# Ííø¼è‰t Ž—Ë‹À®|íÞ^#Aú¯Z¼ÓŽ-Ð@ÈY]°4ë,ÔYAV?ÌÉÐŽµ4ï4͹ÿÝ_½ù™y>õ¤£ºò¨)Sézdˆ•@X3 —! 1X•1ßݵ¾¦%iÜp?ŸUÆ.˜ÓäÈúE9ƒB= ³zõj%•ª««W­ÐŽÍ·cò„±2l®Œ ʈ@ÜÔð¾¶³œ°Ÿn 4Z¶´Ü"€0"ü‰PSp2ˆj¢ÒôÏøAsP~Óߒ툕?ɇIÛN÷AÑP®ºO ²ï£Ó3°8™b¬‘=D€ˆ%1Ë…¤$Ï ÉEH¦…LŽñ–̹ÿõ×_¿ùÿ=ñØÓ¡À”iÇ6ÂÆÀœ !¶qGú›;VŽO;Õ+æ°©ÌЗïHÉ+ˆé@yvË–­X\·Ož:ïÙÇ oâ±Ç>ŠàÄ û~BÊ .8ô…+Þ;–/I^ý€#‘)}â¦ÆÃdM“Ó˜œ‡Cl¦È CH*\X$m?8°4 ù…Ì’^HGWˆÙþËñökš>·›÷_RûsÒÿÎ#i«+ÍĤÛ+uaCü|Î`:[æ>:ÿßo_ÿÌ#/¾ð4_Y6dÂQutÛ’º'x˜3—}¼dTVh¾aNãêuÌ™›e-ÕÚéq °'ê¹4Áix‚móÁÚœ¾ûÇ^&›ÒõL€ˆ]çI¶M×Fp³×š/ÏW<@Ií/ÏK|ñGJÇ`Ö_Ê›Ès×lYÕ+H!§»­£½MK^õ·ûž~îq®¬lÌ1_ÏãÒ‰¸L"{ýxç¶M¡öŒöè[»V¯ÛìÊe}°ã!ùÊJÆÛ¡ä¡¤9’åèpŽ|EÔY@øÀ~"à{Ÿ¸î8PÀ„î°Þ]I ¬ä`Å]¾àß§ˆŸÁbñiÀÂC(?dzÄN€p jæTÑb9Ù©y¤‹¯»òÞæ•6füÔ¡Ã |rÁR:ä_o¿1ÒªrÕ2îãÙ'4šélgJƒL6Ï´Â9dc‘ü§{Þëv°àøR½nMÏð2º”Ø—v+ÊBŸÍ§#¾bB3‰•$;Â`BíÞ$¨È‘eBî»gÿÁURrÌ”éÆÚ8@¨2È:ä Ü ÖJii1§Ê¬—É ·P%!07b˜¬ŠÒ ËH’'¹ÁHGÄ:HvÆÉ~q`ÿv˜= € È&Ã` ýc0`1vu &ËÏfÐSºóº#ôˆb·€c;èÇ® BR± Û’¤¬”*.A[0LQˆʽsÿVRQ}ì”c‡¥v!ä› LÚ3 ¼&¨ú€=¶Ærެ™VÒP,E“Ë"RQH¨×B”i¬÷‚Ú:šŠ Ô},|ÈBפ%™Î½¶döU”IAë’=4gÅ| $î(ù X¹ßÜÿä*R{¾ #ù²Ïä` 5XÈ#†mÊÛ‰€ÀVIŒ:¾YÑ«´$ÌyôQÑ-ŸxÜ ]þ8ì‰M‰à€sð9ÛY±äCül0"K:2²cxƒY ˜@qPkÊslЇcz^S3¾€/LËnë-[±ìì³Nom”•„0J°©˜ƒ¿{äÓ“q°¿à Ž»ÿ#ØcãPßÿu¿ŸÓ¶Ðoc§àìµw]ÕéyÂuí£}GbœüØ‘_×Qîÿ}àÜ÷‰¤¼íIéþDÚÇáz.í­Ç,áŒì”÷þ&ö Ϭœæ~üC ~IçŠö@[C3hoôÞ”¶xßî÷Ócxx‡ÞmE‚a$Q†4Ð÷9Ýà'IbÀÎNA”Lž½ãªø‰÷Ô3ÄâÞ=ÆIG‹ ŠH€w!¨€c„O鄱î[ OÇG#«YI“±ÁSÒm<¸Š3´íï8÷W{>WIá‚n³]˜sŒ„ž§ÏížË?z†ûë‡Îçž/¾÷¨úþÄÂ{õ˜m Wx:Û´Ýw>»ÞšõúK·nùtXCýÊ5kêwïlRSCä|¨“pÁŠvHFür{úÕ¼Ieâ1(À—&¬'¿ánê˜ÂÓè!A;Ú€Æû©y>^E«ŽÖ–}ìÚÝjé d'àÀ’©0!І¬ø%ËX‹‡ÙèríO¡+˜S wÒ (vltÙí¾otqz÷ƒ¼ÎM½rþ»Üsû! !‚«§½Qݽ` Æß‚{ûpÙv©ŸdßWèþ¬î>õÃõ\º ľ¦k..õ¸§ïߥ`;îÑC>v¦ëÑ…ïý~ËåBe­ÁÒÜö»ÕhÕLHAÖR;o`j0«/[¹tÉô“§Nq^ÞB - B 0E [ɶëFéÈŽ!;A+[NJ@C® ÌGyzš§ 4‹Ûí™DdtU\D ˆ>á€ým÷ô°DfQž¨?‚áº#ÀsMw»}â­™DÊ 8P@Š´Ã—]hß qVû>”Ô´„áîplˆ)ìÇ=:È;`½¾Q»fÃ>›å!ù°¤"nÖ@afD#' ¦yew ?.Q™ÃÍðÑææ·¼~ÔQ“ºtb|¾®7Ê[2)Мc€+þ´ã/laOÄWa%O÷"Q`Ë6$‰NRd×–™ ,¡ •b„Ó·x/“TÂCZ(‘VºÛŸèù¾ÇœÁÑ×§û‰p PFeŒÞoc€-½ü  òœ ïcëË”»t£¼ùÙ°Mùy(܃ñ@{êÓûlñÕlÖH¬ø 6³Žaeõü\ÙD L4 ƒõ¹×w„ù°·þá¥KC¢–³m*GáòÚa$ÄFÉb@¼®Z²×­ÂIÜ• J!ÈEÔ9”І&Æ‹(ëDrÓÚõ¢Ë=t¸7ŠX jÂFéþ$¹3¶B€ªÒè1_2ýºÇòû!ö"cä¥*0L¬\D½½™Œ*ˤìÜAE¡Ž×…¥}ÿÉá ê£;·(ð ÔíÍc;­W–g ýÀÒcDåôÜ΋ݟeÇÚ’{t$“ô÷÷t@…{Š´µqÀ–IÆ Á £ ôj_°ºð. VÓy±”^â+þIeûíö¹?²ëElGÑTNU½D@©)¤9ÔU!_D•PÙVcµxjKssíHâøÿÆ_’s4*IEND®B`‚pcm-202307/docker-compose.yml000066400000000000000000000006761445420033100160320ustar00rootroot00000000000000version: '2.0' services: pcm: image: opcm/pcm ports: - "9738:9738" volumes: - "/sys/firmware/acpi/tables/MCFG:/pcm/sys/firmware/acpi/tables/MCFG:ro" - "/proc/bus/pci/:/pcm/proc/bus/pci/" - "/proc/sys/kernel/nmi_watchdog:/pcm/proc/sys/kernel/nmi_watchdog" cap_add: - SYS_ADMIN - SYS_RAWIO devices: - /dev/cpu - /dev/mem pcm-202307/examples/000077500000000000000000000000001445420033100142025ustar00rootroot00000000000000pcm-202307/examples/CMakeLists.txt000066400000000000000000000016251445420033100167460ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2022, Intel Corporation set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples) # Linux examples if(UNIX) # Example part file(GLOB EXAMPLE_FILE c_example.c) # create static lib example add_executable(c_example ${EXAMPLE_FILE}) add_dependencies(c_example PCM_SHARED) set_target_properties(c_example PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH_USE_LINK_PATH TRUE INSTALL_RPATH "${CMAKE_BINARY_DIR}/lib:${INSTALL_RPATH}") # rpath to libpcm.so target_compile_definitions(c_example PRIVATE PCM_DYNAMIC_LIB) # -DPCM_DYNAMIC_LIB target_link_libraries(c_example PRIVATE Threads::Threads dl) # -pthread -ldl # create shared lib example add_executable(c_example_shlib ${EXAMPLE_FILE}) target_link_libraries(c_example_shlib PUBLIC PCM_SHARED PRIVATE Threads::Threads) endif(UNIX)pcm-202307/examples/c_example.c000066400000000000000000000072511445420033100163100ustar00rootroot00000000000000#include #include #include #include int pcm_getcpu() { int id = -1; asm volatile ( "rdtscp\n\t" "mov %%ecx, %0\n\t": "=r" (id) :: "%rax", "%rcx", "%rdx"); // processor ID is in ECX: https://www.felixcloutier.com/x86/rdtscp // Linux encodes the NUMA node starting at bit 12, so remove the NUMA // bits when returning the CPU integer by masking with 0xFFF. return id & 0xFFF; } struct { int (*pcm_c_build_core_event)(uint8_t id, const char * argv); int (*pcm_c_init)(); void (*pcm_c_start)(); void (*pcm_c_stop)(); uint64_t (*pcm_c_get_cycles)(uint32_t core_id); uint64_t (*pcm_c_get_instr)(uint32_t core_id); uint64_t (*pcm_c_get_core_event)(uint32_t core_id, uint32_t event_id); } PCM; // lgtm [cpp/short-global-name] #ifndef PCM_DYNAMIC_LIB /* Library functions declaration (instead of .h file) */ int pcm_c_build_core_event(uint8_t, const char *); int pcm_c_init(); void pcm_c_start(); void pcm_c_stop(); uint64_t pcm_c_get_cycles(uint32_t); uint64_t pcm_c_get_instr(uint32_t); uint64_t pcm_c_get_core_event(uint32_t, uint32_t); #endif int main(int argc, const char *argv[]) { int i,a[100],b[100],c[100]; uint32_t total = 0; int lcore_id; int numEvents = argc - 1; /* Seed for predictable rand() results */ srand(0); for (i=0; i < 100; ++i) { a[i] = rand(); b[i] = rand(); c[i] = rand(); } #ifdef PCM_DYNAMIC_LIB void * handle = dlopen("libpcm.so", RTLD_LAZY); if(!handle) { printf("Abort: could not (dynamically) load shared library \n"); return -1; } PCM.pcm_c_build_core_event = (int (*)(uint8_t, const char *)) dlsym(handle, "pcm_c_build_core_event"); PCM.pcm_c_init = (int (*)()) dlsym(handle, "pcm_c_init"); PCM.pcm_c_start = (void (*)()) dlsym(handle, "pcm_c_start"); PCM.pcm_c_stop = (void (*)()) dlsym(handle, "pcm_c_stop"); PCM.pcm_c_get_cycles = (uint64_t (*)(uint32_t)) dlsym(handle, "pcm_c_get_cycles"); PCM.pcm_c_get_instr = (uint64_t (*)(uint32_t)) dlsym(handle, "pcm_c_get_instr"); PCM.pcm_c_get_core_event = (uint64_t (*)(uint32_t,uint32_t)) dlsym(handle, "pcm_c_get_core_event"); #else PCM.pcm_c_build_core_event = pcm_c_build_core_event; PCM.pcm_c_init = pcm_c_init; PCM.pcm_c_start = pcm_c_start; PCM.pcm_c_stop = pcm_c_stop; PCM.pcm_c_get_cycles = pcm_c_get_cycles; PCM.pcm_c_get_instr = pcm_c_get_instr; PCM.pcm_c_get_core_event = pcm_c_get_core_event; #endif if(PCM.pcm_c_init == NULL || PCM.pcm_c_start == NULL || PCM.pcm_c_stop == NULL || PCM.pcm_c_get_cycles == NULL || PCM.pcm_c_get_instr == NULL || PCM.pcm_c_build_core_event == NULL || PCM.pcm_c_get_core_event == NULL) return -1; if (numEvents > 4) { printf("Number of arguments are too many! exit...\n"); return -2; } for (i = 0; i < numEvents; ++i) { PCM.pcm_c_build_core_event(i, argv[i+1]); } printf("[c_example] Initializing PCM measurements:\n"); PCM.pcm_c_init(); printf("[c_example] Calling PCM start()\n"); PCM.pcm_c_start(); for(i=0;i<10000;i++) c[i%100] = 4 * a[i%100] + b[i%100]; for(i=0;i<100;i++) total += c[i]; PCM.pcm_c_stop(); printf("[c_example] PCM measurement stopped, compute result %u\n", total); lcore_id = pcm_getcpu(); printf("C:%llu I:%llu, IPC:%3.2f\n", (unsigned long long)PCM.pcm_c_get_cycles(lcore_id), (unsigned long long)PCM.pcm_c_get_instr(lcore_id), (double)PCM.pcm_c_get_instr(lcore_id)/PCM.pcm_c_get_cycles(lcore_id)); printf("CPU%d E0: %llu, E1: %llu, E2: %llu, E3: %llu\n", lcore_id, (unsigned long long)PCM.pcm_c_get_core_event(lcore_id,0), (unsigned long long)PCM.pcm_c_get_core_event(lcore_id,1), (unsigned long long)PCM.pcm_c_get_core_event(lcore_id,2), (unsigned long long)PCM.pcm_c_get_core_event(lcore_id,3)); return 0; } pcm-202307/pcm.spec000066400000000000000000000054201445420033100140200ustar00rootroot00000000000000 Name: pcm Version: 0 Release: 0 Summary: Intel(r) Performance Counter Monitor Group: System/Monitoring License: BSD-3-Clause Url: https://github.com/opcm/pcm/archive Source: master.zip BuildRoot: %{_tmppath}/%{name}-%{version}-build AutoReqProv: on BuildRequires: unzip BuildRequires: gcc BuildRequires: make BuildRequires: gcc-c++ BuildRequires: cmake %description Intel(r) Performance Counter Monitor (Intel(r) PCM) is an application programming interface (API) and a set of tools based on the API to monitor performance and energy metrics of Intel(r) Core(tm), Xeon(r), Atom(tm) and Xeon Phi(tm) processors. PCM works on Linux, Windows, Mac OS X, FreeBSD and DragonFlyBSD operating systems. %prep %setup -n pcm-master %build mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX=/usr/ -DCMAKE_BUILD_TYPE=RelWithDebInfo .. make -j %install rm -rf $RPM_BUILD_ROOT cd build make install DESTDIR=$RPM_BUILD_ROOT %clean rm -rf $RPM_BUILD_ROOT %post %postun %files %defattr(-,root,root,0755) %doc LICENSE doc/LINUX_HOWTO.txt /usr/share/doc/PCM /usr/share/licenses/pcm /usr/share/doc/PCM/CUSTOM-COMPILE-OPTIONS.md /usr/share/doc/PCM/DOCKER_README.md /usr/share/doc/PCM/ENVVAR_README.md /usr/share/doc/PCM/FAQ.md /usr/share/doc/PCM/FREEBSD_HOWTO.txt /usr/share/doc/PCM/LINUX_HOWTO.txt /usr/share/doc/PCM/MAC_HOWTO.txt /usr/share/doc/PCM/PCM-EXPORTER.md /usr/share/doc/PCM/PCM-SENSOR-SERVER-README.md /usr/share/doc/PCM/PCM_RAW_README.md /usr/share/doc/PCM/README.md /usr/share/doc/PCM/WINDOWS_HOWTO.md /usr/share/doc/PCM/license.txt /usr/share/licenses/pcm/LICENSE %{_sbindir}/pcm-core %{_sbindir}/pcm-iio %{_sbindir}/pcm-latency %{_sbindir}/pcm-lspci %{_sbindir}/pcm-memory %{_sbindir}/pcm-msr %{_sbindir}/pcm-mmio %{_sbindir}/pcm-numa %{_sbindir}/pcm-pcicfg %{_sbindir}/pcm-accel %{_sbindir}/pcm-pcie %{_sbindir}/pcm-power %{_sbindir}/pcm-sensor %{_sbindir}/pcm-sensor-server %{_sbindir}/pcm-tsx %{_sbindir}/pcm-raw %{_sbindir}/pcm %{_bindir}/pcm-client %{_sbindir}/pcm-daemon %{_sbindir}/pcm-bw-histogram %{_datadir}/pcm/ %changelog * Mon Feb 28 2022 - roman.dementiev@intel.com add addition doc files * Tue Jan 04 2022 - maria.markova@intel.com Add cmake adaptation * Fri Dec 17 2021 - maria.markova@intel.com Move licence.txt Linix_HOWTO.txt to doc folder * Tue Aug 25 2020 - roman.dementiev@intel.com Add pcm-raw under %files * Wed Apr 01 2020 - otto.g.bruggeman@intel.com Add pcm-sensor-server under %files * Mon Nov 25 2019 - roman.dementiev@intel.com call make install and use _sbindir or _bindir * Mon Oct 21 2019 - roman.dementiev@intel.com add opCode file to /usr/share/pcm use "install" to copy pcm-bw-histogram.sh * Fri Oct 18 2019 - roman.dementiev@intel.com created spec file pcm-202307/scripts/000077500000000000000000000000001445420033100140535ustar00rootroot00000000000000pcm-202307/scripts/convert_edp_list.sh000066400000000000000000000001151445420033100177470ustar00rootroot00000000000000 cat "$1" | sed '0,/^#.*[gG][rR][oO][uU][pP]/d' | head -n -1 | dos2unix pcm-202307/scripts/cppcheck.sh000077500000000000000000000003601445420033100161710ustar00rootroot00000000000000 cppcheck $1 --force --enable=warning --inline-suppr -iPCMService.cpp -isimdjson -iPcmMsrDriver_info.c -igoogletest -DTEXT -j $2 2> cppcheck.out if [ -s cppcheck.out ] then cat cppcheck.out exit 1 fi echo No issues found pcm-202307/scripts/find_field.awk000066400000000000000000000001461445420033100166430ustar00rootroot00000000000000#!/bin/awk -f { for(i=1; i<=NF; i++) { if (index($i, term) > 0) print (i)":"$i; } }pcm-202307/scripts/find_field.sh000066400000000000000000000000731445420033100164720ustar00rootroot00000000000000 head -1 $1 | awk -F ',' -v term="$2" -f find_field.awk pcm-202307/scripts/generate_summary.py000066400000000000000000000200211445420033100177670ustar00rootroot00000000000000#SPDX-License-Identifier: BSD-3-Clause #Copyright (c) 2023, Intel Corporation import pandas as pd import io import matplotlib from matplotlib import pyplot as plt import numpy as np from xlsxwriter import Workbook import xlsxwriter from datetime import datetime import sys if len(sys.argv) < 2: print("Error: Please input a valid csv filename in the command arguments") exit() try: df = pd.read_csv(sys.argv[1],header=[0,1]) except: arg = sys.argv[1] if arg == "--help" or arg == "-h": print("python3 -{arguments}") print("Following is the accepted list of arguments:") print("sys_exec_time : System Instructions per nominal CPU cycle vs System Time") print("cpu_util_time : Socket Instructions per nominal CPU cycle vs System Time") print("mem_traffic_time : Memory Bandwidth per socket vs System Time") print("cpu_util_socket : Instructions per nominal CPU cycle vs Socket") print("Mem_traffic_time : Average Memory Bandwidth per socket vs Socket") print("Note: If no arguments are passed, the default is to include everything above.") else: print("Error: Please input a avalid csv filename in the command arguments") exit() n = len(sys.argv) arguments=[] for i in range(2, n): arg = sys.argv[i] arg = arg.split('-') arguments.append(arg[1]) n = len(arguments) headers = df.columns columns=[] for title in headers: columns.append(title[0]+' '+title[1]) df.columns = columns flag = True count = 0 columns_added = [] while flag == True: column_name = 'Socket ' + str(count) + ' WRITE' if column_name in df.columns: insert_at_index = df.columns.get_loc(column_name) + 1 columns_added.append(insert_at_index) df.insert (insert_at_index, 'Socket ' + str(count) + ' mem traffic', (df['Socket ' + str(count) + ' READ'] * df['Socket ' + str(count) + ' WRITE'])/950) count = count + 1 else: count = count - 1 break try: system_time = df['System Time'] updated_time_format = [] for i in system_time: format = '%H:%M:%S' time_strip = i.split('.')[0] my_date = datetime.strptime(time_strip, format) updated_time_format.append(my_date.strftime("%H:%M:%S %p")) df['System Time'] = updated_time_format except: print("Generating consolidated report...") # 'System Exec' data={'System Time': df['System Time'], 'System Exec': df['System EXEC']} df1 = pd.DataFrame(data) # 'CPU utilization per socket (NUMA region)' data={} data['System Time'] = df['System Time'] for i in range(0,count+1): key = 'Socket ' + str(i) + ' EXEC' data[key] = df[key] df2 = pd.DataFrame(data) # 'Memory Bandwidth per socket (NUMA region)' data={} data['System Time'] = df['System Time'] for i in range(0,count+1): key = 'Socket ' + str(i) + ' mem traffic' data[key] = df[key] df3 = pd.DataFrame(data) # 'Average CPU utilization (extended instructions)' data={} data['System'] = df['System EXEC'].mean()*100 for i in range(0,count+1): key = 'Socket ' + str(i) + ' EXEC' data['Socket ' + str(i)] = df[key].mean()*100 df4 = pd.DataFrame(data,index=[1]) df4=df4.T # 'Average Memory Bandwidth per socket (NUMA region)' data={} for i in range(0,count+1): key = 'Socket ' + str(i) + ' mem traffic' data['Socket ' + str(i)] = df[key].mean()*100 df5 = pd.DataFrame(data,index=[1]) df5=df5.T writer = pd.ExcelWriter('consolidated_summary.xlsx', engine='xlsxwriter') sheet_name = 'consolidated' df.to_excel(writer, sheet_name=sheet_name) workbook = writer.book worksheet = writer.sheets[sheet_name] cell_format = workbook.add_format() cell_format.set_bg_color('yellow') cell_format.set_bold() for index in columns_added: excel_column = '' + xlsxwriter.utility.xl_col_to_name(index+1)+':'+xlsxwriter.utility.xl_col_to_name(index+1) worksheet.set_column(excel_column, None, cell_format) # ---------------------------------------------------------------------------------------- if "sys_exec_time" in arguments or n == 0: sheet_name = 'System EXEC' df1.to_excel(writer, sheet_name=sheet_name) workbook = writer.book worksheet = writer.sheets[sheet_name] (max_row, max_col) = df1.shape chart1 = workbook.add_chart({'type': 'line'}) chart1.add_series({ 'name': [sheet_name, 0, 2], 'categories': [sheet_name, 1, 1, max_row, 1], 'values': [sheet_name, 1, 2, max_row, 2], }) chart1.set_title ({'name': 'Instructions per nominal CPU cycle'}) chart1.set_x_axis({'name': 'System Time'}) chart1.set_y_axis({'name': 'System Instructions per nominal CPU cycle', 'major_gridlines': {'visible': False}}) worksheet.insert_chart(1, 6, chart1) # ---------------------------------------------------------------------------------------- if "cpu_util_time" in arguments or n == 0: sheet_name = 'CPU utilization per socket' df2.to_excel(writer, sheet_name=sheet_name) workbook = writer.book worksheet = writer.sheets[sheet_name] (max_row, max_col) = df2.shape chart2 = workbook.add_chart({'type': 'line'}) for i in range(0,max_col-1): col = i + 2 chart2.add_series({ 'name': [sheet_name, 0, col], 'categories': [sheet_name, 1, 1, max_row, 1], 'values': [sheet_name, 1, col, max_row, col], }) chart2.set_title ({'name': 'Instructions per nominal CPU cycle'}) chart2.set_x_axis({'name': 'System Time'}) chart2.set_y_axis({'name': 'Socket Instructions per nominal CPU cycle', 'major_gridlines': {'visible': False}}) worksheet.insert_chart(1, 6, chart2) # ---------------------------------------------------------------------------------------- if "mem_traffic_time" in arguments or n == 0: sheet_name = 'Memory Bandwidth per socket' df3.to_excel(writer, sheet_name=sheet_name) workbook = writer.book worksheet = writer.sheets[sheet_name] (max_row, max_col) = df3.shape chart3 = workbook.add_chart({'type': 'line'}) for i in range(0,max_col-1): col = i + 2 chart3.add_series({ 'name': [sheet_name, 0, col], 'categories': [sheet_name, 1, 1, max_row, 1], 'values': [sheet_name, 1, col, max_row, col], }) chart3.set_title ({'name': 'Memory Bandwidth per socket'}) chart3.set_x_axis({'name': 'System Time'}) chart3.set_y_axis({'name': 'Memory Traffic (GB)', 'major_gridlines': {'visible': False}}) worksheet.insert_chart(1, 6, chart3) # ---------------------------------------------------------------------------------------- if "cpu_util_socket" in arguments or n == 0: sheet_name = 'Average CPU Util' df4.to_excel(writer, sheet_name=sheet_name) workbook = writer.book worksheet = writer.sheets[sheet_name] (max_row, max_col) = df4.shape chart4 = workbook.add_chart({'type': 'column'}) chart4.add_series({ 'name': [sheet_name, 1, 0], 'categories': [sheet_name, 1, 0, max_row, 0], 'values': [sheet_name, 1, 1, max_row, 1], }) chart4.set_title ({'name': 'Instructions per nominal CPU cycle'}) chart4.set_x_axis({'name': 'Socket'}) chart4.set_y_axis({'name': 'Socket Instructions per nominal CPU cycle', 'major_gridlines': {'visible': False}}) worksheet.insert_chart(1, 6, chart4) # ---------------------------------------------------------------------------------------- if "Mem_traffic_time" in arguments or n == 0: sheet_name = 'Avg Memory Bandwidth per socket' df5.to_excel(writer, sheet_name=sheet_name) workbook = writer.book worksheet = writer.sheets[sheet_name] (max_row, max_col) = df4.shape chart5 = workbook.add_chart({'type': 'column'}) chart5.add_series({ 'name': [sheet_name, 1, 0], 'categories': [sheet_name, 1, 0, max_row, 0], 'values': [sheet_name, 1, 1, max_row, 1], }) chart5.set_title ({'name': 'Average Memory Bandwidth per socket'}) chart5.set_x_axis({'name': 'Socket'}) chart5.set_y_axis({'name': 'Memory Traffic (GB)', 'major_gridlines': {'visible': False}}) worksheet.insert_chart(1, 6, chart5) writer.close() pcm-202307/scripts/get_sles_bins.sh000066400000000000000000000004361445420033100172320ustar00rootroot00000000000000 filename=`curl https://download.opensuse.org/repositories/home:/opcm/SLE_15_SP1/x86_64/ -s | sed -n 's/.*\(pcm-0-[0-9]*\.1\.x86_64.rpm\).*/\1/p'` curl -L https://download.opensuse.org/repositories/home:/opcm/SLE_15_SP1/x86_64/$filename -o $filename rpm2cpio $filename | cpio -idmv pcm-202307/scripts/grafana/000077500000000000000000000000001445420033100154525ustar00rootroot00000000000000pcm-202307/scripts/grafana/README.md000066400000000000000000000044241445420033100167350ustar00rootroot00000000000000-------------------------------------------------------------------------------- Instructions on How To Run PCM Grafana Dashboard -------------------------------------------------------------------------------- Installation on target system to be analyzed: 1. Build from the root PCM directory: ``` cd ../.. # if the current location is 'scripts/grafana/' mkdir build && cd build cmake .. && make -j$(nproc) pcm-sensor-server ``` 2. As root start pcm-sensor-server: `cd bin && sudo ./pcm-sensor-server` Alternatively one can start [pcm-sensor-server as a container from docker hub](../../doc/DOCKER_README.md). Installation of the grafana front-end (can be on any *host* system with connectivity to the target system): 1. Make sure curl and docker are installed on the *host* 2. In PCM source directory on the *host*: `cd scripts/grafana` 3. (Download once and) start docker containers on the *host*: `sudo sh start.sh http://target_system_address:9738` - `start.sh` script starts telegraf/influxdb/grafana containers - `start-prometheus.sh` is an alternative script which starts prometheus + grafana containers: `sudo sh start-prometheus.sh target_system_address:9738` - `start.sh` and `start-prometheus.sh` can also be used to monitor multiple hosts running pcm-sensor-server containers:`sudo sh start.sh targets.txt` or `sudo sh start-prometheus.sh targets.txt`. Here `targets.txt` should be of the following format: ```properties host1_ipaddress:pcmport host2_ipaddress:pcmport . . hostn_ipaddress:pcmport ``` - Don't use `localhost` to specify the `target_system_address` if the *host* and the target are the same machine because `localhost` resolves to the own private IP address of the docker container when accessed inside the container. The external IP address or hostname should be used instead. 4. Start your browser at http://*host*:3000/ and then login with admin user, password admin . Change the password and then click on "**Home**" (left top corner) -> "Intel® Performance Counter Monitor (Intel® PCM) Dashboard" 5. You can also stop and delete the containers when needed: `sudo sh stop.sh` ![pcm grafana output](https://raw.githubusercontent.com/wiki/opcm/pcm/pcm-dashboard-full.png) pcm-202307/scripts/grafana/automatic_influxdb.yml000066400000000000000000000006651445420033100220650ustar00rootroot00000000000000apiVersion: 1 datasources: - name: PCM type: influxdb access: proxy url: "http://influxdb:8086" password: user: database: telegraf basicAuth: false basicAuthUser: basicAuthPassword: withCredentials: isDefault: true jsonData: tlsAuth: false tlsAuthWithCACert: false timeInterval: "2s" secureJsonData: tlsCACert: "" tlsClientCert: "" tlsClientKey: "" version: 1 editable: true pcm-202307/scripts/grafana/automatic_prometheus.yml000066400000000000000000000003541445420033100224400ustar00rootroot00000000000000apiVersion: 1 datasources: - name: PCM type: prometheus access: proxy url: "http://prometheus:9090" password: user: database: basicAuth: false isDefault: true editable: true jsonData: readOnly: false version: 1 pcm-202307/scripts/grafana/prometheus.yml.template000066400000000000000000000016261445420033100222070ustar00rootroot00000000000000# my global config global: scrape_interval: 2s # Set the scrape interval to every 15 seconds. Default is every 1 minute. evaluation_interval: 2s # Evaluate rules every 15 seconds. The default is every 1 minute. # scrape_timeout is set to the global default (10s). # Alertmanager configuration alerting: alertmanagers: - static_configs: - targets: # - alertmanager:9093 # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. rule_files: # - "first_rules.yml" # - "second_rules.yml" # A scrape configuration containing exactly one endpoint to scrape: # Here it's Prometheus itself. scrape_configs: # The job name is added as a label `job=` to any timeseries scraped from this config. - job_name: 'pcm' # metrics_path defaults to '/metrics' # scheme defaults to 'http'. static_configs: - targets: ['PCMSENSORSERVER'] pcm-202307/scripts/grafana/provisioning/000077500000000000000000000000001445420033100202005ustar00rootroot00000000000000pcm-202307/scripts/grafana/provisioning/dashboards/000077500000000000000000000000001445420033100223125ustar00rootroot00000000000000pcm-202307/scripts/grafana/provisioning/dashboards/pcm-provider.yml000066400000000000000000000014451445420033100254500ustar00rootroot00000000000000apiVersion: 1 providers: # an unique provider name - name: 'default PCM dashboard provider' # org id. will default to orgId 1 if not specified orgId: 1 # name of the dashboard folder. Required folder: '' # folder UID. will be automatically generated if not specified folderUid: '' # provider type. Required type: file # disable dashboard deletion disableDeletion: false # enable dashboard editing editable: true # how often Grafana will scan for changed dashboards updateIntervalSeconds: 10 # allow updating provisioned dashboards from the UI allowUiUpdates: false options: # path to dashboard files on disk. Required path: /var/lib/grafana/dashboards pcm-202307/scripts/grafana/start-prometheus.sh000066400000000000000000000027311445420033100213370ustar00rootroot00000000000000#!/bin/sh if [ "$#" -ne 1 ]; then echo echo "Usage: $0 target_address:port" echo echo "target_address is the hostname or IP address of the system that runs pcm-sensor-server" exit 1 fi mkdir -p grafana_volume/dashboards mkdir -p prometheus_volume chmod -R 777 *_volume mkdir -p provisioning/datasources cp automatic_prometheus.yml provisioning/datasources/automatic.yml # check if argument is file, create the prometheus.yml accordingly if [ -f "$1" ]; then echo "creating prometheus.yml for hosts in targets file"; head -n -1 "prometheus.yml.template" > prometheus.yml while read -r line; do echo " - targets: ['$line']" >> "prometheus.yml" done < "$1" echo Downloading PCM dashboard curl -o grafana_volume/dashboards/pcm-dashboard.json $(head -1 $1)/dashboard/prometheus else echo "creating prometheus.yml for $1 "; sed "s#PCMSENSORSERVER#$1#g" prometheus.yml.template > prometheus.yml echo Downloading PCM dashboard curl -o grafana_volume/dashboards/pcm-dashboard.json $1/dashboard/prometheus fi echo Starting prometheus docker run --name prometheus -d -p 9090:9090 -v $PWD/prometheus.yml:/etc/prometheus/prometheus.yml -v $PWD/prometheus_volume:/prometheus prom/prometheus echo Starting grafana docker run -d --link=prometheus --name=grafana -p 3000:3000 -v $PWD/grafana_volume:/var/lib/grafana -v $PWD/provisioning:/etc/grafana/provisioning grafana/grafana echo Start browser at http://localhost:3000/ and login with admin user, password admin pcm-202307/scripts/grafana/start.sh000066400000000000000000000037321445420033100171500ustar00rootroot00000000000000#!/bin/sh if [ "$#" -ne 1 ]; then echo echo "Usage: $0 http(s)://target_address:port" echo echo "target_address is the hostname or IP address of the system that runs pcm-sensor-server" exit 1 fi # check if argument is file, create the telegraf.conf accordingly if [ -f "$1" ]; then echo "creating telegraf.conf for hosts in targets file"; head -n -7 "telegraf.conf.template" > telegraf.conf while IFS='' read -r line || [[ -n "$line" ]]; do # Split the line at the : character to get the IP and port ip=$(echo "$line" | cut -d ':' -f 1) port=$(echo "$line" | cut -d ':' -f 2) # Append the transformed line to the output file, separated by a comma echo -n "\"http://$ip:$port/persecond/\"," >> telegraf.conf done < $1 sed -i '$ s/,$//' telegraf.conf tail -n -6 "telegraf.conf.template" >> telegraf.conf echo Downloading PCM dashboard curl -o grafana_volume/dashboards/pcm-dashboard.json $(head -1 $1)/dashboard else echo "creating telegraf.conf for $1 "; sed "s#PCMSENSORSERVER#$1#g" telegraf.conf.template > telegraf.conf echo Downloading PCM dashboard curl -o grafana_volume/dashboards/pcm-dashboard.json $1/dashboard fi mkdir -p grafana_volume/dashboards mkdir -p influxdb_volume chmod -R 777 *_volume mkdir -p provisioning/datasources cp automatic_influxdb.yml provisioning/datasources/automatic.yml echo Downloading PCM dashboard curl -o grafana_volume/dashboards/pcm-dashboard.json $1/dashboard echo Starting influxdb docker run -d --name influxdb -p 8083:8083 -p 8086:8086 -v $PWD/influxdb_volume:/var/lib/influxdb influxdb:1.8.0-alpine echo Starting telegraf docker run -d --name telegraf --link=influxdb -v $PWD/telegraf.conf:/etc/telegraf/telegraf.conf:ro telegraf echo Starting grafana docker run -d --link=influxdb --name=grafana -p 3000:3000 -v $PWD/provisioning:/etc/grafana/provisioning -v $PWD/grafana_volume:/var/lib/grafana grafana/grafana echo Start browser at http://localhost:3000/ and login with admin user, password admin pcm-202307/scripts/grafana/stop.sh000066400000000000000000000003261445420033100167740ustar00rootroot00000000000000 for c in grafana telegraf influxdb prometheus; do id=`docker ps -a -q --filter="name=$c" --format="{{.ID}}"` if [ ! -z "$id" ] then echo Stopping and deleting $c docker rm $(docker stop $id) fi done pcm-202307/scripts/grafana/telegraf.conf.template000066400000000000000000000106721445420033100217320ustar00rootroot00000000000000# # Telegraf is entirely plugin driven. All metrics are gathered from the # declared inputs, and sent to the declared outputs. # # Plugins must be declared in here to be active. # To deactivate a plugin, comment out the name and any variables. # # Use 'telegraf -config telegraf.conf -test' to see what metrics a config # file would generate. # # Environment variables can be used anywhere in this config file, simply prepend # them with $. For strings the variable must be within quotes (ie, "$STR_VAR"), # for numbers and booleans they should be plain (ie, $INT_VAR, $BOOL_VAR) # Global tags can be specified here in key="value" format. [global_tags] # dc = "us-east-1" # will tag all metrics with dc=us-east-1 # rack = "1a" ## Environment variables can be used as tags, and throughout the config file # user = "$USER" # Configuration for telegraf agent [agent] ## Default data collection interval for all inputs # interval = "2s" ## Rounds collection interval to 'interval' ## ie, if interval="10s" then always collect on :00, :10, :20, etc. round_interval = false ## Telegraf will send metrics to outputs in batches of at ## most metric_batch_size metrics. metric_batch_size = 1000 ## For failed writes, telegraf will cache metric_buffer_limit metrics for each ## output, and will flush this buffer on a successful write. Oldest metrics ## are dropped first when this buffer fills. metric_buffer_limit = 10000 ## Collection jitter is used to jitter the collection by a random amount. ## Each plugin will sleep for a random time within jitter before collecting. ## This can be used to avoid many plugins querying things like sysfs at the ## same time, which can have a measurable effect on the system. collection_jitter = "0s" ## Default flushing interval for all outputs. You shouldn't set this below ## interval. Maximum flush_interval will be flush_interval + flush_jitter flush_interval = "10s" ## Jitter the flush interval by a random amount. This is primarily to avoid ## large write spikes for users running a large number of telegraf instances. ## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s flush_jitter = "0s" ## By default, precision will be set to the same timestamp order as the ## collection interval, with the maximum being 1s. ## Precision will NOT be used for service inputs, such as logparser and statsd. ## Valid values are "ns", "us" (or "µs"), "ms", "s". precision = "" ## Run telegraf in debug mode debug = false ## Run telegraf in quiet mode quiet = false ## Override default hostname, if empty use os.Hostname() ## hostname = "" ## If set to true, do no set the "host" tag in the telegraf agent. omit_hostname = false ############################################################################### # OUTPUT PLUGINS # ############################################################################### # Configuration for influxdb server to send metrics to [[outputs.influxdb]] ## The full HTTP or UDP endpoint URL for your InfluxDB instance. ## Multiple urls can be specified as part of the same cluster, ## this means that only ONE of the urls will be written to each interval. # urls = ["udp://localhost:8089"] # UDP endpoint example urls = ["http://influxdb:8086"] # required ## The target database for metrics (telegraf will create it if not exists). database = "telegraf" # required ## Retention policy to write to. Empty string writes to the default rp. retention_policy = "" ## Write consistency (clusters only), can be: "any", "one", "quorum", "all" write_consistency = "any" ## Write timeout (for the InfluxDB client), formatted as a string. ## If not provided, will default to 5s. 0s means no timeout (not recommended). timeout = "5s" # username = "telegraf" # password = "metricsmetricsmetricsmetrics" ## Set the user agent for HTTP POSTs (can be useful for log differentiation) # user_agent = "telegraf" ## Set UDP payload size, defaults to InfluxDB UDP Client default (512 bytes) # udp_payload = 512 ## Optional SSL Config # ssl_ca = "/etc/telegraf/ca.pem" # ssl_cert = "/etc/telegraf/cert.pem" # ssl_key = "/etc/telegraf/key.pem" ## Use SSL but skip chain & host verification # insecure_skip_verify = false [[inputs.http]] urls = [ "PCMSENSORSERVER/persecond/" ] interval = "2s" data_format = "json" headers = {"Accept" = "application/json"} pcm-202307/scripts/pcm-background.sh000066400000000000000000000000411445420033100172760ustar00rootroot00000000000000 pcm "$@" & echo "$!" > pcm.pid pcm-202307/scripts/pcm-exporter000066400000000000000000000000601445420033100164170ustar00rootroot00000000000000#!/bin/sh ../build/bin/pcm-sensor-server "$@" pcm-202307/scripts/pcm-stop.sh000066400000000000000000000000241445420033100161450ustar00rootroot00000000000000 kill `cat pcm.pid` pcm-202307/scripts/pcm.plot000066400000000000000000000006251445420033100155350ustar00rootroot00000000000000 set key autotitle columnhead set datafile separator "," # change as needed # set xlabel 'sample # (each is 1000ms)' set ylabel 'metric value' set terminal pdf set output "pcm.pdf" # change below as needed # plot metrics 3 .. 37 do for [m=3:37] { plot "single_header.pcm.csv" using m with dots } # plot metrics 84 .. 107 do for [m=84:107] { plot "single_header.pcm.csv" using m with dots } pcm-202307/scripts/pmu-query.py000066400000000000000000000116551445420033100164010ustar00rootroot00000000000000#!/usr/bin/env python3 import io import urllib.request import urllib.parse import json import csv # subprocess is used as multiplatform approach, usage is verified (20-07-2022) import subprocess # nosec import sys import platform import getopt import re import shutil all_flag = False download_flag = False filename = None offcore_events = [] core_events = [] try: opts, args = getopt.getopt(sys.argv[1:], "a,f:,d", ["all", "file=", "download"]) for o, a in opts: if o in ("-a", "--all"): all_flag = True if o in ("-f", "--file"): filename = a if o in ("-d", "--download"): download_flag = True except getopt.GetoptError as err: print("parse error: %s\n" % (str(err))) sys.exit(-2) if filename is None: # vefified that link to mapfile.csv is safe and correct (20-07-2022) map_file_raw = urllib.request.urlopen("https://raw.githubusercontent.com/intel/perfmon/main/mapfile.csv").read().decode('utf-8') # nosec map_dict = csv.DictReader(io.StringIO(map_file_raw), delimiter=',') map_file = [] core_path = "" offcore_path = "" while True: try: map_file.append(next(map_dict)) except StopIteration: break if platform.system() == "CYGWIN_NT-6.1": p = subprocess.Popen(["./pcm-core.exe -c"], stdout=subprocess.PIPE, shell=True) elif platform.system() == "Windows": p = subprocess.Popen(["pcm-core.exe", "-c"], stdout=subprocess.PIPE, shell=True) elif platform.system() == "Linux": pcm_core = shutil.which("pcm-core") if not pcm_core: print("Could not find pcm-core executable!") sys.exit(-1) p = subprocess.Popen([pcm_core, "-c"], stdout=subprocess.PIPE, shell=True) else: p = subprocess.Popen(["../build/bin/pcm-core -c"], stdout=subprocess.PIPE, shell=True) (output, err) = p.communicate() p_status = p.wait() for model in map_file: if re.search(model["Family-model"], output.decode("utf-8")): if model["EventType"] == "core": core_path = model["Filename"] elif model["EventType"] == "offcore": offcore_path = model["Filename"] print(model) if core_path: # vefified that links, created on base of map_file are correct (20-07-2022) json_core_data = urllib.request.urlopen( # nosec "https://raw.githubusercontent.com/intel/perfmon/main" + core_path ) core_events = json.load(json_core_data) if download_flag: with open(core_path.split("/")[-1], "w") as outfile: json.dump(core_events, outfile, sort_keys=True, indent=4) else: print("no core event found for %s CPU, program abort..." % output.decode("utf-8")) sys.exit(-1) if offcore_path: # vefified that links, created on base of map_file are correct (20-07-2022) json_offcore_data = urllib.request.urlopen( # nosec "https://raw.githubusercontent.com/intel/perfmon/main" + offcore_path ) offcore_events = json.load(json_offcore_data) if download_flag: with open(offcore_path.split("/")[-1], "w") as outfile: json.dump(offcore_events, outfile, sort_keys=True, indent=4) else: for f in filename.split(","): print(f) core_events.extend(json.load(open(f))) if all_flag: for event in core_events + offcore_events: if "EventName" in event and "BriefDescription" in event: print(event["EventName"] + ":" + event["BriefDescription"]) sys.exit(0) name = input("Event to query (empty enter to quit):") while name: for event in core_events + offcore_events: if "EventName" in event and name.lower() in event["EventName"].lower(): print(event["EventName"] + ":" + event["BriefDescription"]) for ev_code in event["EventCode"].split(", "): print( "cpu/umask=%s,event=%s,name=%s%s%s%s%s%s/" % ( event["UMask"], ev_code, event["EventName"], (",offcore_rsp=%s" % (event["MSRValue"])) if event["MSRValue"] != "0" else "", (",inv=%s" % (event["Invert"])) if event["Invert"] != "0" else "", (",any=%s" % (event["AnyThread"])) if event["AnyThread"] != "0" else "", (",edge=%s" % (event["EdgeDetect"])) if event["EdgeDetect"] != "0" else "", (",cmask=%s" % (event["CounterMask"])) if event["CounterMask"] != "0" else "", ) ) name = input("Event to query (empty enter to quit):") pcm-202307/scripts/readmem.sh000066400000000000000000000011471445420033100160240ustar00rootroot00000000000000 numactl --cpunodebind=0 --membind=0 ./readmem 10 & numactl --cpunodebind=1 --membind=1 ./readmem 10 & numactl --cpunodebind=0 --membind=0 ./readmem 10 & numactl --cpunodebind=1 --membind=1 ./readmem 10 & numactl --cpunodebind=0 --membind=0 ./readmem 10 & numactl --cpunodebind=1 --membind=1 ./readmem 10 & numactl --cpunodebind=0 --membind=0 ./readmem 10 & numactl --cpunodebind=1 --membind=1 ./readmem 10 & numactl --cpunodebind=0 --membind=0 ./readmem 10 & numactl --cpunodebind=1 --membind=1 ./readmem 10 & numactl --cpunodebind=0 --membind=0 ./readmem 10 & numactl --cpunodebind=1 --membind=1 ./readmem 10 & pcm-202307/scripts/single_header.awk000066400000000000000000000004631445420033100173530ustar00rootroot00000000000000BEGIN { line = 0; } { if (line == 0) { # print $0; for(i=1; i<=NF; i++) { first[i] = $i; } } else if (line == 1) { for(i=1; i<=NF; i++) { if ($i != "") printf first[i]" "$i"," } print "" } else { print $0 } line = line + 1; } pcm-202307/scripts/single_header.sh000066400000000000000000000000771445420033100172040ustar00rootroot00000000000000 cat $1 | awk -F ',' -f single_header.awk > single_header.$1 pcm-202307/src/000077500000000000000000000000001445420033100131535ustar00rootroot00000000000000pcm-202307/src/CMakeLists.txt000066400000000000000000000257621445420033100157270ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2022, Intel Corporation # All pcm-* executables set(PROJECT_NAMES pcm pcm-numa pcm-latency pcm-power pcm-msr pcm-memory pcm-tsx pcm-pcie pcm-core pcm-iio pcm-lspci pcm-pcicfg pcm-mmio pcm-raw pcm-accel) file(GLOB COMMON_SOURCES msr.cpp cpucounters.cpp pci.cpp mmio.cpp bw.cpp utils.cpp topology.cpp debug.cpp threadpool.cpp uncore_pmu_discovery.cpp) if (APPLE) file(GLOB UNUX_SOURCES dashboard.cpp) else() file(GLOB UNUX_SOURCES dashboard.cpp resctrl.cpp) endif() if(UNIX) # LINUX, FREE_BSD, APPLE if (NOT APPLE) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS} -s") # --strip-unneeded for packaging list(APPEND PROJECT_NAMES pcm-sensor-server) endif() list(APPEND PROJECT_NAMES pcm-sensor) # libpcm.a add_library(PCM_STATIC STATIC ${COMMON_SOURCES} ${UNUX_SOURCES}) set_target_properties(PCM_STATIC PROPERTIES OUTPUT_NAME pcm) # libpcm.a with -DPCM_SILENT for Release* add_library(PCM_STATIC_SILENT STATIC ${COMMON_SOURCES} ${UNUX_SOURCES}) target_compile_definitions(PCM_STATIC_SILENT PRIVATE $<$:PCM_SILENT> $<$:PCM_SILENT> $<$:PCM_SILENT> ) # libpcm.so add_library(PCM_SHARED SHARED pcm-core.cpp) # PCM_SILENT in Release* for pcm-core.cpp target_compile_definitions(PCM_SHARED PRIVATE $<$:PCM_SILENT> $<$:PCM_SILENT> $<$:PCM_SILENT> ) if(APPLE) add_subdirectory(MacMSRDriver) include_directories("${CMAKE_SOURCE_DIR}/src/MacMSRDriver") # target_include_directories doesn't work target_link_libraries(PCM_SHARED PRIVATE PCM_STATIC_SILENT PcmMsr Threads::Threads) else() target_link_libraries(PCM_SHARED PRIVATE PCM_STATIC_SILENT Threads::Threads) endif() set_target_properties(PCM_SHARED PROPERTIES OUTPUT_NAME pcm) endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2 /wd4251 /wd4273 /EHa /Zi") add_definitions(/W3) # https://cmake.org/cmake/help/latest/prop_tgt/MSVC_RUNTIME_LIBRARY.html # windows/restrictDriverAccess.cpp is building separately add_library(restrictDriverAccess OBJECT windows/restrictDriverAccess.cpp) set_target_properties(restrictDriverAccess PROPERTIES COMMON_LANGUAGE_RUNTIME "") target_compile_definitions(restrictDriverAccess PRIVATE _CONSOLE _UNICODE UNICODE) # Rest of windows/* files + restrictDriverAccess.cpp -> PCM_STATIC file(GLOB WINDOWS_SOURCES winpmem/winpmem.cpp windows/stdafx.cpp freegetopt/getopt.cpp) add_library(PCM_STATIC STATIC $ ${COMMON_SOURCES} ${WINDOWS_SOURCES}) target_compile_definitions(PCM_STATIC PRIVATE UNICODE _UNICODE _CONSOLE) # Graphical perfmon front-end: pcm-lib, pcm-service # Files: COMMON_FILES() + pcm-lib.cpp winpmem\winpmem.cpp dllmain.cpp file(GLOB PCM_LIB_SOURCES winpmem/winpmem.cpp dllmain.cpp pcm-lib.cpp ) add_library(pcm-lib SHARED $ ${COMMON_SOURCES} ${PCM_LIB_SOURCES}) target_compile_definitions(pcm-lib PRIVATE _WINDOWS _USRDLL PCM_EXPORTS _WINDLL _UNICODE UNICODE) # Pcm-service files: PCM_SHARED + AssemblyInfo.cpp PCMInstaller.cpp PCMService.cpp file(GLOB PCM_SERVICE_SOURCES windows/PCMInstaller.cpp windows/PCMService.cpp windows/AssemblyInfo.cpp winddows/utils.cpp) add_executable(pcm-service $ ${PCM_SERVICE_SOURCES}) target_compile_definitions(pcm-service PRIVATE _UNICODE UNICODE _CONSOLE) set_target_properties(pcm-service PROPERTIES LINK_FLAGS "/INCREMENTAL:NO" COMMON_LANGUAGE_RUNTIME "") set_property(TARGET pcm-service PROPERTY VS_DOTNET_REFERENCES "System;System.Configuration.Install;System.Data;System.Management;System.ServiceProcess;System.Xml") target_link_libraries(pcm-service pcm-lib) endif(MSVC) ####################### # SIMDJSON dependency ####################### add_library(PCM_SIMDJSON INTERFACE) # interface library for simdjson set(SIMDJSON_IS_APPLICABLE TRUE) # true if simdjson can be used, default - TRUE # check simdjson support matrix - https://github.com/simdjson/simdjson/blob/master/doc/basics.md # > GCC 7.4, > Clang 6.0 , > MSVC 2017 if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.4) OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6) OR (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_TOOLSET_VERSION VERSION_LESS 141)) # corresponds to VS2017 message(WARNING " ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_VERSION} is incompartible with simdjson features' requirements.\n" " Refer to simdjson support matrix - https://github.com/simdjson/simdjson/blob/master/doc/basics.md .\n" " Parsing events from https://github.com/intel/perfmon won't be supported.") set(SIMDJSON_IS_APPLICABLE FALSE) endif() if(SIMDJSON_IS_APPLICABLE) find_package(simdjson QUIET) # Working form Ububtu 22.04 if(simdjson_FOUND) message(STATUS "System SIMDJSON is used") target_link_libraries(PCM_SIMDJSON INTERFACE simdjson::simdjson) target_compile_definitions(PCM_SIMDJSON INTERFACE SYSTEM_SIMDJSON) target_compile_definitions(PCM_SIMDJSON INTERFACE PCM_SIMDJSON_AVAILABLE) else() if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/simdjson/singleheader/simdjson.h") message(STATUS "Local SIMDJSON exists: ${CMAKE_CURRENT_SOURCE_DIR}/simdjson/singleheader/simdjson.h") file(GLOB SIMDJSON_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/simdjson/singleheader/simdjson.cpp) target_sources(PCM_SIMDJSON INTERFACE ${SIMDJSON_SOURCE}) target_include_directories(PCM_SIMDJSON INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/simdjson/singleheader) target_compile_definitions(PCM_SIMDJSON INTERFACE PCM_SIMDJSON_AVAILABLE) else() message(WARNING " ${CMAKE_CURRENT_SOURCE_DIR}/simdjson/singleheader/simdjson.h doesn't exist\n" " Use `git clone --recursive` flag when cloning pcm repository to clone simdjson submodule as well or\n" " update submodule with command 'git submodule update --init --recursive' or\n" " run 'git clone https://github.com/simdjson/simdjson.git' in 'src' directory to get simdjson library") endif() endif(simdjson_FOUND) endif(SIMDJSON_IS_APPLICABLE) ####################### # End of SIMDJSON dependency section ####################### foreach(PROJECT_NAME ${PROJECT_NAMES}) file(GLOB PROJECT_FILE ${PROJECT_NAME}.cpp) set(LIBS PCM_STATIC) add_executable(${PROJECT_NAME} ${PROJECT_FILE}) # specific file for pcm-raw project if(${PROJECT_NAME} STREQUAL pcm-raw) set(LIBS ${LIBS} PCM_SIMDJSON) endif(${PROJECT_NAME} STREQUAL pcm-raw) if(${PROJECT_NAME} STREQUAL pcm-sensor-server) file(READ pcm-sensor-server.service.in SENSOR_SERVICE_IN) string(REPLACE "@@CMAKE_INSTALL_SBINDIR@@" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SBINDIR}" SENSOR_SERVICE "${SENSOR_SERVICE_IN}") file(WRITE "${CMAKE_BINARY_DIR}/pcm-sensor-server.service" "${SENSOR_SERVICE}") if(LINUX_SYSTEMD) install(FILES "${CMAKE_BINARY_DIR}/pcm-sensor-server.service" DESTINATION "${LINUX_SYSTEMD_UNITDIR}") endif(LINUX_SYSTEMD) endif(${PROJECT_NAME} STREQUAL pcm-sensor-server) if(LINUX OR FREE_BSD) set(LIBS ${LIBS} Threads::Threads) install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_SBINDIR}) endif(LINUX OR FREE_BSD) if(APPLE) set(LIBS ${LIBS} Threads::Threads PcmMsr) install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_SBINDIR}) endif(APPLE) if(MSVC) target_compile_definitions(${PROJECT_NAME} PRIVATE _UNICODE UNICODE _CONSOLE) # for all, except pcm-lib and pcm-service endif(MSVC) target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS}) endforeach(PROJECT_NAME ${PROJECT_NAMES}) ####################### # Install ####################### if(UNIX) # APPLE, LINUX, FREE_BSD if(LINUX) # Daemon & client file(GLOB DAEMON_SOURCES "daemon/*.cpp") add_executable(daemon ${DAEMON_SOURCES}) target_link_libraries(daemon PRIVATE PCM_STATIC Threads::Threads) set_target_properties(daemon PROPERTIES OUTPUT_NAME "pcm-daemon") install(TARGETS daemon DESTINATION ${CMAKE_INSTALL_SBINDIR}) file(GLOB CLIENT_SOURCES "client/*.cpp") add_executable(client ${CLIENT_SOURCES}) target_link_libraries(client PRIVATE Threads::Threads) set_target_properties(client PROPERTIES OUTPUT_NAME "pcm-client") install(TARGETS client DESTINATION ${CMAKE_INSTALL_BINDIR}) endif(LINUX) # Install extra files install(FILES pcm-bw-histogram.sh DESTINATION ${CMAKE_INSTALL_SBINDIR} RENAME pcm-bw-histogram PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) file(GLOB OPCODE_FILES "opCode*.txt") foreach(opcode_file ${OPCODE_FILES}) get_filename_component(opcode_file_name ${opcode_file} NAME) configure_file(${opcode_file} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${opcode_file_name} COPYONLY) install(FILES ${opcode_file} DESTINATION ${CMAKE_INSTALL_DATADIR}/pcm) endforeach(opcode_file ${OPCODE_FILES}) file(COPY "PMURegisterDeclarations" DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) install(DIRECTORY "PMURegisterDeclarations" DESTINATION ${CMAKE_INSTALL_DATADIR}/pcm) # Install docs install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/pcm) install(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) file(GLOB DOC_FILES ${CMAKE_SOURCE_DIR}/doc/*.txt ${CMAKE_SOURCE_DIR}/doc/*.md) foreach(doc_file ${DOC_FILES}) get_filename_component(doc_file_name ${doc_file} NAME) configure_file(${doc_file} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${doc_file_name} COPYONLY) install(FILES ${doc_file} DESTINATION ${CMAKE_INSTALL_DOCDIR}) endforeach(doc_file ${DOC_FILES}) endif(UNIX) if(MSVC) file(GLOB OPCODE_FILES "opCode*.txt") foreach(opcode_file ${OPCODE_FILES}) add_custom_command(TARGET pcm-iio POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${opcode_file} $) endforeach(opcode_file ${OPCODE_FILES}) add_custom_command(TARGET pcm-raw POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "$/PMURegisterDeclarations") add_custom_command(TARGET pcm-raw POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/src/PMURegisterDeclarations" "$/PMURegisterDeclarations") add_custom_command(TARGET pcm-service POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/src/windows/pcm-service.exe.config" $) endif(MSVC) pcm-202307/src/MacMSRDriver/000077500000000000000000000000001445420033100154115ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/CMakeLists.txt000066400000000000000000000007431445420033100201550ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2022, Intel Corporation set(CMAKE_MACOSX_RPATH 1) set(CMAKE_CXX_FLAGS "-Wall") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") file(GLOB LIB_FILES PCIDriverInterface.cpp MSRAccessor.cpp) find_library(IOKIT_LIBRARY IOKit) add_library(PcmMsr SHARED ${LIB_FILES}) target_link_libraries(PcmMsr PRIVATE ${IOKIT_LIBRARY}) add_subdirectory(PcmMsr) # Installation install(TARGETS PcmMsr DESTINATION "lib") pcm-202307/src/MacMSRDriver/MSRAccessor.cpp000066400000000000000000000117611445420033100202470ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #include "MSRAccessor.h" #include #include #include using namespace std; MSRAccessor::MSRAccessor() { service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching(kPcmMsrDriverClassName)); openConnection(); } int32_t MSRAccessor::buildTopology(uint32_t num_cores, void* pTopos) { size_t topology_struct_size = sizeof(TopologyEntry)*num_cores; kern_return_t ret = IOConnectCallStructMethod(connect, kBuildTopology, NULL, 0, pTopos, &topology_struct_size); return (ret == KERN_SUCCESS) ? 0 : -1; } int32_t MSRAccessor::read(uint32_t core_num, uint64_t msr_num, uint64_t * value) { pcm_msr_data_t idatas, odatas; size_t struct_size = sizeof(pcm_msr_data_t); idatas.msr_num = (uint32_t)msr_num; idatas.cpu_num = core_num; kern_return_t ret = IOConnectCallStructMethod(connect, kReadMSR, &idatas, struct_size, &odatas, &struct_size); if(ret == KERN_SUCCESS) { *value = odatas.value; return sizeof(uint64_t); } else { return -1; } } int32_t MSRAccessor::write(uint32_t core_num, uint64_t msr_num, uint64_t value){ pcm_msr_data_t idatas; idatas.value = value; idatas.msr_num = (uint32_t)msr_num; idatas.cpu_num = core_num; kern_return_t ret = IOConnectCallStructMethod(connect, kWriteMSR, &idatas, sizeof(pcm_msr_data_t), NULL, NULL); if(ret == KERN_SUCCESS) { return sizeof(uint64_t); } else { return -1; } } uint32_t MSRAccessor::getNumInstances() { kern_return_t kernResult; uint32_t output_count = 1; uint64_t knum_insts = 0; kernResult = IOConnectCallScalarMethod(connect, kGetNumInstances, NULL, 0, &knum_insts, &output_count); if (kernResult != KERN_SUCCESS) { cerr << "IOConnectCallScalarMethod returned 0x" << hex << setw(8) << kernResult << dec << endl; } // TODO add error handling; also, number-of-instance related // functions may go away as they do not appear to be used. return knum_insts; } uint32_t MSRAccessor::incrementNumInstances() { kern_return_t kernResult; uint32_t output_count = 1; uint64_t knum_insts = 0; kernResult = IOConnectCallScalarMethod(connect, kIncrementNumInstances, NULL, 0, &knum_insts, &output_count); if (kernResult != KERN_SUCCESS) { cerr << "IOConnectCallScalarMethod returned 0x" << hex << setw(8) << kernResult << dec << endl; } // TODO add error handling; also, these functions may go away as // they do not appear to be used. return knum_insts; } uint32_t MSRAccessor::decrementNumInstances() { kern_return_t kernResult; uint32_t output_count = 1; uint64_t knum_insts = 0; kernResult = IOConnectCallScalarMethod(connect, kDecrementNumInstances, NULL, 0, &knum_insts, &output_count); if (kernResult != KERN_SUCCESS) { cerr << "IOConnectCallScalarMethod returned 0x" << hex << setw(8) << kernResult << dec << endl; } // TODO add error handling; also, these functions may go away as // they do not appear to be used. return knum_insts; } MSRAccessor::~MSRAccessor() { closeConnection(); } kern_return_t MSRAccessor::openConnection() { kern_return_t kernResult = IOServiceOpen(service, mach_task_self(), 0, &connect); if (kernResult != KERN_SUCCESS) { cerr << "IOServiceOpen returned 0x" << hex << setw(8) << kernResult << dec << endl; } else { kernResult = IOConnectCallScalarMethod(connect, kOpenDriver, NULL, 0, NULL, NULL); if (kernResult != KERN_SUCCESS) { cerr << "kOpenDriver returned 0x" << hex << setw(8) << kernResult << dec << endl; } } return kernResult; } void MSRAccessor::closeConnection() { kern_return_t kernResult = IOConnectCallScalarMethod(connect, kCloseDriver, NULL, 0, NULL, NULL); if (kernResult != KERN_SUCCESS) { cerr << "kCloseDriver returned 0x" << hex << setw(8) << kernResult << dec << endl; } kernResult = IOServiceClose(connect); if (kernResult != KERN_SUCCESS) { cerr << "IOServiceClose returned 0x" << hex << setw(8) << kernResult << dec << endl; } } pcm-202307/src/MacMSRDriver/MSRAccessor.h000066400000000000000000000012551445420033100177110ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #include #include "PcmMsr/UserKernelShared.h" class MSRAccessor { private: io_service_t service; io_connect_t connect; kern_return_t openConnection(); void closeConnection(); public: MSRAccessor(); int32_t read(uint32_t cpu_num,uint64_t msr_num, uint64_t * value); int32_t write(uint32_t cpu_num, uint64_t msr_num, uint64_t value); int32_t buildTopology(uint32_t num_cores, void*); uint32_t getNumInstances(); uint32_t incrementNumInstances(); uint32_t decrementNumInstances(); ~MSRAccessor(); }; pcm-202307/src/MacMSRDriver/MSRKernel.h000066400000000000000000000016521445420033100173700ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #define PcmMsrDriverClassName com_intel_driver_PcmMsr #define kPcmMsrDriverClassName "com_intel_driver_PcmMsr" #ifndef MSR_KERNEL_SHARED #define MSR_KERNEL_SHARED #include typedef struct { uint64_t value; uint32_t cpu_num; uint32_t msr_num; } pcm_msr_data_t; /* // The topologyEntry struct that is used by PCM typedef struct{ uint32_t os_id; uint32_t socket; uint32_t core_id; } topologyEntry; // A kernel version of the topology entry structure. It has // an extra unused int to explicitly align the struct on a 64bit // boundary, preventing the compiler from adding extra padding. enum { kOpenDriver, kCloseDriver, kReadMSR, kWriteMSR, kBuildTopology, kGetNumInstances, kIncrementNumInstances, kDecrementNumInstances, kNumberOfMethods }; */ #endif pcm-202307/src/MacMSRDriver/PCIDriverInterface.cpp000066400000000000000000000132671445420033100215360ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2013, Intel Corporation // written by Patrick Konsor // #include #include #include "PCIDriverInterface.h" #include #include "PcmMsr/UserKernelShared.h" io_connect_t PCIDriver_connect = 0; std::map PCIDriver_mmap; // setupDriver #ifdef __cplusplus extern "C" #endif int PCIDriver_setupDriver() { kern_return_t kern_result; io_iterator_t iterator; bool driverFound = false; io_service_t local_driver_service; // get services kern_result = IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching(kPcmMsrDriverClassName), &iterator); if (kern_result != KERN_SUCCESS) { fprintf(stderr, "[error] IOServiceGetMatchingServices returned 0x%08x\n", kern_result); return kern_result; } // find service while ((local_driver_service = IOIteratorNext(iterator)) != IO_OBJECT_NULL) { driverFound = true; break; } if (driverFound == false) { fprintf(stderr, "[error] No matching drivers found \"%s\".\n", kPcmMsrDriverClassName); return KERN_FAILURE; } IOObjectRelease(iterator); // connect to service kern_result = IOServiceOpen(local_driver_service, mach_task_self(), 0, &PCIDriver_connect); if (kern_result != KERN_SUCCESS) { fprintf(stderr, "[error] IOServiceOpen returned 0x%08x\n", kern_result); return kern_result; } return KERN_SUCCESS; } // read32 #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_read32(uint32_t addr, uint32_t* val) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } uint64_t input[] = { (uint64_t)addr }; uint64_t val_ = 0; uint32_t outputCnt = 1; kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kRead, input, 1, &val_, &outputCnt); *val = (uint32_t)val_; return result; } // read64 #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_read64(uint32_t addr, uint64_t* val) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } kern_return_t result; uint64_t input[] = { (uint64_t)addr }; uint64_t lo = 0; uint64_t hi = 0; uint32_t outputCnt = 1; result = IOConnectCallScalarMethod(PCIDriver_connect, kRead, input, 1, &lo, &outputCnt); input[0] = (uint64_t)addr + 4; result |= IOConnectCallScalarMethod(PCIDriver_connect, kRead, input, 1, &hi, &outputCnt); *val = (hi << 32) | lo; return result; } // write32 #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_write32(uint32_t addr, uint32_t val) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } uint64_t input[] = { (uint64_t)addr, (uint64_t)val }; return IOConnectCallScalarMethod(PCIDriver_connect, kWrite, input, 2, NULL, 0); } // write64 #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_write64(uint32_t addr, uint64_t val) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } kern_return_t result; uint64_t input[] = { (uint64_t)addr, val & 0xffffffff }; result = IOConnectCallScalarMethod(PCIDriver_connect, kWrite, input, 2, NULL, 0); input[0] = (uint64_t)addr + 4; input[1] = val >> 32; result |= IOConnectCallScalarMethod(PCIDriver_connect, kWrite, input, 2, NULL, 0); return result; } // mapMemory #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_mapMemory(uint32_t address, uint8_t** virtual_address) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } uint64_t input[] = { (uint64_t)address }; uint64_t output[2]; uint32_t outputCnt = 2; kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kMapMemory, input, 1, output, &outputCnt); PCIDriver_mmap[(uint8_t*)output[1]] = (void*)output[0]; *virtual_address = (uint8_t*)output[1]; return result; } // unmapMemory #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_unmapMemory(uint8_t* virtual_address) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } void* memory_map = PCIDriver_mmap[virtual_address]; if (memory_map != NULL) { uint64_t input[] = { (uint64_t)memory_map }; kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kUnmapMemory, input, 1, NULL, 0); PCIDriver_mmap.erase(virtual_address); // remove from map return result; } else { return KERN_INVALID_ADDRESS; } } // readMemory32 #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_readMemory32(uint8_t* address, uint32_t* val) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } uint64_t input[] = { (uint64_t)address }; uint64_t val_ = 0; uint32_t outputCnt = 1; kern_return_t result = IOConnectCallScalarMethod(PCIDriver_connect, kReadMemory, input, 1, &val_, &outputCnt); *val = (uint32_t)val_; return result; } // readMemory64 #ifdef __cplusplus extern "C" #endif uint32_t PCIDriver_readMemory64(uint8_t* address, uint64_t* val) { if (!PCIDriver_connect) { if (PCIDriver_setupDriver() != KERN_SUCCESS) { return KERN_FAILURE; } } kern_return_t result; uint64_t input[] = { (uint64_t)address }; uint64_t lo = 0; uint64_t hi = 0; uint32_t outputCnt = 1; result = IOConnectCallScalarMethod(PCIDriver_connect, kReadMemory, input, 1, &lo, &outputCnt); input[0] = (uint64_t)address + 4; result |= IOConnectCallScalarMethod(PCIDriver_connect, kReadMemory, input, 1, &hi, &outputCnt); *val = (hi << 32) | lo; return result; } pcm-202307/src/MacMSRDriver/PCIDriverInterface.h000066400000000000000000000022321445420033100211710ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2013, Intel Corporation // written by Patrick Konsor // #ifndef pci_driver_driverinterface_h #define pci_driver_driverinterface_h #ifdef __cplusplus extern "C" { #endif #define PCI_ENABLE 0x80000000 #define FORM_PCI_ADDR(bus,dev,fun,off) (((PCI_ENABLE)) | \ ((bus & 0xFF) << 16) | \ ((dev & 0x1F) << 11) | \ ((fun & 0x07) << 8) | \ ((off & 0xFF) << 0)) uint32_t PCIDriver_read32(uint32_t addr, uint32_t* val); uint32_t PCIDriver_read64(uint32_t addr, uint64_t* val); uint32_t PCIDriver_write32(uint32_t addr, uint32_t val); uint32_t PCIDriver_write64(uint32_t addr, uint64_t val); uint32_t PCIDriver_mapMemory(uint32_t address, uint8_t** virtual_address); uint32_t PCIDriver_unmapMemory(uint8_t* virtual_address); uint32_t PCIDriver_readMemory32(uint8_t* address, uint32_t* val); uint32_t PCIDriver_readMemory64(uint8_t* address, uint64_t* val); #ifdef __cplusplus } #endif #endif pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/000077500000000000000000000000001445420033100206065ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.pbxproj000066400000000000000000000426471445420033100236770ustar00rootroot00000000000000// !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 81ADBF0A156EBD73006D9B47 /* PcmMsrClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81ADBF09156EBD73006D9B47 /* PcmMsrClient.cpp */; }; 81ADBF1A156EEDB9006D9B47 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81ADBF0B156EDBA1006D9B47 /* IOKit.framework */; }; 81DEAF6315703531005E8EC6 /* MSRAccessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81ADBF1C156EFF69006D9B47 /* MSRAccessor.cpp */; }; 81DEAF6615703946005E8EC6 /* DriverInterface.c in Sources */ = {isa = PBXBuildFile; fileRef = 81DEAF6515703946005E8EC6 /* DriverInterface.c */; }; 81DEAF67157039F6005E8EC6 /* DriverInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 81ADBF17156EECDA006D9B47 /* DriverInterface.h */; }; 81DEAF68157039FB005E8EC6 /* MSRAccessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 81ADBF1B156EFF56006D9B47 /* MSRAccessor.h */; }; 81F91BC6156D9BF8007DD788 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 81F91BC4156D9BF8007DD788 /* InfoPlist.strings */; }; 81F91BC9156D9BF8007DD788 /* PcmMsr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81F91BC8156D9BF8007DD788 /* PcmMsr.cpp */; }; 895805FC1760E6E5006ED117 /* PCIDriverInterface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 895805FA1760E6E5006ED117 /* PCIDriverInterface.cpp */; }; 895805FD1760E6E5006ED117 /* PCIDriverInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 895805FB1760E6E5006ED117 /* PCIDriverInterface.h */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ 816FC6A5158296D200D9DEB4 /* PBXBuildRule */ = { isa = PBXBuildRule; compilerSpec = com.apple.compilers.proxy.script; fileType = pattern.proxy; isEditable = 1; outputFiles = ( ); }; /* End PBXBuildRule section */ /* Begin PBXCopyFilesBuildPhase section */ 816FC6A31582965F00D9DEB4 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = /usr/local/lib; dstSubfolderSpec = 0; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 81ADBF08156EBD65006D9B47 /* PcmMsrClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PcmMsrClient.h; sourceTree = ""; }; 81ADBF09156EBD73006D9B47 /* PcmMsrClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PcmMsrClient.cpp; sourceTree = ""; }; 81ADBF0B156EDBA1006D9B47 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 81ADBF0D156EDD11006D9B47 /* UserKernelShared.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = UserKernelShared.h; path = PcmMsr/UserKernelShared.h; sourceTree = ""; }; 81ADBF12156EEB93006D9B47 /* libPcmMsr.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libPcmMsr.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 81ADBF17156EECDA006D9B47 /* DriverInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DriverInterface.h; sourceTree = ""; }; 81ADBF1B156EFF56006D9B47 /* MSRAccessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MSRAccessor.h; sourceTree = ""; }; 81ADBF1C156EFF69006D9B47 /* MSRAccessor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MSRAccessor.cpp; sourceTree = ""; }; 81DEAF6515703946005E8EC6 /* DriverInterface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DriverInterface.c; sourceTree = ""; }; 81F91BBC156D9BF8007DD788 /* PcmMsrDriver.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PcmMsrDriver.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 81F91BC3156D9BF8007DD788 /* PcmMsr-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PcmMsr-Info.plist"; sourceTree = ""; }; 81F91BC5156D9BF8007DD788 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 81F91BC7156D9BF8007DD788 /* PcmMsr.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PcmMsr.h; sourceTree = ""; }; 81F91BC8156D9BF8007DD788 /* PcmMsr.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PcmMsr.cpp; sourceTree = ""; }; 81F91BCA156D9BF8007DD788 /* PcmMsr-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PcmMsr-Prefix.pch"; sourceTree = ""; }; 895805FA1760E6E5006ED117 /* PCIDriverInterface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCIDriverInterface.cpp; sourceTree = ""; }; 895805FB1760E6E5006ED117 /* PCIDriverInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCIDriverInterface.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 81ADBF0F156EEB93006D9B47 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 81ADBF1A156EEDB9006D9B47 /* IOKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 81F91BB7156D9BF8007DD788 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 81ADBF16156EECC6006D9B47 /* PcmMsrLibrary */ = { isa = PBXGroup; children = ( 81ADBF17156EECDA006D9B47 /* DriverInterface.h */, 81DEAF6515703946005E8EC6 /* DriverInterface.c */, 81ADBF1B156EFF56006D9B47 /* MSRAccessor.h */, 81ADBF1C156EFF69006D9B47 /* MSRAccessor.cpp */, 895805FA1760E6E5006ED117 /* PCIDriverInterface.cpp */, 895805FB1760E6E5006ED117 /* PCIDriverInterface.h */, 81F91BC1156D9BF8007DD788 /* PcmMsr */, ); name = PcmMsrLibrary; sourceTree = ""; }; 81F91BAF156D9BF8007DD788 = { isa = PBXGroup; children = ( 81ADBF0D156EDD11006D9B47 /* UserKernelShared.h */, 81ADBF16156EECC6006D9B47 /* PcmMsrLibrary */, 81F91BBE156D9BF8007DD788 /* Frameworks */, 81F91BBD156D9BF8007DD788 /* Products */, ); sourceTree = ""; }; 81F91BBD156D9BF8007DD788 /* Products */ = { isa = PBXGroup; children = ( 81F91BBC156D9BF8007DD788 /* PcmMsrDriver.kext */, 81ADBF12156EEB93006D9B47 /* libPcmMsr.dylib */, ); name = Products; sourceTree = ""; }; 81F91BBE156D9BF8007DD788 /* Frameworks */ = { isa = PBXGroup; children = ( 81ADBF0B156EDBA1006D9B47 /* IOKit.framework */, ); name = Frameworks; sourceTree = ""; }; 81F91BC1156D9BF8007DD788 /* PcmMsr */ = { isa = PBXGroup; children = ( 81F91BC7156D9BF8007DD788 /* PcmMsr.h */, 81F91BC8156D9BF8007DD788 /* PcmMsr.cpp */, 81F91BC2156D9BF8007DD788 /* Supporting Files */, 81ADBF08156EBD65006D9B47 /* PcmMsrClient.h */, 81ADBF09156EBD73006D9B47 /* PcmMsrClient.cpp */, ); path = PcmMsr; sourceTree = ""; }; 81F91BC2156D9BF8007DD788 /* Supporting Files */ = { isa = PBXGroup; children = ( 81F91BC3156D9BF8007DD788 /* PcmMsr-Info.plist */, 81F91BC4156D9BF8007DD788 /* InfoPlist.strings */, 81F91BCA156D9BF8007DD788 /* PcmMsr-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ 81ADBF10156EEB93006D9B47 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( 81DEAF67157039F6005E8EC6 /* DriverInterface.h in Headers */, 81DEAF68157039FB005E8EC6 /* MSRAccessor.h in Headers */, 895805FD1760E6E5006ED117 /* PCIDriverInterface.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; 81F91BB8156D9BF8007DD788 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ 81ADBF11156EEB93006D9B47 /* PcmMsrLibrary */ = { isa = PBXNativeTarget; buildConfigurationList = 81ADBF13156EEB93006D9B47 /* Build configuration list for PBXNativeTarget "PcmMsrLibrary" */; buildPhases = ( 81ADBF0E156EEB93006D9B47 /* Sources */, 81ADBF0F156EEB93006D9B47 /* Frameworks */, 81ADBF10156EEB93006D9B47 /* Headers */, 816FC6A31582965F00D9DEB4 /* CopyFiles */, ); buildRules = ( 816FC6A5158296D200D9DEB4 /* PBXBuildRule */, ); dependencies = ( ); name = PcmMsrLibrary; productName = PcmMsrLibrary; productReference = 81ADBF12156EEB93006D9B47 /* libPcmMsr.dylib */; productType = "com.apple.product-type.library.dynamic"; }; 81F91BBB156D9BF8007DD788 /* PcmMsrDriver */ = { isa = PBXNativeTarget; buildConfigurationList = 81F91BCD156D9BF8007DD788 /* Build configuration list for PBXNativeTarget "PcmMsrDriver" */; buildPhases = ( 81F91BB6156D9BF8007DD788 /* Sources */, 81F91BB7156D9BF8007DD788 /* Frameworks */, 81F91BB8156D9BF8007DD788 /* Headers */, 81F91BB9156D9BF8007DD788 /* Resources */, 81F91BBA156D9BF8007DD788 /* Rez */, ); buildRules = ( ); dependencies = ( ); name = PcmMsrDriver; productName = PcmMsr; productReference = 81F91BBC156D9BF8007DD788 /* PcmMsrDriver.kext */; productType = "com.apple.product-type.kernel-extension"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 81F91BB1156D9BF8007DD788 /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0710; }; buildConfigurationList = 81F91BB4156D9BF8007DD788 /* Build configuration list for PBXProject "PcmMsr" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( English, en, ); mainGroup = 81F91BAF156D9BF8007DD788; productRefGroup = 81F91BBD156D9BF8007DD788 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 81F91BBB156D9BF8007DD788 /* PcmMsrDriver */, 81ADBF11156EEB93006D9B47 /* PcmMsrLibrary */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 81F91BB9156D9BF8007DD788 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 81F91BC6156D9BF8007DD788 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXRezBuildPhase section */ 81F91BBA156D9BF8007DD788 /* Rez */ = { isa = PBXRezBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXRezBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 81ADBF0E156EEB93006D9B47 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 81DEAF6315703531005E8EC6 /* MSRAccessor.cpp in Sources */, 81DEAF6615703946005E8EC6 /* DriverInterface.c in Sources */, 895805FC1760E6E5006ED117 /* PCIDriverInterface.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 81F91BB6156D9BF8007DD788 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 81F91BC9156D9BF8007DD788 /* PcmMsr.cpp in Sources */, 81ADBF0A156EBD73006D9B47 /* PcmMsrClient.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 81F91BC4156D9BF8007DD788 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 81F91BC5156D9BF8007DD788 /* en */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ 81ADBF14156EEB93006D9B47 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEPLOYMENT_LOCATION = NO; EXECUTABLE_PREFIX = lib; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/lib; PRODUCT_NAME = PcmMsr; }; name = Debug; }; 81ADBF15156EEB93006D9B47 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_ARC = YES; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEPLOYMENT_LOCATION = NO; EXECUTABLE_PREFIX = lib; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INSTALL_PATH = /usr/lib; PRODUCT_NAME = PcmMsr; }; name = Release; }; 81F91BCB156D9BF8007DD788 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; name = Debug; }; 81F91BCC156D9BF8007DD788 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 13.0; SDKROOT = macosx; }; name = Release; }; 81F91BCE156D9BF8007DD788 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1.0.0d1; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "PcmMsr/PcmMsr-Prefix.pch"; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INFOPLIST_FILE = "PcmMsr/PcmMsr-Info.plist"; MODULE_NAME = com.intel.driver.PcmMsrDriver; MODULE_VERSION = 1.0.0d1; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.intel.driver.PcmMsr; PRODUCT_NAME = PcmMsrDriver; SDKROOT = macosx; VALID_ARCHS = x86_64; WRAPPER_EXTENSION = kext; }; name = Debug; }; 81F91BCF156D9BF8007DD788 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1.0.0d1; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "PcmMsr/PcmMsr-Prefix.pch"; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INFOPLIST_FILE = "PcmMsr/PcmMsr-Info.plist"; MODULE_NAME = com.intel.driver.PcmMsrDriver; MODULE_VERSION = 1.0.0d1; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.intel.driver.PcmMsr; PRODUCT_NAME = PcmMsrDriver; SDKROOT = macosx; VALID_ARCHS = x86_64; WRAPPER_EXTENSION = kext; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 81ADBF13156EEB93006D9B47 /* Build configuration list for PBXNativeTarget "PcmMsrLibrary" */ = { isa = XCConfigurationList; buildConfigurations = ( 81ADBF14156EEB93006D9B47 /* Debug */, 81ADBF15156EEB93006D9B47 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 81F91BB4156D9BF8007DD788 /* Build configuration list for PBXProject "PcmMsr" */ = { isa = XCConfigurationList; buildConfigurations = ( 81F91BCB156D9BF8007DD788 /* Debug */, 81F91BCC156D9BF8007DD788 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 81F91BCD156D9BF8007DD788 /* Build configuration list for PBXNativeTarget "PcmMsrDriver" */ = { isa = XCConfigurationList; buildConfigurations = ( 81F91BCE156D9BF8007DD788 /* Debug */, 81F91BCF156D9BF8007DD788 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 81F91BB1156D9BF8007DD788 /* Project object */; } pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/000077500000000000000000000000001445420033100246045ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/contents.xcworkspacedata000066400000000000000000000002271445420033100315470ustar00rootroot00000000000000 pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/000077500000000000000000000000001445420033100267475ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/aiott.xcuserdatad/000077500000000000000000000000001445420033100323755ustar00rootroot00000000000000UserInterfaceState.xcuserstate000066400000000000000000002651511445420033100403640ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/aiott.xcuserdatadbplist00ÔX$versionX$objectsY$archiverT$top† ¯)*+,-./01EFGHIJKLMNOefghijklmnvwxy‚ƒŽ”¦§¨©ª«¬­®¸½ÃÄÅÌÒÓÔרÞßãçóôõö÷øùúûýþÿ !34:;AEFJMNQRVWcdefjkopvw}~ˆ‰Š‹¡¢£¤¥¦§¨©¯°¸»¼¿ÀÃÄÇÈËÌÐÔØäåæçèéêëìíñõöþ  #&*/37;IJKLMNTU[\bchpqrst|}~„‰‘’“›œ¡§¬´µ¶¾¿ÑÒÓÔÕÖרêëìíîïðñùúûüýþÿ$(*/0FGHIJKLMNOPSW[ghijko|}‚ƒ„‰•™Ÿ ¡¢¥±µ¼½¾ÁÄÇÊÔÕÖרêëìíîïðñòøùÿ )*+,45;?IJKLRSYZ[agopqyz{ƒ„…†Š¤¥¦§¨©ª«¬­®¯¿ÀÁÂÃÄÅÓ×ÚÞáåèìïóöüý  &'+0128<ABCIJNSTU]adhkuvwx}~€†‹”žŸ ¡¥¦§«¯¹½ÀÄÇËÎÜÝÞßàáâèéîö÷øù !"#$%&'()*+,-456789=CDFJPRVZ^bfjntuv€‚ƒ„…‰’“¡¢£¤¥¦§­®³»¼½¾ÆÇÈÌÑÒÓÝÞßàáåõö÷øùúû¾ÁÄÇÊÍÐÓÖÙÜßâåèëîñô÷úý  !$'*-0369<?BEHKNQTWZ]`cfilorux{~„‡Š“–™œŸ¢¥¨«®±´·º½ÀÃÆÉÌÏÒÕØÛÞáäçêíðóöùüÿ          # & ) , / 2 5 8 ; > A D G J M P S V Y \ _ b e h k n q t w z } € ƒ † ‰ Œ ’ • ˜ › ž ¡ ¤ § ª ­ ° ³ ¶ ¹ ¼ ¿ Â Å È Ë Î Ñ Ô × Ú Ý à ã æ é ì ï ò õ ø û þ          # $ ) 1 2 3 4 < = > B G H I S T U V W [ k l m n o p q u x : = @ C F I L O R U X [ ^ a d g j m p s v y |  ‚ … ˆ ‹ Ž ‘ ” — š   £ ¦ © ¬ ¯ ² µ ¸ » ¾ Á Ä Ç Ê Í Ð Ó Ö Ù Ü ß â å è ë î ñ ô ÷ ú ý         ! $ ' * - 0 3 6 9 < ? B E H K N Q T W Z ] ` c f i l o r u x { ~ „ ‡ Š “ – ™ œ Ÿ ¢ ¥ ¨ « ® ± ´ · º ½ À Ã Æ É Ì Ï Ò Õ Ø Û Þ á ä ç ê í ð ó ö ù ü ÿ          # & ) , / 2 5 8 ; > A D G J M P S V Y \ _ b e h k n q t w { á å è ì ï ó ö ú ý  $'+.259<@CGJNQUX\_cfjmqtx{‚†‰”—›ž¢¥©¬°³·º¾ÁÅÈÌÏÓÖÚÝáäèëïòöùý  #'*6789:;<=IJKLMNO[\]ijkwxyz†‡ˆ”•–—˜™š›§¨©µ¶·¸ÄÅÆÇÈÉÊÖרäåæçóôõö $%&'345ABCDPQRS_`abnopq}~‹ŒŽš›œ©ª«¬¸¹º»ÇÈÉÊÖרÙåæçóôõö !"./0<=>?KLMNZ[\]ijklxyz{‡ˆ‰•–—˜™š›œ¨©ª¶·¸¹ÅÆÇÈÔÕÖ×ãäåæòóôõbfgklpquvz{€„…‰ŠŽ“”˜™ž¢£§¨¬­±²¶·»¼ÀÁÅÆÊËÏÐÔÕÙÚÞßãäèéíîòó÷øüý   &'*017=CDJPVW]cijpv|€ˆ‘•™ž¤ª«±·¸¾ÄÊÐÔÚÞäêðöü  "(.28>DEIORX^djpv€‚ƒ‡ˆ‰‘™š›œ£¤¥¯°±²¹ÃÄÅÆÇÈÉÓÔÕÖàáâåõö÷øùúþ!%&*+/0459:>?CDHIMNRSWX`abcdlmnowxyz‚ƒ„…Ž˜™š¢£¤¥­®¯·¸¹ÁÂÃËÌÍÎÛßâæéíðô÷ûþ  U$nullÓ V$classWNS.keysZNS.objects€O¢€€¢€\_$44D36AD8-513B-4CD9-A6CA-53DE19A36F0F_IDEWorkspaceDocumentÓ  €<¨€€€€€ € € € ¨!"#$'$€ Z[€M€€€€M_>IDEWorkspaceTabController_2FB767B5-A143-4E05-88C9-42AED9476D06^IDEWindowFrame_!IDEOrderedWorkspaceTabControllers_IDEWindowInFullscreenMode_,IDEWorkspaceWindowControllerUniqueIdentifier_IDEActiveWorkspaceTabController_IDEWindowToolbarIsVisible_IDEWindowTabBarIsVisibleÓ 3<€<¨456789:;€€€€€€€€¨='?@AB$D€€€€Í€ÔO€M€A[IDETabLabel_IDEShowNavigator]IDEEditorArea_-IDEWorkspaceTabControllerUtilityAreaSplitView_IDENavigatorArea_,IDEWorkspaceTabControllerDesignAreaSplitView_IDEShowUtilities_AssistantEditorsLayout^IOUserClient.h Ó Q[€<©RSTUVWXYZ€€€€€€€€ €!©\]^'Dabc$€"€q€ €€A€®€Ä€Ì€M_IDEEditorMode_Genius_IDEEditorMode_StandardZlayoutTree]IDEShowEditorZEditorMode_IDEDefaultDebugArea_DebuggerSplitView_ DefaultPersistentRepresentations_ShowDebuggerAreaÓ ps€<¢qr€#€$¢tu€%€&]SplitPosition_%EditorLayout_PersistentRepresentation">ÿ…öÓ {~€<¢|}€'€(¢€€)€YYAlternateTMainÓ …‰€O£†‡ˆ€*€+€,£ŠDŒ€-€A€W_)EditorLayout_StateSavingStateDictionaries_EditorLayout_Selected_EditorLayout_GeometryÒ ‘’€V¡“€.Ó –ž€<§—˜™š›œ€/€0€1€2€3€4€5§Ÿ ¡¢£¤¥€6€7€F€P€Q€R€S\FileDataType_ArchivableRepresentation[EditorState_NavigableItemName_DocumentNavigableItemName_DocumentExtensionIdentifier[DocumentURL_public.c-headerÕ¯°± ²³´µ¶D_DocumentLocation^IdentifierPath_DomainIdentifier_IndexOfDocumentIdentifier€B€8€€E€AÒ ¹º€@¢»¼€9€=Ó ¿Á€<¡À€:¡Â€;_navigableItem_nameXPcmMsr.hÒÆÇÈÉZ$classnameX$classes_NSMutableDictionary£ÈÊË\NSDictionaryXNSObjectÓ ÎЀ<¡Ï€>¡Ñ€?Zidentifier_(Xcode.IDEKit.GeniusCategory.CounterpartsÒÆÇÕÖWNSArray¢ÕËÓÙ ÚµÜÝYtimestamp[documentURL€€D€C_efile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.hÒÆÇàá_DVTDocumentLocation¢âË_DVTDocumentLocationÒÆÇäå_(IDENavigableItemArchivableRepresentation¢æË_(IDENavigableItemArchivableRepresentationÓ éî€O¤êëìí€G€H€I€J¤ïð$ò€K€L€M€N_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aµ´©ùáqY{0, 1166}X{547, 0}ÒÆÇÊü¢ÊËXPcmMsr.hXPcmMsr.h_&Xcode.IDEKit.EditorDocument.SourceCodeÓ µ[NS.relativeWNS.base€U€T€_efile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.hÒÆÇ UNSURL¢ ËUNSURLÒÆÇ  ^NSMutableArray£ ÕËÒ ‘€V¡€X_{{0, 0}, {537, 861}}Ó €O£†‡ˆ€*€+€,£D€Z€A€oÒ ¹€@¡ €[Ó #+€<§—˜™š›œ€/€0€1€2€3€4€5§,-.//¤2€\€]€i€a€a€R€m_public.c-plus-plus-sourceÕ¯°± ²567¶D€g€_€^€E€A_/Xcode.IDENavigableItemDomain.WorkspaceStructureÒ ¹=€@£>?@€`€c€eÒ BC/ZIdentifier€b€aZPcmMsr.cppÒÆÇGH_IDEArchivableStringIndexPair¢IË_IDEArchivableStringIndexPairÒ BCL€b€dVPcmMsrÒ BCP€b€fVPcmMsrÓÙ ÚµÜU€€D€h_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.cppÓ Y^€O¤êëìí€G€H€I€J¤_`$b€j€k€M€l#Aµ´©ùá€ÔY{0, 1702}W{35, 6}Ó hµ€U€n€_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.cppÒ ¹m€@¡n€p_{{0, 0}, {1074, 861}}Ó rt€<¡s€r¡u€s_%EditorLayout_PersistentRepresentationÓ y{€<¡z€t¡|€uTMainÓ €„€O£‚ƒ€v€w€x£…D‡€y€A€ž_)EditorLayout_StateSavingStateDictionaries_EditorLayout_Selected_EditorLayout_GeometryÒ ¹€@¡Ž€zÓ ‘™€<§’“”•–—˜€{€|€}€~€€€€§š›œžŸ €‚€ƒ€“€›€‡€œ€\FileDataType_ArchivableRepresentation[EditorState_NavigableItemName_DocumentNavigableItemName_DocumentExtensionIdentifier[DocumentURL_public.c-headerÕ¯°± ²ª«¬¶D€€…€„€E€A_.Xcode.IDENavigableItemDomain.FrameworkFilePathÒ ¹²€@¥³´µ¶·€†€ˆ€Š€Œ€ŽÒ BCž€b€‡^IOUserClient.hÒ BC¾€b€‰UIOKitÒ BC€b€‹_Kernel.frameworkÒ BCÆ€b€ZFrameworksÒ BCÊ€b€ZMacOSX10.7ÓÙ ÚµÜÏ€€D€‘Ò ÑÒÓYNS.string€’_Åfile://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/IOKit/IOUserClient.hÒÆÇÕÖ_NSMutableString£Õ×ËXNSStringÓ Ú߀O¤ÛÜÝÞ€”€•€–€—¤àá$〘€™€M€š_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AµÈ2z‹ûj\{5637, 2449}Z{5280, 45}_class IOUserClient_&Xcode.IDEKit.EditorDocument.SourceCodeÓ ϵ€U€‘€Ò ¹ó€@¡ô€Ÿ_{{0, 0}, {1334, 861}}Ô÷ø ùúµüý_primaryEditorContextNode_geniusEditorContextNode_rootLayoutTreeNode€¡€€­€ªÖÿ ×ýµ_ documentArchivableRepresentation[orientationVparent[contentTypeXchildren€¢€ª€¬€Õ¯°± ²  ¬¶D€©€£€„€E€AÒ ¹€@¥€¤€¥€¦€§€¨Ò BCž€b€‡Ò BC¾€b€‰Ò BC€b€‹Ò BCÆ€b€Ò BCÊ€b€ÓÙ ÚµÜÏ€€D€‘Öÿ µ×µ×.€€€¬€«Ò ¹1€@¡ú€¡ÒÆÇ45_'IDEWorkspaceTabControllerLayoutTreeNode¢6Ë_'IDEWorkspaceTabControllerLayoutTreeNodeÒÆÇ89_#IDEWorkspaceTabControllerLayoutTree¢:Ë_#IDEWorkspaceTabControllerLayoutTreeÓ =C€<¥>?@AB€¯€°€±€²€³¥DEFDH€´€µ€·€´€¹_LayoutFocusModeYVariablesWConsoleZLayoutMode_IDEDebugArea_SplitViewÓ PR€<¡Q€¶¡D€A_VariablesViewSelectedScopeÓ WY€<¡X€¸¡D€A_ConsoleFilterModeÓ ^`€<¡_€º¡a€»_DVTSplitViewItemsÒ ‘e€V¢fg€¼€ÁÓ jm€O¢kl€½€¾¢no€¿€À]DVTIdentifier_DVTViewMagnitude]VariablesView#@„ÀÓ vy€O¢kl€½€¾¢z{€Â€Ã[ConsoleArea#@„èÓ €‚€<¡_€º¡ƒ€ÅÒ ‘†€V¢‡ˆ€Æ€ÉÓ ‹Ž€O¢kl€½€¾¢€Ç€ÈYIDEEditor#@‰HÓ •˜€O¢kl€½€¾¢™š€Ê€Ë_IDEDebuggerArea#@spÓ Ÿ €<  Ó £¥€<¡_€º¡¦€ÎÒ ‘©€V¢ª«€Ï€ÒÓ ®±€O¢kl€½€¾¢²³€Ð€ÑP#@ƒ Ó ¸»€O¢kl€½€¾¢²½€Ð€Ó#@o Ó ÁÉ€<§ÂÃÄÅÆÇȀՀր׀؀ـڀۧÊËÌÍÎÏЀ܀í€ú€û%*F_Xcode.IDEKit.Navigator.Symbol_ Xcode.IDEKit.Navigator.BatchFind_SelectedNavigator_Xcode.IDEKit.Navigator.Issues_Xcode.IDEKit.Navigator.Debug_ Xcode.IDEKit.Navigator.Structure_Xcode.IDEKit.Navigator.LogsÓ Úâ€<§ÛÜÝÞßàá€Ý€Þ€ß€à€á€â€ã§ã'$'çè'€ä€€M€€ê€ë€_IDEExpandedItems_IDESymbolNavigatorShowHierarchy_$IDESymbolNavigatorShowContainersOnly_!IDESymbolNavigatorShowClassesOnly_IDESymbolNamePatternString_!IDESymbolNavigatorSelectedSymbols_#IDESymbolNavigatorShowWorkspaceOnlyÒ ‘ó€V¥ôõö÷ø€å€æ€ç€è€é_c:@C@MSRAccessor_(c:@C@com_intel_driver_PcmMsr@C@MetaClass_.c:@C@com_intel_driver_PcmMsrClient@C@MetaClass_"c:@C@com_intel_driver_PcmMsrClient_c:@C@com_intel_driver_PcmMsrPÒ ‘€V¡€ì_"c:@C@MSRAccessor@F@openConnection#Ó €<§     €î€ï€ð€ñ€ò€ó€ô§D$çD€A€M€ê€õ€ö€A€ø_#IDEBatchFindNavigatorScrollPosition_!IDEBatchFindNavigatorShowsOptions_"IDEBatchFindNavigatorReplaceString_IDEBatchFindNavigatorFindString_'IDEBatchFindNavigatorSelectedRowIndexes_IDEBatchFindNavigatorFindMode_$IDEBatchFindNavigatorCollapsedGroupsVmallocÔ ! "#XNSLength\NSRangeCountZNSLocation€÷ÒÆÇ%&ZNSIndexSet¢'ËZNSIndexSetÒ ×)€ùÒÆÇ+,_NSMutableIndexSet£-.Ë_NSMutableIndexSetZNSIndexSet_ Xcode.IDEKit.Navigator.StructureÓ 2<€<©3456789:;€ü€ý€þ€ÿ©$>?@$BC$E€M€M"#€M$_IDEErrorFilteringEnabled^IDEVisibleRect_IDECollapsedFiles_IDEExpandedIssues^IDEShowsByType_IDESelectedNavigables_IDECollapsedTypes_IDERecentFilteringEnabled_IDECollapsedGroups_{{0, 0}, {345, 945}}Ò QR ÒÆÇTU\NSMutableSet£TVËUNSSetÒ QY¡Z Ó ]b€<¤^_`a    ¤cdef_documentLocations[fullMessageTtypeYsubissuesÒ ¹m€@¡nÙpqÙrs tÚuv×µxxyz{z_characterRangeLoc_characterRangeLen_endingColumnNumber_startingColumnNumber_startingLineNumber_endingLineNumberc€ß_‡file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/include/stdlib.hÒÆÇ~_DVTTextDocumentLocation£€Ë_DVTTextDocumentLocation_DVTDocumentLocation_DFunctions that differ only in their return type cannot be overloaded^Semantic IssueÒ ‘†€V¢‡ˆÓ ‹€<¤^_`a    ¤‘’“”Ò ¹—€@¡˜ÙpqÙrs tÚuš×µœœyž"€ _gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.cpp_pIn file included from /Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.cpp:2:_Clang LLVM 1.0 NoticeÒ ‘¤€V Ó §¬€<¤^_`a    ¤­®“° !Ò ¹³€@¡´ÙpqÙrs tÚu¶×µ¸¸yº»º €_·file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/libkern/libkern.h_Previous declaration is hereÒ ‘À€V Ò ‘ÀV Ò QÆ Ò QÉ Ó ÌЀ<£ÍÎÏ&'(£ÑD$)€A€M_IDEStackCompressionValue_IDEThreadOrQueueMode_IDEShowOnlyInterestingContentÓ Úâ€<§ÛÜÝÞßàá+,-./01§ã$åæ$$é2€M3<€M€MD^IDEVisibleRect_"IDEUnsavedDocumentFilteringEnabled_IDESelectedTree_IDEExpandedItemsTree_IDESCMStatusFilteringEnabled_!IDERecentDocumentFilteringEnabled_,IDENavigatorExpandedItemsBeforeFilteringTree_{{0, 0}, {345, 817}}Ó ôö€O¡õ4¡÷5_IDEValuesAsTreeÓ ûý€<¡ü6¡þ7VPcmMsrÓ €<¡8¡9VPcmMsrÓ   €<¡ :¡ ;_PcmMsrClient.cppPÓ €O¡õ4¡=Ó €<¡ü6¡>Ó #€<¥  !";8?@A¥ %   ;B;;;]PcmMsrLibraryXProductsZFrameworksÓ .1€<¢ 0;C¢  ;;_Supporting FilesÓ 79€O¡õ4¡:EÓ =>€<  Ó AE€<£BCDGHI£$GH€MJN_#IDELogNavigatorRecentFilterStateKey_&IDELogNavigatorSelectedObjectsStateKey_"IDELogNavigatorVisibleRectStateKeyÓ NP€O¡OK¡QL_IDEValuesAsTreeÓ UW€<¡VM¡ç€ê_ Build PcmMsrLibrary : 1:22:27 PM_{{0, 0}, {259, 825}}Ó ]_€<¡_€º¡`PÒ ‘c€V£defQTWÓ il€O¢kl€½€¾¢mnRS_IDENavigatorArea#@u Ó sv€O¢kl€½€¾¢wxUV]IDEEditorArea#@”ØÓ }€€O¢kl€½€¾¢‚XY_IDEUtilitiesArea#@p@_{{0, 91}, {1680, 937}}Ò ¹ˆ€@¡€Ó Œ˜€<«Ž‘’“”•–—]^_`abcdefg«'šDœžŸ ¡$$€h€A‹“±²ò€M€M_BreakpointsActivated_DefaultEditorStatesForURLs_DebuggingWindowBehavior_ActiveRunDestination\ActiveScheme_0LastCompletedPersistentSchemeBasedActivityReport_DocumentWindows_DefaultEditorFrameSizeForURLs_RecentEditorDocumentURLs_AppFocusInMiniDebugging_MiniDebuggingConsoleÓ ±¸€<¦²³´µ¶·ijklmn¦¹º»¼½¾o˜³Äð_IDEQuickLookEditor.Editor_'Xcode.IDEKit.EditorDocument.PlistEditor_7Xcode.Xcode3ProjectSupport.EditorDocument.Xcode3Project_&Xcode.IDEKit.EditorDocument.SourceCode_'Xcode.IDEKit.EditorDocument.LogDocument_,Xcode.IDEKit.EditorDocument.ASCIIPlistEditorÓ ÇÍ€<¥ÈÉÊËÌprtvx¥ÎÏÐÑÒz‡’Ó Õµ€Uq€Ò ÑÒÙ€’_file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-ccpdocwrovhbjackmzaqtolttprt/Build/Products/Debug/libPcmMsrLibrary.dylibÓ ܵ€Us€Ò ÑÒà€’_file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-cwyrljtdvuljlketlkxmiygymebv/Build/Products/Debug/libPcmMsrLibrary.dylibÓ ãµ€Uu€Ò ÑÒ瀒_Œfile://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-cwyrljtdvuljlketlkxmiygymebv/Build/Products/Debug/liblibPcmMsr.dylibÓ êµ€Uw€Ò ÑÒ_‰file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-cwyrljtdvuljlketlkxmiygymebv/Build/Products/Debug/libPcmMsr.dylibÓ ñµ€Uy€Ò ÑÒõ€’_‰file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-admpducbfnneumeyynvfwmnhhodt/Build/Products/Debug/libPcmMsr.dylibÓ øú€<¡ù{¡û|_SelectedDocumentLocationsÒ ¹ÿ€@¡}ÔÙ ÚD_IDEQuickLookPageNumber€~€A_file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-ccpdocwrovhbjackmzaqtolttprt/Build/Products/Debug/libPcmMsrLibrary.dylib#Aµy_—¼ÒÆÇ  _IDEQuickLookDocumentLocation£  Ë_IDEQuickLookDocumentLocation_DVTDocumentLocationÓ €<¡‚¡ƒ_SelectedDocumentLocationsÒ ¹€@¡„ÔÙ ÚD†€…€A_file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-cwyrljtdvuljlketlkxmiygymebv/Build/Products/Debug/libPcmMsrLibrary.dylib#Aµ£jHT3èÓ "$€<¡#ˆ¡%‰_SelectedDocumentLocationsÒ ¹)€@¡*ŠÔÙ Ú,.DŒ€‹€A_Œfile://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-cwyrljtdvuljlketlkxmiygymebv/Build/Products/Debug/liblibPcmMsr.dylib#Aµ«N2lþ7Ó 46€<¡#ˆ¡7ŽÒ ¹:€@¡;ÔÙ Ú=?D‘€€A_‰file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-cwyrljtdvuljlketlkxmiygymebv/Build/Products/Debug/libPcmMsr.dylib#Aµ«Oî 'Ó EG€<¡F“¡H”_SelectedDocumentLocationsÒ ¹L€@¡M•ÔÙ ÚOQD—€–€A_‰file://localhost/Users/aiott/Library/Developer/Xcode/DerivedData/PcmMsr-admpducbfnneumeyynvfwmnhhodt/Build/Products/Debug/libPcmMsr.dylib#Aµ¯—Æéu°Ó WZ€<¢XY™›¢[\ªÓ _µ€Uš€Ò ÑÒc€’_Dfile://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsr-Info.plistÓ fµ€Uœ€Ò ÑÒj€’_Sfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/PcmMsr-Info.plistÓ mq€<£nopžŸ £rst¡¤¥_IDE_PLIST_EDITOR_SELECTION_KEY_ IDE_PLIST_EDITOR_VISIBLERECT_KEY_IDE_PLIST_EDITOR_EXPANSION_KEYÒ ¹z€@¢{|¢£_IOKitPersonalities\PcmMsrClient_{{0, 0}, {1090, 832}}Ò Q‚£ƒ„…¦§©Ò ‘ˆ€V¢{|¢£Ò ‘€V¡Ž¨_OSBundleLibrariesÒ ‘’€V¡{¢Ó –š€<£—˜™«¬­£›œ®°±_IDE_PLIST_EDITOR_SELECTION_KEY_ IDE_PLIST_EDITOR_VISIBLERECT_KEY_IDE_PLIST_EDITOR_EXPANSION_KEYÒ ¹£€@¡¤¯_OSBundleLibraries_{{0, 0}, {963, 806}}Ò Q©¡ª²Ò ‘­€V¡¤¯Ó ±µ€<£²³´´¶¸£¶·¸ºùßÓ »µ€Uµ€Ò ÑÒ¿€’_?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÒ ÑÒÀ€’_Architectures||ADDITIONAL_SDKSÒ ÑÒÀ’_Architectures||ARCHSÒ ÑÒÆ€’_Architectures||SDKROOTÒ ÑÒÉ€’_#Architectures||SUPPORTED_PLATFORMSÒ ÑÒÌ€’_Architectures||VALID_ARCHSÒ ÑÒÏ€’_Build Locations||SYMROOTÒ ÑÒÒ€’_Build Locations||OBJROOTÒ ÑÒÕ€’_%Build Locations||SHARED_PRECOMPS_DIRÒ ÑÒØ€’_Build Options||BUILD_VARIANTSÒ ÑÒÛ€’_Build Options||GCC_VERSIONÒ ÑÒÞ€’_(Build Options||DEBUG_INFORMATION_FORMATÒ ÑÒဒ_'Build Options||GENERATE_PROFILING_CODEÒ ÑÒ䀒_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRÒ ÑÒ瀒_)Build Options||RUN_CLANG_STATIC_ANALYZERÒ ÑÒꀒ_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESÒ ÑÒ퀒_ Build Options||VALIDATE_PRODUCTÒ ÑÒð€’_%Code Signing||CODE_SIGN_ENTITLEMENTSÒ ÑÒó€’_!Code Signing||CODE_SIGN_IDENTITYÒ ÑÒö€’_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHÒ ÑÒù€’_$Code Signing||OTHER_CODE_SIGN_FLAGSÒ ÑÒü€’_Deployment||STRIPFLAGSÒ ÑÒÿ€’_Deployment||ALTERNATE_GROUPÒ ÑÒ€’_Deployment||ALTERNATE_OWNERÒ ÑÒ€’_Deployment||ALTERNATE_MODEÒ ÑÒ€’_(Deployment||ALTERNATE_PERMISSIONS_FILESÒ ÑÒ €’_!Deployment||COMBINE_HIDPI_IMAGESÒ ÑÒ€’_ Deployment||DEPLOYMENT_LOCATIONÒ ÑÒ€’_&Deployment||DEPLOYMENT_POSTPROCESSINGÒ ÑÒ€’_Deployment||INSTALL_GROUPÒ ÑÒ€’_Deployment||INSTALL_OWNERÒ ÑÒ€’_Deployment||INSTALL_MODE_FLAGÒ ÑÒ€’_Deployment||DSTROOTÒ ÑÒ €’_Deployment||INSTALL_PATHÒ ÑÒ#€’_%Deployment||MACOSX_DEPLOYMENT_TARGETÒ ÑÒ&€’_%Deployment||PRODUCT_DEFINITION_PLISTÒ ÑÒ)€’_Deployment||SKIP_INSTALLÒ ÑÒ,€’_$Deployment||STRIP_INSTALLED_PRODUCTÒ ÑÒ/€’_Deployment||STRIP_STYLEÒ ÑÒ2€’_Deployment||SEPARATE_STRIPÒ ÑÒ5€’_Kernel Module||MODULE_NAMEÒ ÑÒ8€’_Kernel Module||MODULE_STARTÒ ÑÒ;€’_Kernel Module||MODULE_STOPÒ ÑÒ>€’_Kernel Module||MODULE_VERSIONÒ ÑÒA€’_Linking||BUNDLE_LOADERÒ ÑÒD€’_%Linking||DYLIB_COMPATIBILITY_VERSIONÒ ÑÒG€’_Linking||DYLIB_CURRENT_VERSIONÒ ÑÒJ€’_Linking||DEAD_CODE_STRIPPINGÒ ÑÒM€’_'Linking||LINKER_DISPLAYS_MANGLED_NAMESÒ ÑÒP€’_Linking||LD_NO_PIEÒ ÑÒS€’_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSÒ ÑÒV€’_Linking||LD_DYLIB_INSTALL_NAMEÒ ÑÒY€’_Linking||EXPORTED_SYMBOLS_FILEÒ ÑÒ\€’_Linking||INIT_ROUTINEÒ ÑÒ_€’_&Linking||LINK_WITH_STANDARD_LIBRARIESÒ ÑÒb€’_Linking||MACH_O_TYPEÒ ÑÒe€’_Linking||ORDER_FILEÒ ÑÒh€’_Linking||OTHER_LDFLAGSÒ ÑÒk€’_%Linking||GENERATE_MASTER_OBJECT_FILEÒ ÑÒn€’_Linking||PRELINK_LIBSÒ ÑÒq€’_Linking||KEEP_PRIVATE_EXTERNSÒ ÑÒt€’_!Linking||LD_RUNPATH_SEARCH_PATHSÒ ÑÒw€’_Linking||SEPARATE_SYMBOL_EDITÒ ÑÒz€’_Linking||PRELINK_FLAGSÒ ÑÒ}€’_Linking||SECTORDER_FLAGSÒ ÑÒ€€’_!Linking||UNEXPORTED_SYMBOLS_FILEÒ ÑÒƒ€’_Linking||WARNING_LDFLAGSÒ ÑÒ†€’_Linking||LD_GENERATE_MAP_FILEÒ ÑÒ‰€’_%Packaging||APPLY_RULES_IN_COPY_FILESÒ ÑÒŒ€’_ Packaging||EXECUTABLE_EXTENSIONÒ ÑÒ€’_Packaging||EXECUTABLE_PREFIXÒ ÑÒ’€’_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSÒ ÑÒ•€’_!Packaging||GENERATE_PKGINFO_FILEÒ ÑÒ˜€’_Packaging||FRAMEWORK_VERSIONÒ ÑÒ›€’_Packaging||INFOPLIST_FILEÒ ÑÒž€’_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSÒ ÑÒ¡€’_#Packaging||INFOPLIST_OUTPUT_FORMATÒ ÑÒ¤€’_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSÒ ÑÒ§€’_#Packaging||INFOPLIST_PREFIX_HEADERÒ ÑÒª€’_ Packaging||INFOPLIST_PREPROCESSÒ ÑÒ­€’_&Packaging||COPYING_PRESERVES_HFS_DATAÒ ÑÒ°€’_'Packaging||PRIVATE_HEADERS_FOLDER_PATHÒ ÑÒ³€’_Packaging||PRODUCT_NAMEÒ ÑÒ¶€’_$Packaging||PLIST_FILE_OUTPUT_FORMATÒ ÑÒ¹€’_&Packaging||PUBLIC_HEADERS_FOLDER_PATHÒ ÑÒ¼€’_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGÒ ÑÒ¿€’_Packaging||WRAPPER_EXTENSIONÒ ÑÒ€’_'Search Paths||ALWAYS_SEARCH_USER_PATHSÒ ÑÒÅ€’_%Search Paths||FRAMEWORK_SEARCH_PATHSÒ ÑÒÈ€’_"Search Paths||HEADER_SEARCH_PATHSÒ ÑÒË€’_#Search Paths||LIBRARY_SEARCH_PATHSÒ ÑÒ΀’_Search Paths||REZ_SEARCH_PATHSÒ ÑÒÑ€’_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ ÑÒÔ€’_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ ÑÒ×€’_'Search Paths||USER_HEADER_SEARCH_PATHSÒ ÑÒÚ€’_Unit Testing||OTHER_TEST_FLAGSÒ ÑÒÝ€’_Unit Testing||TEST_AFTER_BUILDÒ ÑÒà€’_Unit Testing||TEST_HOSTÒ ÑÒ〒_Unit Testing||TEST_RIGÒ ÑÒ怒_$Versioning||CURRENT_PROJECT_VERSIONÒ ÑÒ递_Versioning||VERSION_INFO_FILEÒ ÑÒ쀒_%Versioning||VERSION_INFO_EXPORT_DECLÒ ÑÒ_ Versioning||VERSION_INFO_PREFIXÒ ÑÒò€’_ Versioning||VERSION_INFO_SUFFIXÒ ÑÒõ€’_Versioning||VERSIONING_SYSTEMÒ ÑÒø€’_!Versioning||VERSION_INFO_BUILDERÒ ÑÒû€’_AApple LLVM compiler 3.1 - Code Generation||GCC_FAST_OBJC_DISPATCHÒ ÑÒþ€’_HApple LLVM compiler 3.1 - Code Generation||CLANG_X86_VECTOR_INSTRUCTIONSÒ ÑÒ €’_>Apple LLVM compiler 3.1 - Code Generation||GCC_STRICT_ALIASINGÒ ÑÒ €’_IApple LLVM compiler 3.1 - Code Generation||GCC_GENERATE_DEBUGGING_SYMBOLSÒ ÑÒ €’_=Apple LLVM compiler 3.1 - Code Generation||GCC_DYNAMIC_NO_PICÒ ÑÒ €’_KApple LLVM compiler 3.1 - Code Generation||GCC_GENERATE_TEST_COVERAGE_FILESÒ ÑÒ €’_IApple LLVM compiler 3.1 - Code Generation||GCC_INLINES_ARE_PRIVATE_EXTERNÒ ÑÒ €’_KApple LLVM compiler 3.1 - Code Generation||GCC_INSTRUMENT_PROGRAM_FLOW_ARCSÒ ÑÒ €’_HApple LLVM compiler 3.1 - Code Generation||GCC_ENABLE_KERNEL_DEVELOPMENTÒ ÑÒ €’_3Apple LLVM compiler 3.1 - Code Generation||LLVM_LTOÒ ÑÒ €’_Apple LLVM compiler 3.1 - Language||GCC_ENABLE_OBJC_EXCEPTIONSÒ ÑÒ L€’_8Apple LLVM compiler 3.1 - Language||GCC_ENABLE_TRIGRAPHSÒ ÑÒ O€’_KApple LLVM compiler 3.1 - Language||GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLSÒ ÑÒ R€’_CApple LLVM compiler 3.1 - Language||GCC_USE_INDIRECT_FUNCTION_CALLSÒ ÑÒ U€’_CApple LLVM compiler 3.1 - Language||GCC_USE_REGISTER_FUNCTION_CALLSÒ ÑÒ X€’_KApple LLVM compiler 3.1 - Language||GCC_INCREASE_PRECOMPILED_HEADER_SHARINGÒ ÑÒ [€’_9Apple LLVM compiler 3.1 - Language||CLANG_ENABLE_OBJC_ARCÒ ÑÒ ^€’_6Apple LLVM compiler 3.1 - Language||GCC_ENABLE_OBJC_GCÒ ÑÒ a€’_0Apple LLVM compiler 3.1 - Language||OTHER_CFLAGSÒ ÑÒ d€’_8Apple LLVM compiler 3.1 - Language||OTHER_CPLUSPLUSFLAGSÒ ÑÒ g€’_@Apple LLVM compiler 3.1 - Language||GCC_PRECOMPILE_PREFIX_HEADERÒ ÑÒ j€’_5Apple LLVM compiler 3.1 - Language||GCC_PREFIX_HEADERÒ ÑÒ m€’_@Apple LLVM compiler 3.1 - Language||GCC_ENABLE_BUILTIN_FUNCTIONSÒ ÑÒ p€’_=Apple LLVM compiler 3.1 - Language||GCC_ENABLE_PASCAL_STRINGSÒ ÑÒ s€’_=Apple LLVM compiler 3.1 - Language||GCC_FORCE_CPU_SUBTYPE_ALLÒ ÑÒ v€’_3Apple LLVM compiler 3.1 - Language||GCC_SHORT_ENUMSÒ ÑÒ y€’_FApple LLVM compiler 3.1 - Language||GCC_USE_STANDARD_INCLUDE_SEARCHINGÒ ÑÒ |€’_ZApple LLVM compiler 3.1 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSÒ ÑÒ €’_DApple LLVM compiler 3.1 - Warnings||GCC_WARN_CHECK_SWITCH_STATEMENTSÒ ÑÒ ‚€’_EApple LLVM compiler 3.1 - Warnings||CLANG_WARN__EXIT_TIME_DESTRUCTORSÒ ÑÒ …€’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_FOUR_CHARACTER_CONSTANTSÒ ÑÒ ˆ€’_3Apple LLVM compiler 3.1 - Warnings||GCC_WARN_SHADOWÒ ÑÒ ‹€’_NApple LLVM compiler 3.1 - Warnings||CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESÒ ÑÒ Ž€’_DApple LLVM compiler 3.1 - Warnings||GCC_WARN_64_TO_32_BIT_CONVERSIONÒ ÑÒ ‘€’_GApple LLVM compiler 3.1 - Warnings||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONÒ ÑÒ ”€’_FApple LLVM compiler 3.1 - Warnings||GCC_WARN_ALLOW_INCOMPLETE_PROTOCOLÒ ÑÒ —€’_AApple LLVM compiler 3.1 - Warnings||GCC_WARN_INHIBIT_ALL_WARNINGSÒ ÑÒ š€’_LApple LLVM compiler 3.1 - Warnings||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDÒ ÑÒ €’_>Apple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_RETURN_TYPEÒ ÑÒ  €’_@Apple LLVM compiler 3.1 - Warnings||GCC_WARN_MISSING_PARENTHESESÒ ÑÒ £€’_MApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSÒ ÑÒ ¦€’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_MISSING_PROTOTYPESÒ ÑÒ ©€’_BApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_MISSING_NEWLINEÒ ÑÒ ¬€’_SApple LLVM compiler 3.1 - Warnings||GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTORÒ ÑÒ ¯€’_CApple LLVM compiler 3.1 - Warnings||GCC_WARN_NON_VIRTUAL_DESTRUCTORÒ ÑÒ ²€’_=Apple LLVM compiler 3.1 - Warnings||CLANG_WARN_OBJCPP_ARC_ABIÒ ÑÒ µ€’_2Apple LLVM compiler 3.1 - Warnings||WARNING_CFLAGSÒ ÑÒ ¸€’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSÒ ÑÒ »€’_NApple LLVM compiler 3.1 - Warnings||CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONSÒ ÑÒ ¾€’_5Apple LLVM compiler 3.1 - Warnings||GCC_WARN_PEDANTICÒ ÑÒ Á€’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_POINTER_SIGNEDNESSÒ ÑÒ Ä€’_9Apple LLVM compiler 3.1 - Warnings||GCC_WARN_SIGN_COMPAREÒ ÑÒ Ç€’_BApple LLVM compiler 3.1 - Warnings||GCC_WARN_STRICT_SELECTOR_MATCHÒ ÑÒ Ê€’_MApple LLVM compiler 3.1 - Warnings||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONÒ ÑÒ Í€’_ZApple LLVM compiler 3.1 - Warnings||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSÒ ÑÒ Ð€’_VApple LLVM compiler 3.1 - Warnings||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSÒ ÑÒ Ó€’_@Apple LLVM compiler 3.1 - Warnings||GCC_TREAT_WARNINGS_AS_ERRORSÒ ÑÒ Ö€’_FApple LLVM compiler 3.1 - Warnings||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFÒ ÑÒ Ù€’_@Apple LLVM compiler 3.1 - Warnings||GCC_WARN_UNDECLARED_SELECTORÒ ÑÒ Ü€’_@Apple LLVM compiler 3.1 - Warnings||GCC_WARN_UNINITIALIZED_AUTOSÒ ÑÒ ß€’_Static Analyzer - Checkers||CLANG_ANALYZER_DEADCODE_DEADSTORESÒ ÑÒ ý€’_9Static Analyzer - Checkers||CLANG_ANALYZER_OBJC_SELF_INITÒ ‘ €V¡ ÝÒ ÑÒ €’_Linking||LD_DYLIB_INSTALL_NAMEÓ   €<  Ó  €<¥   àáâãä¥     åæïðÃ_-Xcode3ProjectEditorPreviousProjectEditorClass_(Xcode3ProjectEditor.sourceList.splitview_,Xcode3ProjectEditorPreviousTargetEditorClass_,Xcode3ProjectEditorSelectedDocumentLocations_-Xcode3ProjectEditor_Xcode3BuildSettingsEditor_Xcode3BuildSettingsEditorÓ   !€<¡ ç¡ "è_DVTSplitViewItemsÒ ‘ &€V¢ ' (éíÓ + .€O¢ , -êë¢ç 0€êì]DVTIdentifier_DVTViewMagnitude#@e@Ó 6 9€O¢ , -êë¢ç ;€êî#@Œ@_Xcode3BuildSettingsEditorÒ ¹ @€@¡ AñÔÙ oÚ Cq E Fó÷ôò_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr.xcodeproj/#Aµ´Þ`ã…/Ó  K O€<£y M Nðõö£ P Q R÷øùVTarget_"Xcode3BuildSettingsEditorLocations_Xcode3BuildSettingsEditor\PcmMsrDriverÒ ¹ Y€@¡ ZúÓ  ] d€<¦ ^ _ ` a b cûüýþÿ¦D fDD iD€´€A€A€A_"Xcode3BuildPropertyNameDisplayMode_Selected Build Properties_$Xcode3BuildSettingsEditorDisplayMode_#Xcode3BuildPropertyValueDisplayMode_#Collapsed Build Property Categories_Xcode3BuildSettingsEditorModeÒ ‘ s€V¡ tÒ ÑÒ w€’_Kernel Module||MODULE_NAMEÒ ‘ z€V¯¿ { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÒ ÑÒ <€’_Architectures||ADDITIONAL_SDKSÒ ÑÒ ?€’_Architectures||ARCHSÒ ÑÒ B€’_Architectures||SDKROOTÒ ÑÒ E€’_ Architectures||ONLY_ACTIVE_ARCHÒ ÑÒ H€’_#Architectures||SUPPORTED_PLATFORMSÒ ÑÒ K€’_Architectures||VALID_ARCHSÒ ÑÒ N€’_Build Locations||SYMROOTÒ ÑÒ Q€’_Build Locations||OBJROOTÒ ÑÒ T€’_%Build Locations||SHARED_PRECOMPS_DIRÒ ÑÒ W€’_Build Options||BUILD_VARIANTSÒ ÑÒ Z€’_Build Options||GCC_VERSIONÒ ÑÒ ]€’_'Build Options||GENERATE_PROFILING_CODEÒ ÑÒ `€’_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRÒ ÑÒ c€’_)Build Options||RUN_CLANG_STATIC_ANALYZERÒ ÑÒ f€’_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESÒ ÑÒ i€’_ Build Options||VALIDATE_PRODUCTÒ ÑÒ l€’_%Code Signing||CODE_SIGN_ENTITLEMENTSÒ ÑÒ o€’_!Code Signing||CODE_SIGN_IDENTITYÒ ÑÒ r€’_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHÒ ÑÒ u€’_$Code Signing||OTHER_CODE_SIGN_FLAGSÒ ÑÒ x€’_Deployment||STRIPFLAGSÒ ÑÒ {€’_Deployment||ALTERNATE_GROUPÒ ÑÒ ~€’_Deployment||ALTERNATE_OWNERÒ ÑÒ €’_Deployment||ALTERNATE_MODEÒ ÑÒ „€’_(Deployment||ALTERNATE_PERMISSIONS_FILESÒ ÑÒ ‡€’_!Deployment||COMBINE_HIDPI_IMAGESÒ ÑÒ Š€’_ Deployment||DEPLOYMENT_LOCATIONÒ ÑÒ €’_&Deployment||DEPLOYMENT_POSTPROCESSINGÒ ÑÒ €’_Deployment||INSTALL_GROUPÒ ÑÒ “€’_Deployment||INSTALL_OWNERÒ ÑÒ –€’_Deployment||INSTALL_MODE_FLAGÒ ÑÒ ™€’_Deployment||DSTROOTÒ ÑÒ œ€’_Deployment||INSTALL_PATHÒ ÑÒ Ÿ€’_%Deployment||MACOSX_DEPLOYMENT_TARGETÒ ÑÒ ¢€’_%Deployment||PRODUCT_DEFINITION_PLISTÒ ÑÒ ¥€’_Deployment||SKIP_INSTALLÒ ÑÒ ¨€’_$Deployment||STRIP_INSTALLED_PRODUCTÒ ÑÒ «€’_Deployment||STRIP_STYLEÒ ÑÒ ®€’_Deployment||SEPARATE_STRIPÒ ÑÒ ±€’_Kernel Module||MODULE_NAMEÒ ÑÒ ´€’_Kernel Module||MODULE_STARTÒ ÑÒ ·€’_Kernel Module||MODULE_STOPÒ ÑÒ º€’_Kernel Module||MODULE_VERSIONÒ ÑÒ ½€’_Linking||BUNDLE_LOADERÒ ÑÒ À€’_%Linking||DYLIB_COMPATIBILITY_VERSIONÒ ÑÒ Ã€’_Linking||DYLIB_CURRENT_VERSIONÒ ÑÒ Æ€’_Linking||DEAD_CODE_STRIPPINGÒ ÑÒ É€’_'Linking||LINKER_DISPLAYS_MANGLED_NAMESÒ ÑÒ Ì€’_Linking||LD_NO_PIEÒ ÑÒ Ï€’_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSÒ ÑÒ Ò€’_Linking||LD_DYLIB_INSTALL_NAMEÒ ÑÒ Õ€’_Linking||EXPORTED_SYMBOLS_FILEÒ ÑÒ Ø€’_Linking||INIT_ROUTINEÒ ÑÒ Û€’_&Linking||LINK_WITH_STANDARD_LIBRARIESÒ ÑÒ Þ€’_Linking||MACH_O_TYPEÒ ÑÒ á€’_Linking||ORDER_FILEÒ ÑÒ ä€’_Linking||OTHER_LDFLAGSÒ ÑÒ ç€’_%Linking||GENERATE_MASTER_OBJECT_FILEÒ ÑÒ ê€’_Linking||PRELINK_LIBSÒ ÑÒ í€’_Linking||KEEP_PRIVATE_EXTERNSÒ ÑÒ ð€’_!Linking||LD_RUNPATH_SEARCH_PATHSÒ ÑÒ ó€’_Linking||SEPARATE_SYMBOL_EDITÒ ÑÒ ö€’_Linking||PRELINK_FLAGSÒ ÑÒ ù€’_Linking||SECTORDER_FLAGSÒ ÑÒ ü€’_!Linking||UNEXPORTED_SYMBOLS_FILEÒ ÑÒ ÿ€’_Linking||WARNING_LDFLAGSÒ ÑÒ €’_Linking||LD_GENERATE_MAP_FILEÒ ÑÒ €’_%Packaging||APPLY_RULES_IN_COPY_FILESÒ ÑÒ €’_ Packaging||EXECUTABLE_EXTENSIONÒ ÑÒ €’_Packaging||EXECUTABLE_PREFIXÒ ÑÒ €’_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSÒ ÑÒ €’_!Packaging||GENERATE_PKGINFO_FILEÒ ÑÒ €’_Packaging||FRAMEWORK_VERSIONÒ ÑÒ €’_Packaging||INFOPLIST_FILEÒ ÑÒ €’_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSÒ ÑÒ €’_#Packaging||INFOPLIST_OUTPUT_FORMATÒ ÑÒ €’_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSÒ ÑÒ #€’_#Packaging||INFOPLIST_PREFIX_HEADERÒ ÑÒ &€’_ Packaging||INFOPLIST_PREPROCESSÒ ÑÒ )€’_&Packaging||COPYING_PRESERVES_HFS_DATAÒ ÑÒ ,€’_'Packaging||PRIVATE_HEADERS_FOLDER_PATHÒ ÑÒ /€’_Packaging||PRODUCT_NAMEÒ ÑÒ 2€’_$Packaging||PLIST_FILE_OUTPUT_FORMATÒ ÑÒ 5€’_&Packaging||PUBLIC_HEADERS_FOLDER_PATHÒ ÑÒ 8€’_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGÒ ÑÒ ;€’_Packaging||WRAPPER_EXTENSIONÒ ÑÒ >€’_'Search Paths||ALWAYS_SEARCH_USER_PATHSÒ ÑÒ A€’_%Search Paths||FRAMEWORK_SEARCH_PATHSÒ ÑÒ D€’_#Search Paths||LIBRARY_SEARCH_PATHSÒ ÑÒ G€’_Search Paths||REZ_SEARCH_PATHSÒ ÑÒ J€’_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ ÑÒ M€’_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ ÑÒ P€’_'Search Paths||USER_HEADER_SEARCH_PATHSÒ ÑÒ S€’_Unit Testing||OTHER_TEST_FLAGSÒ ÑÒ V€’_Unit Testing||TEST_AFTER_BUILDÒ ÑÒ Y€’_Unit Testing||TEST_HOSTÒ ÑÒ \€’_Unit Testing||TEST_RIGÒ ÑÒ _€’_$Versioning||CURRENT_PROJECT_VERSIONÒ ÑÒ b€’_Versioning||VERSION_INFO_FILEÒ ÑÒ e€’_%Versioning||VERSION_INFO_EXPORT_DECLÒ ÑÒ h€’_ Versioning||VERSION_INFO_PREFIXÒ ÑÒ k€’_ Versioning||VERSION_INFO_SUFFIXÒ ÑÒ n€’_Versioning||VERSIONING_SYSTEMÒ ÑÒ q€’_!Versioning||VERSION_INFO_BUILDERÒ ÑÒ t€’_AApple LLVM compiler 3.1 - Code Generation||GCC_FAST_OBJC_DISPATCHÒ ÑÒ w€’_HApple LLVM compiler 3.1 - Code Generation||CLANG_X86_VECTOR_INSTRUCTIONSÒ ÑÒ z€’_>Apple LLVM compiler 3.1 - Code Generation||GCC_STRICT_ALIASINGÒ ÑÒ }€’_IApple LLVM compiler 3.1 - Code Generation||GCC_GENERATE_DEBUGGING_SYMBOLSÒ ÑÒ €€’_=Apple LLVM compiler 3.1 - Code Generation||GCC_DYNAMIC_NO_PICÒ ÑÒ ƒ€’_KApple LLVM compiler 3.1 - Code Generation||GCC_GENERATE_TEST_COVERAGE_FILESÒ ÑÒ †€’_IApple LLVM compiler 3.1 - Code Generation||GCC_INLINES_ARE_PRIVATE_EXTERNÒ ÑÒ ‰€’_KApple LLVM compiler 3.1 - Code Generation||GCC_INSTRUMENT_PROGRAM_FLOW_ARCSÒ ÑÒ Œ€’_HApple LLVM compiler 3.1 - Code Generation||GCC_ENABLE_KERNEL_DEVELOPMENTÒ ÑÒ €’_3Apple LLVM compiler 3.1 - Code Generation||LLVM_LTOÒ ÑÒ ’€’_Apple LLVM compiler 3.1 - Language||GCC_ENABLE_OBJC_EXCEPTIONSÒ ÑÒ Å€’_8Apple LLVM compiler 3.1 - Language||GCC_ENABLE_TRIGRAPHSÒ ÑÒ È€’_KApple LLVM compiler 3.1 - Language||GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLSÒ ÑÒ Ë€’_CApple LLVM compiler 3.1 - Language||GCC_USE_INDIRECT_FUNCTION_CALLSÒ ÑÒ Î€’_CApple LLVM compiler 3.1 - Language||GCC_USE_REGISTER_FUNCTION_CALLSÒ ÑÒ Ñ€’_KApple LLVM compiler 3.1 - Language||GCC_INCREASE_PRECOMPILED_HEADER_SHARINGÒ ÑÒ Ô€’_9Apple LLVM compiler 3.1 - Language||CLANG_ENABLE_OBJC_ARCÒ ÑÒ ×€’_6Apple LLVM compiler 3.1 - Language||GCC_ENABLE_OBJC_GCÒ ÑÒ Ú€’_0Apple LLVM compiler 3.1 - Language||OTHER_CFLAGSÒ ÑÒ Ý€’_8Apple LLVM compiler 3.1 - Language||OTHER_CPLUSPLUSFLAGSÒ ÑÒ à€’_@Apple LLVM compiler 3.1 - Language||GCC_PRECOMPILE_PREFIX_HEADERÒ ÑÒ ã€’_5Apple LLVM compiler 3.1 - Language||GCC_PREFIX_HEADERÒ ÑÒ æ€’_@Apple LLVM compiler 3.1 - Language||GCC_ENABLE_BUILTIN_FUNCTIONSÒ ÑÒ é€’_=Apple LLVM compiler 3.1 - Language||GCC_ENABLE_PASCAL_STRINGSÒ ÑÒ ì€’_=Apple LLVM compiler 3.1 - Language||GCC_FORCE_CPU_SUBTYPE_ALLÒ ÑÒ ï€’_3Apple LLVM compiler 3.1 - Language||GCC_SHORT_ENUMSÒ ÑÒ ò€’_FApple LLVM compiler 3.1 - Language||GCC_USE_STANDARD_INCLUDE_SEARCHINGÒ ÑÒ õ€’_ZApple LLVM compiler 3.1 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSÒ ÑÒ ø€’_DApple LLVM compiler 3.1 - Warnings||GCC_WARN_CHECK_SWITCH_STATEMENTSÒ ÑÒ û€’_EApple LLVM compiler 3.1 - Warnings||CLANG_WARN__EXIT_TIME_DESTRUCTORSÒ ÑÒ þ€’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_FOUR_CHARACTER_CONSTANTSÒ ÑÒ €’_3Apple LLVM compiler 3.1 - Warnings||GCC_WARN_SHADOWÒ ÑÒ €’_NApple LLVM compiler 3.1 - Warnings||CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIESÒ ÑÒ €’_DApple LLVM compiler 3.1 - Warnings||GCC_WARN_64_TO_32_BIT_CONVERSIONÒ ÑÒ €’_GApple LLVM compiler 3.1 - Warnings||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONÒ ÑÒ €’_FApple LLVM compiler 3.1 - Warnings||GCC_WARN_ALLOW_INCOMPLETE_PROTOCOLÒ ÑÒ €’_AApple LLVM compiler 3.1 - Warnings||GCC_WARN_INHIBIT_ALL_WARNINGSÒ ÑÒ €’_LApple LLVM compiler 3.1 - Warnings||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDÒ ÑÒ €’_>Apple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_RETURN_TYPEÒ ÑÒ €’_@Apple LLVM compiler 3.1 - Warnings||GCC_WARN_MISSING_PARENTHESESÒ ÑÒ €’_MApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSÒ ÑÒ €’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_MISSING_PROTOTYPESÒ ÑÒ "€’_BApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_MISSING_NEWLINEÒ ÑÒ %€’_SApple LLVM compiler 3.1 - Warnings||GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTORÒ ÑÒ (€’_CApple LLVM compiler 3.1 - Warnings||GCC_WARN_NON_VIRTUAL_DESTRUCTORÒ ÑÒ +€’_=Apple LLVM compiler 3.1 - Warnings||CLANG_WARN_OBJCPP_ARC_ABIÒ ÑÒ .€’_2Apple LLVM compiler 3.1 - Warnings||WARNING_CFLAGSÒ ÑÒ 1€’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSÒ ÑÒ 4€’_NApple LLVM compiler 3.1 - Warnings||CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONSÒ ÑÒ 7€’_5Apple LLVM compiler 3.1 - Warnings||GCC_WARN_PEDANTICÒ ÑÒ :€’_EApple LLVM compiler 3.1 - Warnings||GCC_WARN_ABOUT_POINTER_SIGNEDNESSÒ ÑÒ =€’_9Apple LLVM compiler 3.1 - Warnings||GCC_WARN_SIGN_COMPAREÒ ÑÒ @€’_BApple LLVM compiler 3.1 - Warnings||GCC_WARN_STRICT_SELECTOR_MATCHÒ ÑÒ C€’_MApple LLVM compiler 3.1 - Warnings||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONÒ ÑÒ F€’_ZApple LLVM compiler 3.1 - Warnings||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSÒ ÑÒ I€’_VApple LLVM compiler 3.1 - Warnings||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSÒ ÑÒ L€’_@Apple LLVM compiler 3.1 - Warnings||GCC_TREAT_WARNINGS_AS_ERRORSÒ ÑÒ O€’_FApple LLVM compiler 3.1 - Warnings||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFÒ ÑÒ R€’_@Apple LLVM compiler 3.1 - Warnings||GCC_WARN_UNDECLARED_SELECTORÒ ÑÒ U€’_@Apple LLVM compiler 3.1 - Warnings||GCC_WARN_UNINITIALIZED_AUTOSÒ ÑÒ X€’_Static Analyzer - Checkers||CLANG_ANALYZER_DEADCODE_DEADSTORESÒ ÑÒ v€’_9Static Analyzer - Checkers||CLANG_ANALYZER_OBJC_SELF_INITÓ  y z€<  Ó  } ¯€<¯1 ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥X § ¨  ª « ¬ ­ ®ÅÇÉËÍÏÑÓÕ×ÙÛÝßáãåçéëíïñóõ÷ùûýÿ   ™€!¯1 ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à#+258<?GJNUX\`hlpsw{ƒ†ŠŽ’–šž¡¥©­°³·»¿ÃÇÊÒÕÙÝáåéìÓ  ãµ€UÆ€Ò ÑÒ ç€’_?file://localhost/Users/aiott/Desktop/PcmMsr/DriverInterface.cppÓ  êµ€UÈ€Ò ÑÒ î€’_¸file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/sys/appleapiopts.hÓ  ñµ€UÊ€Ò ÑÒ õ€’_ºfile://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/TargetConditionals.hÓ  øµ€UÌ€Ò ÑÒ ü€’_²file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/sys/kernel.hÓ  ÿµ€UÎ€Ò ÑÒ€’_;file://localhost/Users/aiott/Desktop/PcmMsr/MSRAccessor.cppÓ µ€UÐ€Ò ÑÒ €’_»file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/mach/processor_info.hÓ  µ€UÒ€Ò ÑÒ€’_Jfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/MSRAccessor.cppÓ µ€UÔ€Ò ÑÒ€’_¶file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/libkern/sysctl.hÓ µ€UÖ€Ò ÑÒ€’_efile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/MSRAccessor.cppÓ "µ€UØ€Ò ÑÒ&€’_\file://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/en.lproj/InfoPlist.stringsÓ )µ€UÚ€Ò ÑÒ-€’_Mfile://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/en.lproj/InfoPlist.stringsÓ 0µ€UÜ€Ò ÑÒ4€’_>file://localhost/Users/aiott/Desktop/PcmMsr/UserKernelShared.hÓ 7µ€UÞ€Ò ÑÒ;€’_Efile://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/UserKernelShared.hÓ >µ€Uà€Ò ÑÒB€’_Tfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/UserKernelShared.hÓ Eµ€Uâ€Ò ÑÒI€’_ofile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/UserKernelShared.hÓ Lµ€Uä€Ò ÑÒP€’_½file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/i386/machine_routines.hÓ Sµ€Uæ€Ò ÑÒW€’_³file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/i386/eflags.hÓ Zµ€Uè€Ò ÑÒ^€’_Lfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/DriverInterface.cÓ aµ€Uê€Ò ÑÒe€’_=file://localhost/Users/aiott/Desktop/PcmMsr/DriverInterface.cÓ hµ€Uì€Ò ÑÒl€’_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/DriverInterface.cÓ oµ€Uî€Ò ÑÒs€’_Lfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/PcmMsr.cppÓ vµ€Uð€Ò ÑÒz€’_¶file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/mach/host_info.hÓ }µ€Uò€Ò ÑÒ€’_=file://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsr.cppÓ „µ€Uô€Ò ÑÒˆ€’_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.cppÓ ‹µ€Uö€Ò ÑÒ€’_‡file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/include/stdlib.hÓ ’µ€Uø€Ò ÑÒ–€’_=file://localhost/Users/aiott/Desktop/PcmMsr/DriverInterface.hÓ ™µ€Uú€Ò ÑÒ€’_Lfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/DriverInterface.hÓ  µ€Uü€Ò ÑÒ¤€’_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/DriverInterface.hÓ §µ€Uþ€Ò ÑÒ«€’_³file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/kern/thread.hÓ ®µ€U€Ò ÑÒ²€’_;file://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsr.hÓ µµ€U€Ò ÑÒ¹€’_Cfile://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsrClient.cppÓ ¼µ€U€Ò ÑÒÀ€’_Rfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/PcmMsrClient.cppÓ õ€U€Ò ÑÒÇ€’_´file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/mach/machine.hÓ ʵ€U€Ò ÑÒ΀’_²file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/i386/cpuid.hÓ ѵ€U €Ò ÑÒÕ€’_Jfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/PcmMsr.hÓ ص€U €Ò ÑÒÜ€’_9file://localhost/Users/aiott/Desktop/PcmMsr/MSRAccessor.hÓ ßµ€U€Ò ÑÒ〒_Hfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/MSRAccessor.hÓ æµ€U€Ò ÑÒꀒ_cfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/MSRAccessor.hÓ íµ€U€Ò ÑÒñ€’_efile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.hÓ ôµ€U€Ò ÑÒø€’_±file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/kern/task.hÓ ûµ€U€Ò ÑÒÿ€’_Dfile://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsr-Prefix.pchÓ µ€U€Ò ÑÒ€’_mfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsrClient.cppÓ  µ€U€Ò ÑÒ €’_Afile://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsrClient.hÓ µ€U€Ò ÑÒ€’_Pfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/PcmMsrClient.hÓ µ€U€Ò ÑÒ€’_kfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsrClient.hÓ µ€U €Ò ÑÒ"€’_·file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Headers/libkern/libkern.hÓ %µ€U"€Ò ÑÒ)€’_Âfile://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/libkern/libkern.hÓ ,1€<¤-./0$%&'¤23$5()€M*_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aµp7/uÚY{0, 2535}X{225, 0}Ó ?D€<¤@ABC,-./¤EF$01€MÝ_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aµuo/Õ{Y{0, 2070}Ó QV€<¤@ABC,-./¤WX$34€MÝ#AµuoKÖð\{9561, 3216}Ó _d€<¤@ABC,-./¤ef$67€MÝ#Aµuo&]SXY{0, 3719}Ó mr€<¤@ABC,-./¤st$v9:€M;#Aµy†Å䪹Y{0, 2015}X{970, 0}Ó |€<¤@ABC,-./¤‚ƒ$=>€MÝ#Aµup8\\{2626, 3174}Ó Š€<¤‹ŒŽ@ABC¤‘$“DE€MF_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aµ¦:»|5\{2158, 1775}Y{1446, 0}Ó ¢€<¤@ABC,-./¤£¤$HI€MÝ#Aµu’Ÿtîò\{1390, 4975}Ó «°€<¤ÛÜÝÞ€”€•€–€—¤±²$´KL€MM#Aµ½¥ÒéTÊ\{1496, 1822}Y{2867, 0}Ó º¿€<¤»¼½¾OPQR¤ÀÁ$ST€MÝ_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aµ«Oïmþ¨W{0, 45}Ó ÌÑ€<¤@ABC,-./¤ÒÓ$VW€MÝ#Aµum»,Î’W{0, 45}Ó Ú߀<¤@ABC,-./¤àá$ãYZ€M[#Aµy†r]X{0, 390}X{271, 0}Ó éî€<¤@ABC,-./¤ïð$ò]^€M_#Aµy†³™$JX{0, 774}X{654, 0}Ó øý€<¤ùúûüabcd¤þÿ$ef€Mg_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#Aµ£‘K;{)X{0, 923}X{262, 0}Ó  €<¤ÛÜÝÞ€”€•€–€—¤$ij€Mk#Aµ¸«Ua†UY{0, 2583}Y{1886, 0}Ó €<¤@ABC,-./¤ !$#mn€Mo#AµvÒ B5[{113, 2095}Y{1431, 0}Ó ).€<¤@ABC,-./¤/0$qr€MÝ#AµvÒš•hY{0, 2505}Ó 7<€<¤‹ŒŽ@ABC¤=>$@tu€Mv#Aµ¥úÃâµÔY{0, 1583}Y{5438, 0}Ó FK€<¤@ABC,-./¤LM$Oxy€Mz#Aµy†Õáo3[{683, 2532}Y{1277, 0}Ó UZ€<¤ÛÜÝÞ€”€•€–€—¤[\$^|}€M~#Aµ½¦¦“ž\{8399, 2546}W{17, 0}Ó di€<¤‹ŒŽ@ABC¤jk$m€€M‚#Aµ¦;F÷&\{1942, 2082}Y{1170, 0}Ó sx€<¤@ABC,-./¤yz$„…€MÝ#AµuomI0\{5145, 3006}Ó †€<¤@ABC,-./¤‡ˆ$Їˆ€M‰#Aµ}–7É\{9240, 2951}Ó $)€<¤@ABC,-./¤*+$±²€MÝ#AµvÑA¹h”\{5731, 2051}Ó 27€<¤‹ŒŽ@ABC¤89$;´µ€M¶#Aµ¥ú?Èc|X{0, 950}X{950, 0}Ó AF€<¤@ABC,-./¤GH$J¸¹€Mº#Aµy†wZˆ{X{0, 511}X{305, 0}Ó PU€<¤ùúûüabcd¤VW$Y¼½€M¾#Aµ£•›iÇiX{0, 734}X{710, 0}Ó _d€<¤ÛÜÝÞ€”€•€–€—¤ef$hÀÁ€MÂ#Aµ½¦¤ŸY{0, 2104}Y{1628, 0}Ó ns€<¤ÛÜÝÞ€”€•€–€—¤tu$wÄÅ€MÆ#Aµ½«r‰Š"Y{0, 2672}Y{2636, 0}Ó }‚€<¤@ABC,-./¤ƒ„$ÈÉ€MÝ#Aµunô’à;\{1756, 2347}Ó ‹€<¤ŒŽËÌÍΤ‘’$”ÏЀMÑ_PrimaryDocumentTimestamp_$PrimaryDocumentVisibleCharacterRange]HideAllIssues_%PrimaryDocumentSelectedCharacterRange#AµnëS¹ºY{0, 1457}Y{1371, 0}Ó ž£€<¤@ABC,-./¤¤¥$ÓÔ€MÝ#Aµum¹Ÿ2ßW{0, 92}Ó ¬±€<¤ÛÜÝÞ€”€•€–€—¤²³$µÖ×€MØ#AµÈ2u…æ¬[{546, 3487}Z{1656, 12}Ó »À€<¤ÛÜÝÞ€”€•€–€—¤ÁÂ$ÄÚÛ€MÜ#AµÈ2z‹»¥\{5637, 2449}Z{5280, 45}Ó ÊÏ€<¤@ABC,-./¤ÐÑ$ÓÞ߀Mà#Aµyl´èá‚Y{0, 1691}Y{1686, 0}Ó ÙÞ€<¤ùúûüabcd¤ßà$ââã€Mä#Aµ£6 Ù„Y{0, 2444}Y{1810, 0}Ó èí€<¤ÛÜÝÞ€”€•€–€—¤îï$ñæç€Mè#Aµ¸žÕsŽ~Y{0, 3991}Y{3044, 0}Ó ÷ü€<¤@ABC,-./¤ýþ$êë€MÝ#AµvÑÌ4ô\{5533, 1708}Ó  €<¤êëìí€G€H€I€J¤  $íî€Mï#Aµ´È˃Ǔ\{2469, 1926}Y{4875, 0}Ó ;€<¯& !"#$%&'()*+,-./0123456789:ñóõ÷ùûýÿ   !#%')+-/13579;¯&<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a=@BCDFGHJKLNOPXY[\^_`aefghinopsvw|}~€Ó dµ€Uò€_2x-xcode-log://5A288867-C894-4F61-B588-6C2CBA58878CÓ iµ€Uô€_2x-xcode-log://0525A4CA-06C9-4080-AF6D-0A5355DC99EDÓ nµ€Uö€_2x-xcode-log://45E1ED50-2C99-4E9F-A621-9B48EDEAC6AAÓ sµ€Uø€_2x-xcode-log://8D085A3A-D24F-43D7-9B2C-F53FD6A8038FÓ xµ€Uú€_2x-xcode-log://EB047792-AD33-4C6A-99D0-54184F5CE7AEÓ }µ€Uü€_2x-xcode-log://C862713F-672F-46F3-B5C5-C12EC96E89FCÓ ‚µ€Uþ€_2x-xcode-log://9D94ADAA-D3D0-4395-BCCD-D57ED4E8841DÓ ‡µ€U€_2x-xcode-log://451440CB-B8B4-4C96-81EE-3C6A12BB784FÓ Œµ€U€_2x-xcode-log://EBE62B83-E49F-4D45-A94B-F4D244006DFFÓ ‘µ€U€_2x-xcode-log://0B106FA5-32DD-466B-A18F-AFFF5BDDFCADÓ –µ€U€_2x-xcode-log://543B16A3-F5F4-48F9-BF50-7EA6872DF471Ó ›µ€U€_2x-xcode-log://5C1F001B-F44E-4400-BECE-29680D26A236Ó  µ€U €_2x-xcode-log://FB141C82-5056-4A95-833C-F2A79AA5942DÓ ¥µ€U €_2x-xcode-log://54AA8DBD-B199-48C3-A1C2-5F7BB00B881BÓ ªµ€U€_2x-xcode-log://1FADA345-13B5-4673-930C-E64AE795D47DÓ ¯µ€U€_2x-xcode-log://2BED104D-FF15-485A-9A15-CADF412F26B4Ó ´µ€U€_2x-xcode-log://5D3E2E80-4DE8-4A86-AFAA-6444E5FA3896Ó ¹µ€U€_2x-xcode-log://3FACC23D-8E7B-4601-B682-08B73406726AÓ ¾µ€U€_2x-xcode-log://3C0A2304-614E-4753-9FAC-93027E06F5CBÓ õ€U€_2x-xcode-log://56D4903F-B532-4835-93D4-13C933B10A4EÓ ȵ€U€_2x-xcode-log://84BE9950-103D-441D-BF3A-65D41491813EÓ ͵€U€_2x-xcode-log://25075FC2-D936-4BE1-94AA-12BC362B7498Ó Òµ€U€_2x-xcode-log://228112D9-E0F3-4500-9CFC-07EAA0495A2AÓ ×µ€U €_2x-xcode-log://6570D0AE-6A8A-4190-AE44-D6278CE134D8Ó ܵ€U"€_2x-xcode-log://9D796B5E-9990-4B0F-AA4A-8668466D2780Ó áµ€U$€_2x-xcode-log://B8C0EC08-D0BF-4674-8C3C-2AED7273DAC8Ó æµ€U&€_2x-xcode-log://AE30E8E6-3499-49F6-BE29-C250DEE03D41Ó ëµ€U(€_2x-xcode-log://D026A6F9-5F65-45D2-B0BB-E65CFAF1AE7DÓ ðµ€U*€_2x-xcode-log://26F2CDD2-3262-42D5-AAC8-6727C2834758Ó õµ€U,€_2x-xcode-log://0B7F1F62-A3FE-4B91-AA39-E8210DAC3ACEÓ úµ€U.€_2x-xcode-log://D8449429-94F1-4F0F-BC95-EBB2912DF67EÓ ÿµ€U0€_2x-xcode-log://6DDF14AA-27CD-492C-AD80-23C70C93B46DÓ µ€U2€_2x-xcode-log://E12DDE21-C526-45EE-B482-3E4E40E2FA3BÓ  µ€U4€_2x-xcode-log://4AB75C9C-325D-44EF-86DA-7232574FDB9AÓ µ€U6€_2x-xcode-log://04C144C7-A301-4A10-BBD4-4FFB1F7566FDÓ µ€U8€_2x-xcode-log://DE67AAEC-D87C-4A25-9F26-1B54600712B5Ó µ€U:€_2x-xcode-log://3E9118F4-C53D-408A-B944-FEC464765ECFÓ µ€U<€_2x-xcode-log://4B536260-1FA1-4668-82C3-E9F8762F3FBBÓ "$€<¡#>¡%?_SelectedDocumentLocationsÒ ¹)€@ Ó ,.€<¡-A¡%?_SelectedDocumentLocationsÓ 35€<¡#>¡%?Ó 9;€<¡-A¡%?Ó ?A€<¡@E¡%?_SelectedDocumentLocationsÓ FH€<¡#>¡%?Ó LN€<¡#>¡%?Ó RT€<¡SI¡%?_SelectedDocumentLocationsÓ Y[€<¡#>¡%?Ó _a€<¡#>¡%?Ó eg€<¡fM¡%?_SelectedDocumentLocationsÓ ln€<¡-A¡%?Ó rt€<¡#>¡%?Ó xz€<¡#>¡{QÒ ¹~€@¡RÕÙ‚ Úµù…†¥_expandTranscriptYindexPath€SW Ó ‰Š‹Œ×_NSIndexPathData_NSIndexPathLengthVTÒ ŽWNS.dataUBÒÆÇ’“]NSMutableData£’”ËVNSDataÒÆÇ–—[NSIndexPath¢˜Ë[NSIndexPathÒÆÇš›_IDELogDocumentLocation£œË_IDELogDocumentLocation_DVTDocumentLocationÓ  ¢€<¡-A¡%?Ó ¦¨€<¡§Z¡%?_SelectedDocumentLocationsÓ ­¯€<¡-A¡%?Ó ³µ€<¡´]¡%?_SelectedDocumentLocationsÓ º¼€<¡-A¡%?Ó À€<¡-A¡%?Ó ÆÈ€<¡´]¡%?Ó Ì΀<¡-A¡ÏbÒ ¹Ò€@¡ÓcÕÙ‚ Úµù׆̀dWÓ ŠÛ‹Ý_NSIndexPathValueVÓ àâ€<¡#>¡%?Ó æè€<¡#>¡%?Ó ìî€<¡#>¡%?Ó òô€<¡´]¡%?Ó øú€<¡#>¡ûjÒ ¹þ€@¡ÿkÕÙ‚ Úµù†æ€lW&Ó ‰Š‹×VmÒ Ž UBÓ €<¡#>¡%?Ó €<¡-A¡%?Ó €<¡-A¡qÒ ¹ €@¡!rÕÙ‚ Úµù׆õ€dW,Ó *,€<¡-A¡-tÒ ¹0€@¡1uÕÙ‚ Úµù׆ú€dW.Ó :<€<¡#>¡%?Ó @B€<¡Ax¡Cy_SelectedDocumentLocationsÒ ¹G€@¡HzÕÙ‚ ÚµùL†€{W2Ó ŠÛ‹QV Ó TV€<¡Ax¡%?Ó Z\€<¡#>¡%?Ó `b€<¡-A¡%?Ó fh€<¡-A¡%?Ó ln€<¡-A¡%?Ó rt€<¡X™¡u‚Ó x|€<£yz{ƒ„…£}~†ˆ‰_IDE_PLIST_EDITOR_SELECTION_KEY_ IDE_PLIST_EDITOR_VISIBLERECT_KEY_IDE_PLIST_EDITOR_EXPANSION_KEYÒ ¹…€@¡†‡_OSBundleLibraries_{{0, 0}, {880, 828}}Ò Q‹¡ŒŠÒ ‘€V¡†‡Ó “–€<¢”•Œ¢—˜Ž_IDEDeviceLocation_IDEDeviceArchitecture_"dvtdevice-local-computer:localhostVx86_64Ó Ÿ¡€<¡ ‘¡¢’]IDENameString]PcmMsrLibraryÓ §«€<£¨©ª”•–£¬­ —°?_0IDEActivityReportCompletionSummaryStringSegments_IDEActivityReportOptions_IDEActivityReportTitleÒ ‘´€V¤µ¶·¸˜Ÿ£§Ó »¿€<£¼½¾™š›£ÀÁœž_&IDEActivityReportStringSegmentPriority_+IDEActivityReportStringSegmentBackSeparator_)IDEActivityReportStringSegmentStringValue#@Q UBuildÓ ËÏ€<£¼½¾™š›£ÐÑÒ ¡¢#@R: ]PcmMsrLibraryÓ ØÜ€<£¼½¾™š›£ÝÞߤ¥¦#?ðc % Ò ŽäUO\bplist00Ô;Troot€#-27EKR_foqsu†Ž™›ž ¢¥§©°¸ÁÈÏØÚÜåèü (/<DFHKPX[`mpuŠ¢´·¼?¾Ó çî€<¦¼éê¾ì홨©›ª«¦ïDñòDD¬€´­¯€´€´_"IDEActivityReportStringSegmentType_"IDEActivityReportStringSegmentDate_'IDEActivityReportStringSegmentDateStyle_'IDEActivityReportStringSegmentTimeStyle#@Òû üýWNS.time#Aµ¸Ÿ £3®ÒÆÇÿVNSDate¢ÿË_7/19/12 at 1:01 PMêÒ ‘€V¡€Ó  €<«    ³µ·¹»½¿ÁÃÅÇ« ÉÎÒÖÚÞáåèëîÓ #µ€U´€_;file://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsr.hÓ (µ€U¶€_Jfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/PcmMsr.hÓ -µ€U¸€_=file://localhost/Users/aiott/Desktop/PcmMsr/DriverInterface.hÓ 2µ€Uº€_=file://localhost/Users/aiott/Desktop/PcmMsr/DriverInterface.cÓ 7µ€U¼€_Afile://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsrClient.hÓ <µ€U¾€_;file://localhost/Users/aiott/Desktop/PcmMsr/MSRAccessor.cppÓ Aµ€UÀ€_Jfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/MSRAccessor.cppÓ Fµ€U€_=file://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr/PcmMsr.cppÓ Kµ€UÄ€_=file://localhost/Users/aiott/Desktop/PcmMsr/PcmMsr.xcodeproj/Ó Pµ€UÆ€_Lfile://localhost/Users/aiott/projects/pcm/MacOS_MSR_Driver/PcmMsr/PcmMsr.cppÓ Uµ€UÈ€_‡file://localhost/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/usr/include/stdlib.hÓ Z]€O¢[\ÊË¢^_ÌÍUwidthVheight#@‚À#@‚ÀÓ fi€O¢g\ÏË¢jkÐÑUwidth#@‚À#@‚ÀÓ qt€O¢r\ÓË¢uvÔÕUwidth#@‚À#@‚ÀÓ |€O¢}\×Ë¢€ØÙUwidth#@ŽH#@‚ÀÓ ‡Š€O¢ˆ\ÛË¢‹ŒÜÝUwidth#@‚À#@‚ÀÓ ’•€O¢}\×Ë¢–—ßà#@‚À#@‚ÀÓ œŸ€O¢\âË¢ ¡ãäUwidth#@‚À#@‚ÀÓ §ª€O¢ˆ\ÛË¢«¬æç#@‚À#@‚ÀÓ ±´€O¢[\ÊË¢µ¶éê#@‚À#@‚ÀÓ »¾€O¢g\ÏË¢¿Àìí#@‘°#@‚ÀÓ ÅÈ€O¢Æ\ïË¢ÉÊðñUwidth#@‚À#@‚ÀÒ ‘ЀVª ÒÓÔÕÖרÙÚ€óõ÷ùûýÿÓ ݵ€Uô€Ò ÑÒဒ_mfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsrClient.cppÓ äµ€Uö€Ò ÑÒ耒_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.cppÓ ëµ€Uø€Ò ÑÒ_efile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsr.hÓ òµ€Uú€Ò ÑÒö€’_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/DriverInterface.cÓ ùµ€Uü€Ò ÑÒý€’_cfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/MSRAccessor.hÓ µ€Uþ€Ò ÑÒ€’_efile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/MSRAccessor.cppÓ µ€U€Ò ÑÒ €’_ofile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/UserKernelShared.hÓ µ€U€Ò ÑÒ€’_kfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/PcmMsr/PcmMsrClient.hÓ µ€U€Ò ÑÒ€’_gfile://localhost/Users/aiott/projects/pcm/IntelPerformanceCounterMonitor/MacMSRDriver/DriverInterface.h_NSKeyedArchiverÑUState€"+5:? M S ` g o z | ƒ … Š Œ ¶ Í Ú Ü í ï ñ ó õ ÷ ù û ý         a p ” ° ß8EGXZ\^`bdfhy{}ƒ†ˆŠ–©·çú)<Udert‡‰‹‘“•—™¬®°²´¶¸º¼¾Õîù(<_r†ˆŠ‘“¡ÉÎÛÝâäæëíïùþ  !#%'SkƒŒŽ‘“ ¢±³µ·¹»½¿ÎÐÒÔÖØÚÜé$@^j|‘¤³Æâäæèêìõ÷üþ .7@KTjq~‡”–™›ž «Ößçìîûˆž£¹Âíò*,579;=FHJLNižÆÏÙÚãìñú,9EMOQS»ÄÊÏÕÞíôýÿ(*1357>@BDMORTacrtvxz|~€‘“•—™›¹ÎÐÒÔÖØ  "+68:ENmr‘šœž¥®°²¹ÆÈÊÌ6CENPRTV_acegpz‚‘“•ÿ  '469;>@huwz|†“•œž ¢©«­¯Ûó (*9;=?ACEGVXZ\^`bdqŒ˜¬Èæò!#T]_jlnprt}™›£¬®°ÃÌÎÐÛäæèóãìþ&(*,.79;=?Z·ÀÍØí#%')2479Qb}—¬®°²´Íðü       " 7 9 ; = ? A J L W Y [ ] _ a j l n w y { „ † ˆ ‘ “ • ž   ¢ ¯ ± ³ µ Î Ð Ò Ô Ö ß á ä æ ï!!!H!Q!w!|!¢!¯!±!¼!¾!À!Â!Ä!Æ!Ñ!Ó!Õ!×!Ù!Û!í!÷!ÿ" "#"0"2"5"7":"<"Y"f"h"k"m"p"r"†"“"•"˜"š""Ÿ"³"¼"¾"Ã"Å"Ç"Ô"Ö"Û"Ý"ß"ä"æ"è"ö# ## #-#/#4#6#8#=#?#A#M#V#c#e#h#j#m#o#x#z###ƒ##’#—#™#›# #¢#¤#®#·#Ä#Æ#Ë#Í#Ï#Ô#Ö#Ø#ê#ó$$$$$$$$$$$&$($-$/$1$>$@$E$G$I$N$P$R$S$\$i$k$p$r$t$y${$}$†$“$•$¤$¦$¨$ª$¬$®$°$²$Á$Ã$Å$Ç$É$Ì$Ï$Ò$ò%%)%I%h%‹%©%¶%¸%Ç%É%Ë%Í%Ï%Ñ%Ó%Õ%ä%æ%è%ê%ì%î%ð%ò&&'&N&r&&³&Ù&â&ä&ï&ñ&ó&õ&÷&ù' '7'h''¬'­'¶'¸'»'½'â'ï'ñ(((((( ( ((((!(#(%('()(+(Q(u(š(¼(æ))-)4)E)N)[)f)h)j)s)~)ƒ)Ž)—)™)¢)¶)½)Ñ)Ü)ÿ* **!*#*%*'*)*,*/*2*5*8*K*M*P*S*V*X*[*^*`*c*~**¡*µ*Ä*Ü*ð+ +!+8+A+D+E+N+[+b+h+q+t+w+z+‡+‰+’+•+˜+›+ž+§+ª+­+°+³+Ç+Ó+Ø+â+ë+í+ð+ó,,,,@,U,l,,”,—,™,›,ž, ,£---6-P-W-q-‡-Î-Ý-æ-è-í-ð-ó... ..... .#.&.).,.5.7.:.=.b.d.f.h.k.n.Ø/K/c/l/n/o/|/~/‡/Š///“/œ/Ÿ/¢/¥/¨/±/³/¶/¹/Þ/á/ã/å/è/ê/í0§0Æ0Ï0Ñ0Ò0Û0Ý0Þ0ç0ê0ë0ô0÷0ø11111111!1#1%1@1W1w1y1†1ˆ1—1š11 1£1¦1©1¬1»1¾1À1Ã1Æ1È1Ê1Í1Ü222*2I2m2œ2³2À2Â2Å2È2Ë2Î2à2í2ï2ò2õ2ø2û33333333$313336393<3?3R3S3`3b3e3h3k3n3{3}3€3ƒ3†3‰3–3˜3£3¦3©3¬3¯3²3½3À3Ã3Æ3É3Ì3Ú3ã3î3û3ý4444 444&4345484;4>4A4N4P4Q4R4_4a4h4k4n4q4x4z4}4€4¦4Ï4ô5555 5 55!5.505356595;5^5u5‚5„5‡5‰5Œ55˜5š5¡5¤5§5ª5·5¹5¾5À5Â5Ç5Ê5Í5à5é5ö5ø5ý5ÿ666 6 66#606267696;6@6C6F6Y6b6{6„6†6‰6‹6˜6š6±6´6·6º6½6À6Ã6Æ6É6Ì6Ï6Ò6é6ë6î6ð6ó6ö6ù6ü6ÿ77777:7T7k7x7«7½7Ý7ø88)86888E8H8K8N8Q8T8W8d8g8j8m8p8s8v8’8¼8ö99I9x9…9‡9’9•9˜9›9ž9¡9¬9¯9²9µ9¸9»9È9Ê9Í9Ï9Ø9Ú:m:z:|:::Š:Œ;;,;.;1;3;<;>;Í;Ú;Ü;ß;á;ê;ì=Z=c=e=h=k=|=•=˜=›=ž= >3><>E>d>k>Š> >­>¯>²>µ>¸>»>×>à>â>å>è>ù>ü>ÿ???—? ?­?¯?²?µ?¸?»?×?à?â?å?è?ù?ü?ÿ@@@“@œ@©@«@®@±@´@·@À@Â@Å@È@Ù@Ü@ß@â@äApAyA†AˆA‹AŽA‘A”A°A¹A»A¾AÁAÒAÕAØAÛAÝBiBrBBB†B‰BŒB‘B”B—B¤B¦B©B«B´B¶BýC C CCCCCrCCCˆC‹CŽC‘C˜C›CžC¡CÂCåDDDDDDD1D>DVD_DbDiDlDoDrD{D}D‚D…DˆD‘D“D–D™D­D¶D¸D»D¾DËDÍDÔD×DÚDÝDäDçDêDíEE1ERE[E]E`EcEwEŽE—EšEE E©E«E®E±E¾EÀEÇEÊEÍEÐE×EÚEÝEàEíEïEòEôEýEÿF>FKFMFPFRF[F]F«F¸FºF½F¿FÈFÊG4GAGCGNGQGTGWGZG]GhGkGnGqGtGwG§GÒHH/H^HzH‡H‰HŒHH’H•H©H²H´H¹H¼H¿HÌHÎHÓHÖHÙHÞHàHãHñII III!I$I'I,I.I1I:IGIIIdIgIjImIpIsIvIyI|II‚I…IˆI‹I¦I©I¬I®I±I´I·IºI½IÀIÃIÆIÉIÌIúJ#JFJaJ|J—J²JÍJèKKK9KTK]K`KiKlKoKrKuK’K¨KºKÇKÎKÛKÝKÞKßKìKîKñKôK÷KúL$L5L7LDLFLGLHLULWLZL]L`LcLlLnL{L}L~LLŒLŽLLLLŸL L¡L®L°L±L²L¿LÁLÂLÃLÐLÒLÓLÔLÝLßLâLåLöMMMM M MLMUMbMdMkMnMqMtM{M~MM„M‹M’MµMÏMÜMåMçMêMíMúMüMýMþNN'N.NNNdN~N‹NN˜N›NžN¡N¤N§N²NµN¸N»N¾NÁNñOOKOzOªOÆOÓOÕOØOÛOÞOáOõOþPPPP PPPP"P%P*P,P/P=PPPYPfPhPmPpPsPxPzP}P†P¢P«P­P°P³PÄPÇPÊPÍPÐQQ(Q5Q7Q>QAQDQGQNQQQTQWQ^QƒQŸQ¦Q¯Q±Q´Q·QÄQÆQÓQÖQÙQÜQßQâQåQòQõQøQúQüQþRR&RBRiRR¯RÔRÝRßTbTeThTkTnTqTtTwTzT}T€TƒT†T‰TŒTT’T•T˜T›TžT¡T¤T§TªT­T°T³T¶T¹T¼T¿TÂTÅTÈTËTÎTÑTÔT×TÚTÝTàTãTæTéTìTïTòTõTøTûTþUUUU U UUUUUUU"U%U(U+U.U1U4U7U:U=U@UCUFUIULUOURUUUXU[U^UaUdUgUjUmUpUsUvUyU|UU‚U…UˆU‹UŽU‘U”U—UšUU U£U¦U©U¬U¯U²UµU¸U»U¾UÁUÄUÇUÊUÍUÐUÓUÖUÙUÜUßUâUåUèUëUîUñUôU÷UúUýVVVV V VVVVVVV!V$V'V*V-V0V3V6V9V<V?VBVEVHVKVNVQVTVWVZV]V`VcVfViVlVoVrVuVxV{V~VV„V‡VŠVVV“V–V™VœVŸV¢V«V­VÏVØVÚVòVûVýWW W"WHWQWSWqWzW|W˜W¡W£W¿WÈWÊWòWûWýXX'X)XGXPXRX}X†XˆX²X»X½YY Y Y7Y@YBYwY€Y‚Y¥Y®Y°YØYáYãZZZZAZJZLZsZ|Z~Z˜Z¡Z£ZÂZËZÍZìZõZ÷[[[ [K[T[V[z[ƒ[…[¨[±[³[Ü[å[ç\\ \\,\5\7\X\a\c\z\ƒ\…\¡\ª\¬\Ô\Ý\ß]]]].]7]9]`]i]k]†]]‘]¯]¸]º]Ø]á]ã^^ ^ ^+^4^6^W^`^b^|^…^‡^¯^¸^º^Ü^å^ç____<_E_G_]_f_h_—_ _¢_Ä_Í_Ï_ñ_ú_ü``` `I`R`T`l`u`w`Ž`—`™`³`¼`¾`æ`ï`ña aaa6a?aAaeanapa‘ašaœa¶a¿aÁaÝaæaèb bbb3b<b>b_bhbjb’b›bbÀbÉbËbëbôböc$c-c/cSc\c^c~c‡c‰c¦c¯c±câcëcíddddOdXdZd€d‰d‹d®d·d¹dâdëdíee e"e=eFeHeoexeze£e¬e®eÙeâeäff ff9fBfDflfufwfœf¥f§fÍfÖfØfúgggDgMgOgŽg—g™gÃgÌgÎgðgùgûhh&h(hChLhNhhhqhshšh£h¥hÆhÏhÑhùiii'i0i2iUi^i`iiŠiŒi°i¹i»iÿjj jUj^j`j¡jªj¬jøkkkCkLkNkœk¥k§kókükþlLlUlWl¢l«l­lãlìlîm-m6m8mzmƒm…mÀmÉmËnnnnbnknmn«n´n¶nönÿoo>oGoIo‡oo’oÔoÝoßpp p"pZpcpepžp§p©pépòpôq.q7q9qqˆqŠqËqÔqÖrrrrjrsrur»rÄrÆs sssesnsps¬sµs·sðsùsût.t7t9ttt}ttÂtËtÍuuuuSu\u^užu§u©uéuòuôv*v3v5v~v‡v‰vævïvñw8wAwCw‹w”w–wÞwçwéxx(x*x{x„x†xÍxÖxØy"y+y-yvyyyÅyÎyÐzz(z*zkztzvz¹zÂzÄ{{{{g{p{r{·{À{Â||!|#|i|r|t|´|½|¿|ô|ý|ÿ}G}P}R}£}¬}®}æ}ï}ñ~9~B~D~€~‰~‹~Ð~Ù~Û+46“œž÷€€€E€N€P€™€¢€¤€ç€ð€ò5>@ˆŠÉÒÔ‚‚‚‚[‚d‚f‚¢‚«‚­‚ì‚õ‚÷ƒ9ƒBƒDƒŽƒ—ƒ™ƒåƒîƒð„1„:„<„x„„ƒ„†„‰„’„”„¶„ÄńƄDŽԄքá„ä„ç„ê„í„ð„û„þ………… …:…e…”…Å󆆆†!†$†'†*†>†G†I†N†Q†T†a†c†h†k†n†s†u†x†††™†¢†¯†±†¶†¹†¼†Á†Ã†Æ†Ï†ë†ô†ö†ù†ü‡ ‡‡‡‡‡ƒ‡Œ‡™‡›‡¢‡¥‡¨‡«‡²‡µ‡¸‡»‡Â‡çˆˆˆˆˆˆ!ˆ.ˆ0ˆ=ˆ@ˆCˆFˆIˆLˆOˆ\ˆ^ˆaˆcˆeˆhˆjˆˆ«ˆÒˆø‰‰>‰G‰I‰L‰O‰X‰Z‰x‰‰ƒ‹‹‹ ‹ ‹‹‹‹‹‹‹"‹%‹(‹+‹.‹1‹4‹7‹:‹=‹@‹C‹F‹I‹L‹O‹R‹U‹X‹[‹^‹a‹d‹g‹j‹m‹p‹s‹v‹y‹|‹‹‚‹…‹ˆ‹‹‹Ž‹‘‹”‹—‹š‹‹ ‹£‹¦‹©‹¬‹¯‹²‹µ‹¸‹»‹¾‹Á‹Ä‹Ç‹Ê‹Í‹Ð‹Ó‹Ö‹Ù‹Ü‹ß‹â‹å‹è‹ë‹î‹ñ‹ô‹÷‹ú‹ýŒŒŒŒ Œ ŒŒŒŒŒŒŒ!Œ$Œ'Œ*Œ-Œ0Œ3Œ6Œ9Œ<Œ?ŒBŒEŒHŒKŒNŒQŒTŒWŒZŒ]Œ`ŒcŒfŒiŒlŒoŒrŒuŒxŒ{Œ~ŒŒ„Œ‡ŒŠŒŒŒ“Œ–Œ™ŒœŒŸŒ¢Œ¥Œ¨Œ«Œ®Œ±Œ´Œ·ŒºŒ½ŒÀŒÃŒÆŒÉŒÌŒÏŒÒŒÕŒØŒÛŒÞŒáŒäŒçŒêŒíŒðŒóŒöŒùŒüŒÿ  #&),/258;>AJLnwy‘šœ¶¿ÁäíïŽŽŽ Ž>ŽGŽIŽeŽnŽpŽŒŽ•Ž—Ž¿ŽÈŽÊŽëŽôŽöIRT— ¢Î×Ù<EGoxzž§©Øáã‘ ‘‘‘/‘8‘:‘Y‘b‘d‘ƒ‘Œ‘Ž‘¬‘µ‘·‘â‘ë‘í’’’’?’H’J’s’|’~’›’¤’¦’Ã’Ì’Î’ï’ø’ú““““8“A“C“k“t“v“ž“§“©“œΓГ÷””””&”(”F”O”Q”o”x”z”™”¢”¤””˔͔î”÷”ù••••F•O•Q•s•|•~•ž•§•©•ӕܕޕô•ý•ÿ–.–7–9–[–d–f–ˆ–‘–“–¬–µ–·–à–é–ë—— ——%—.—0—J—S—U—}—†—ˆ—¡—ª—¬—Í—Ö—Ø—ü˜˜˜(˜1˜3˜M˜V˜X˜t˜}˜˜£˜¬˜®˜Ê˜Ó˜Õ˜ö˜ÿ™™)™2™4™W™`™b™‚™‹™™»™Ä™Æ™ê™ó™õššš š=šFšHšyš‚š„šªš³šµšæšïšñ›› ›"›E›N›P›y›‚›„›®›·›¹›Ô›Ý›ßœœœœ:œCœEœpœyœ{œ›œ¤œ¦œÐœÙœÛ 4=?ajl«´¶õþžž*ž3ž5žWž`žbž„žžžªž³žµžÏžØžÚŸŸ Ÿ Ÿ-Ÿ6Ÿ8Ÿ`ŸiŸkŸŽŸ—Ÿ™Ÿ¼ŸÅŸÇŸèŸñŸó    " f o q ¼ Å Ç¡¡¡¡_¡h¡j¡ª¡³¡µ¢¢ ¢¢Z¢c¢e¢³¢¼¢¾£ £££J£S£U£”££Ÿ£á£ê£ì¤'¤0¤2¤v¤¤¤É¤Ò¤Ô¥¥¥¥]¥f¥h¥¥¥®¥°¥î¥÷¥ù¦;¦D¦F¦~¦‡¦‰¦Á¦Ê¦Ì§§§§P§Y§[§•§ž§ §æ§ï§ñ¨2¨;¨=¨x¨¨ƒ¨Ñ¨Ú¨Ü©"©+©-©s©|©~©Ì©Õ©×ªªªªWª`ªbª•ªžª ªÛªäªæ«)«2«4«l«u«w«º«Ã«Å¬¬¬¬P¬Y¬[¬‘¬š¬œ¬å¬î¬ð­M­V­X­Ÿ­¨­ª­ò­û­ý®E®N®P®†®®‘®â®ë®í¯4¯=¯?¯‰¯’¯”¯Ý¯æ¯è°,°5°7°†°°‘°Ò°Û°Ý± ±)±+±{±„±†±Î±×±Ù²²'²)²²ˆ²Š²Ð²Ù²Û³³$³&³[³d³f³®³·³¹´ ´´´M´V´X´ ´©´«´ç´ð´òµ7µ@µBµ’µ›µµú¶¶¶^¶g¶i¶¬¶µ¶··· · ·N·W·Y·œ·¥·§·æ·ï·ñ¸0¸9¸;¸w¸€¸‚¸Â¸Ë¸Í¹ ¹¹¹S¹\¹^¹ ¹©¹«¹õ¹þººLºUºWº˜º¡º£ºßºìºîºïºðºýºÿ»d»g»j»m»p»s»v»y»|»»‚»…»ˆ»‹»Ž»‘»”»—»š»» »£»¦»©»¬»¯»²»µ»¸»»»¾»Á»Ä»Ç»Ê»Í»Ð»Ó»Ö»Ù»Ü»ß»â»å»ç»ê»í»ð»ó»ö¼[¼^¼a¼d¼g¼j¼m¼p¼s¼v¼y¼|¼¼‚¼…¼ˆ¼‹¼Ž¼‘¼”¼—¼š¼¼ ¼£¼¦¼©¼¬¼¯¼²¼µ¼¸¼»¼¾¼Á¼Ä¼Ç¼Ê¼Í¼Ð¼Ó¼Ö¼Ù¼Ü¼ß¼â¼å¼è¼ë¼î¼û¼ý½½½ ½ ½O½\½^½a½c½l½n¾)¾6¾8¾;¾=¾F¾H¿¿¿¿¿¿"¿$¿Ù¿æ¿è¿ë¿í¿ö¿øÀ6ÀCÀEÀHÀJÀSÀUÁÁ Á"Á%Á'Á0Á2ÁÁŒÁŽÁ‘Á“ÁœÁžÂWÂdÂfÂiÂkÂtÂvÂÞÂëÂíÂðÂòÂûÂýÃ\ÃiÃkÃnÃpÃyÃ{ÃËÃØÃÚÃÝÃßÃèÃêÄ+Ä8Ä:Ä=Ä?ÄHÄJĒğġĤĦįıÅÅÅÅÅÅ%Å'řŦŨūŭŶŸÆxƅƇƊƌƕƗÇMÇZÇ\Ç_ÇaÇjÇlÇ»ÇÈÇÊÇÍÇÏÇØÇÚÈÈ'È)È,È.È7È9ȣȰȲȵȷÈÀÈÂÉÉÉ É#É%É.É0ÉéÉöÉøÉûÉýÊÊÊHÊUÊWÊZÊ\ÊeÊgÊÑÊÞÊàÊãÊåÊîÊðËzˇˉˌˎ˗˙ËÙËæËèËëËíËöËøÌGÌTÌVÌYÌ[ÌdÌfÌÐÌÝÌßÌâÌäÌíÌïͥͲʹͷ͹ÍÂÍÄÎÎÎÎÎÎÎ!ÎgÎtÎvÎyÎ{΄ΆÎÛÎèÎêÎíÎïÎøÎúϱϾÏÀÏÃÏÅÏÎÏÐЅВДЗЙТФÐñÐþÑÑÑÑÑÑLÑYÑ[Ñ^Ñ`ÑiÑkѶÑÃÑÅÑÈÑÊÑÓÑÕÒ;ÒHÒJÒMÒOÒXÒZÒÂÒÏÒÑÒÔÒÖÒßÒáÓ•Ó¢Ó¤Ó§Ó©Ó²Ó´ÓûÔÔ Ô ÔÔÔԊԗԙԜԞԧԩÔíÔúÔüÔÿÕÕ Õ Õ_ÕlÕnÕqÕsÕ|Õ~ÕìÕùÕûÕþÖÖ Ö ÖÅÖÒÖÔÖ×ÖÙÖâÖäש׶׸×Á×Ä×Ç×Ê×Í×Ö×Ù×Ü×Þ×á×üØ#Ø1ØYØbØlØuØ‚Ø„ØØØ“Ø–Ø™Ø¢Ø¥Ø¨ØªØ­ØÈØïØýÙ%Ù.Ù8ÙEÙGÙPÙSÙVÙYÙ\ÙeÙhÙkÙmÙpÙyنٕٓٞ١٤٧٪ٳٶٹٻپÙÇÙÑÙÞÙàÙéÙìÙïÙòÙõÙþÚÚÚÚ ÚÚÚ%Ú2Ú4Ú=Ú@ÚCÚFÚIÚRÚUÚXÚZÚ]ÚfÚsÚ€Ú‚Ú‹ÚŽÚ‘Ú”Ú—Ú Ú£Ú¦Ú¨Ú«ÚÆÚíÚûÛ#Û,Û9ÛCÛPÛRÛ[Û^ÛaÛdÛgÛpÛsÛvÛxÛ{Û„Û‘ÛžÛ Û©Û«Û­Û¯Û±ÛºÛ½ÛÀÛÂÛÅÛÎÛÛÛåÛòÛôÛýÜÜÜÜ ÜÜÜÜÜÜ8Ü_ÜmܕܞܦܾܳܵÜÁÜÄÜÇÜÊÜÓÜÖÜÙÜÛÜÞÜçÜïÜüÜþÝÝ Ý ÝÝÝÝÝ"Ý$Ý'Ý0Ý9ÝBÝOÝQÝZÝ]Ý`ÝcÝfÝoÝrÝuÝwÝz݃݌ݕݢݤݭݰݳݶݹÝÂÝÅÝÈÝÊÝÍÝèÞÞÞEÞNÞWÞ`ÞmÞoÞxÞzÞ|Þ~Þ€Þ‰ÞŒÞÞ‘Þ”ÞÞ§Þ±Þ¾ÞÀÞÉÞÌÞÏÞÒÞÕÞÞÞáÞäÞæÞéÞòÞþßßßß ß#ß&ß)ß,ß5ß8ß;ß=ß@ßIßSß`ßbßkßnßqßtßw߀߃߆߈ߋߔߞߨߵ߷ßÀßÃ߯ßÉßÌßÕߨßÛßÝßàßéßõßÿà ààààààà(à+à.à0à3à<àIàQà^à`àiàlàoàràuà~àà„à†à‰à’àŸà©à¶à¸àÁàÄàÇàÊàÍàÖàÙàÜàÞàáàêà÷áááááááá$á'á*á,á/á8áBáLáYá[ádáfáhájáláuáxá{á}á€á‰á–á á­á¯á¸áºá¼á¾áÀáÉáÌáÏáÑáÔáÝáêáôâââ âââââ!â$â'â)â,â5â>âGâTâVâ_âbâeâhâkâtâwâzâ|ââˆâ‘âšâ§â©â²â´â¶â¸âºâÃâÆâÉâËâÎâ×âáâéâöâøãããã ã ããããã!ã*ã7ãDãFãOãRãUãXã[ãdãgãjãlãoãxããŠã—ã™ã¢ã¥ã¨ã«ã®ã·ãºã½ã¿ãÂãËãØãâãïãñãúãýäääääääää#ä0ä:äGäIäRäUäXä[ä^ägäjämäoärä{äˆä•ä—ä ä£ä¦ä©ä¬äµä¸ä»ä½äÀäÉäÖäãäåäîäñäôä÷äúååå å ååå å)å6å8åAåDåGåJåMåVåYå\å^åaåjåså|å‰å‹å”å—åšåå å©å¬å¯å±å´å½åÆåÏåÜåÞåçåéåëåíåïåøåûåþæææ ææ æ-æ/æ8æ:æ<æ>æ@æIæLæOæQæTæ]ægæqæ~æ€æ‰æŒææ’æ•æžæ¡æ¤æ¦æ©æ²æ¿æÌæÎæ×æÚæÝæàæãæìæïæòæôæ÷çç9çGçoçxç‚çŒç™ç›ç¤ç§çªç­ç°ç¹ç¼ç¿çÁçÄçÍçÕçâçäçíçïçñçóçõçþèèèè èèè)è6è8èAèCèEèGèIèRèUèXèZè]èfèsè~è‹èè–è™èœèŸè¢è«è®è±è³è¶è¿èÉèÓèàèâèëèîèñèôè÷ééééé ééé(é5é7é@éBéDéFéHéQéTéWéYé\éeéoéyé†éˆé‘é”é—éšéé¦é©é¬é®é±éºéÇéÔéÖéßéáéãéåéçéðéóéöéøéûêêêê(ê*êyê|êê‚ê…êˆê‹êŽê‘ê”ê—êšêê ê£ê¦ê©ê¬ê¯ê²êµê¸ê»ê¾êÁêÄêÇêÊêÍêÐêÓêÖêÙêÜêßêâêåêèêëë:ë=ë@ëCëFëIëLëOëRëUëXë[ë^ëaëdëgëjëmëpësëvëyë|ëë‚ë…ëˆë‹ëŽë‘ë”ë—ëšëë ë£ë¦ë©ë¬ë¹ë»ë¾ëÀëõìììì ì>ìKìMìPìRì‡ì”ì–ì™ì›ìÐìÝìßìâìäíí&í(í+í-íbíoíqítíví«í¸íºí½í¿íôîîîîî=îJîLîOîQî†î“î•î˜îšîÏîÜîÞîáîãïï%ï'ï*ï,ïaïnïpïsïuïªï·ï¹ï¼ï¾ïóððððð<ðIðKðNðPð…ð’ð”ð—ð™ðÎðÛðÝðàðâññ$ñ&ñ)ñ+ñ`ñmñoñrñtñ©ñ¶ñ¸ñ»ñ½ñòñÿòòòò;òHòJòMòOò„ò‘ò“ò–ò˜òÍòÚòÜòßòáóó#ó%ó(ó*ó_ólónóqósó¨óµó·óºó¼óñóþôôôô:ôGôIôLôNôƒôô’ô•ô—ôÌôÙôÛôÞôàõõ"õ$õ'õ)õ^õkõmõpõrõ§õ´õ¶õ¹õ»õðõýõÿööö9öFöHöKöMö‚öö‘ö”ö—öšöö¹öÂöÄöÅöÒöÔö×öÚöÝöàöü÷ ÷ ÷÷÷÷÷$÷&÷)÷,÷/÷2÷?÷A÷D÷G÷J÷M÷i÷v÷x÷{÷~÷÷„÷‘÷“÷–÷™÷œ÷Ÿ÷¬÷®÷±÷´÷·÷º÷Ö÷ã÷å÷è÷ë÷î÷ñ÷þøøøø ø øøøø!ø$ø'øCøPøRøUøXø[ø^økømøpøsøvøyø†øˆø‹øŽø‘ø”øøŸø¢ø¥øºøÍø×øÙøÚøÝøàøãøðùùùùù%ù-ù0ù3ù<ùJùQùXùaùmùrù~ù‡ù ù§ùÀùÖùãùåùèùëùîùñùþúúúú ú ú(ú5ú7ú:ú=ú@úCúPúRúUúXú[ú^úzú‡ú‰úŒúú’ú•ú¢ú¤ú§úªú­ú°ú½ú¿úÂúÅúÈúËúØúÚúÝúàúãúæúïúñúôú÷û ûûûûûû%û8û;û=ûJûLûOûRûUûXûeûgûjûmûpûsû€û‚û…ûˆû‹ûŽû›ûû û£û¦û©û¶û¸û»û¾ûÁûÄûÍûÏûÒûÕûêûìûíûðûóûöüüü üüüü%ü'ü*ü-ü0ü3ü@üBüEüHüKüNü[ü]ü`ücüfüiürütüwüzüü‘ü’ü•ü˜ü›ü¨üªü­ü°ü³ü¶ü¿üÁüÄüÇüÜüÞüßüâüåüèüõü÷üúüýýýýýýýýýý:ýCýEýHýKý`ýbýcýfýiýlýyý|ý~ý‹ýýý“ý–ý™ý¦ý¨ý«ý®ý±ý´ýÁýÃýÆýÉýÌýÏýÜýÞýáýäýçýêý÷ýùýüýÿþþþþþþþþ þ-þ/þ6þ9þ<þ?þFþIþLþOþpþ“þ´þ½þ¿þÂþÅþÙþðþùþüþÿÿÿ ÿ ÿÿÿ ÿ"ÿ'ÿ*ÿ-ÿ2ÿ5ÿ8ÿLÿdÿ‰ÿÿÿŸÿ¢ÿ¥ÿ¨ÿ«ÿ¹ÿÇÿÔÿÖÿÝÿàÿãÿæÿíÿðÿóÿö)D]fhqtwz}ŠŒ“–™œ£¦©¬Õ/8:@MOVY\_filox{‰–˜Ÿ¢¥¨¯²µ¸ÁÈÑÔ4ACPSVY\_bortwz|~£Èò%.6?BKRWlnwy|~‹¤§ª­°³¶¹¼¿ÂÅÜßâåèëîñô÷úý  O\^ac°½¿ÂÄXegjl°½¿ÂÄcpruw·ÄÆÉË      n { } € ‚     # & + . 1 7 > G P ] _ d g j o r u { „  š œ ¡ ¤ § ¬ ¯ ² ¸ Á Ê × Ù Þ á ä é ì ï õ þ      ! & ) , 2 ; D Q S X [ ^ c f i r { ˆ Š  ’ • š    ¦ ¯ ¸ Å Ç Ì Ï Ò × Ú Ý æ ï ü þ        & 3 5 : = @ E H K T ] j l q t w |  ‚ ˆ ‘ š £ ¥ º ¼ ¿ Â Å È Ë Î Ñ Ô × ä æ é ë ô ö f s u x z ƒ … ï ü þ vƒ…ˆŠ“•ÿ „‘“–˜¡£ (*œ©«®°¹»)68;=FH²ÄÉÏÑpcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/pjkerly.xcuserdatad/000077500000000000000000000000001445420033100327355ustar00rootroot00000000000000UserInterfaceState.xcuserstate000066400000000000000000002517611445420033100407260ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/project.xcworkspace/xcuserdata/pjkerly.xcuserdatadbplist00Ô†‡X$versionX$objectsY$archiverT$top† ¯¡'()*+,-.DEFGHIJKLMNOPVW\defghnvw{~Š‹ŒŽžŸ ¡¢£¤ª«±²º»¼ÄÅÍÎÔØÙÚàæþÿ $%-.167=ADGMQRhijklmnopqtx{~„Š˜™š¢£¤¬­®ÄÅÆÇÈÉÊËÌÍÓÔÚÛåæçèìþÿ"*+,456:BCDNOPQRSWghijklmtwz}€ƒ†‰Œ’•˜›ž¡¤§ª­°³¶¹¼¿ÂÅÈËÎÑÔ×ÚÝàãæéìïòõøûþ  "%(+.147:=@CFILORUX[^adgjmpsvy|‚…ˆ‹Ž‘”—𠣦©¬¯²µ¸»¾ÁÄÇÊÍÐÓÖÙÜßâåèëîñô÷úý  !$'*-0369<?BEHKNQTWZ]`cfilorux{~„‡Š“–™œŸ¢¥¨«®±´·º½ÀÃÆÉÌÏÒÕØÛÞáäçêíðóöùüÿ  #&),/258;>ADGJMPSVY\_behknqtwz}€„<‡‰Ž’œ¡§¨¬­±µ¹º»ÁÄÈÉÕÖרÙåæçèéìòóùþ!)*+345=GMQUVW[`dhlmq…†‡ˆ‰Š‹Œ•–—Ÿ£¤°±²³´º»ÀÈÉÊËÌÔÕÖ×Ûàáâìíîïðñõ       " % ( + . 1 4 7 : = @ C F I L O R U X [ ^ a d g j m p s v y |  ‚ … ˆ ‹ Ž ‘ ” — š   £ ¦ © ¬ ¯ ² µ ¸ » ¾ Á Ä Ç Ê Í Ð Ó Ö Ù Ü ß â å è ë î ñ ô ÷ ú ý         ! $ ' * - 0 3 6 9 < ? B E H K N Q T W Z ] ` c f i l o r u x { ~ „ ‡ Š “ – ™ œ Ÿ ¢ ¥ ¨ « ® ± ´ · º ½ À Ã Æ É Ì Ï Ò Õ Ø Û Þ á ä ç ê í ð ó ö ù ü ÿ          # & ) , / 2 5 8 ; > A D G J M P S V Y \ _ b e h k n q t w z } € ƒ † ‰ Œ ’ • ˜ › ž ¡ ¤ § ª ­ ° ³ ¶ ¹ ¼ ¿ Â Å È Ë Î Ñ Ô × Ú Ý à ã æ é ì ï ò õ ø û þ          " % ) 5 6 : ? @ J K L P ` g j m p s v y |  ‚ … ˆ ‹ Ž ‘ ” — š   £ ¦ © ¬ ¯ ² µ ¸ » ¾ Á Ä Ç Ê Í Ð Ó Ö Ù Ü ß â å è ë î ñ ô ÷ ú ý  !$'*-0369<?BEHKNQTWZ]`cfilorux{~„‡Š“–™œŸ¢¥¨«®±´·º½ÀÃÆÉÌÏÒÕØÛÞáäçêíðóöùüÿ  #&),/258;>ADGJMPSVY\_behknqtwz}€ƒ†‰Œ’•˜›ž¡¤§ª­°³¶¹¼¿ÂÅÈËÎÑÔ×ÚÝàãæéìïòõøûþ  "%(+.147:=@CFILORUX[^adgjmpswz‚†‡‹Œ–—˜™ž£¦ª«¬¶·¸¹¾ÁÅÆÇÍÎÏãäåæçèéêëìíîïðöú%&'()*+5678BCDEOPQUYijklmqtuvz…U$nullÓ WNS.keysZNS.objectsV$class¢ €€¢€ÿ€!_$F4F24FE0-12C0-4EA1-B30C-7297EF84ECD8_IDEWorkspaceDocumentÓ &§€€€€€ € € § ! $$€ ýþ€€€€€%_>IDEWorkspaceTabController_95C7B26E-9A03-42FB-BAB5-20CF5592BF08^IDEWindowFrame_!IDEOrderedWorkspaceTabControllers_,IDEWorkspaceWindowControllerUniqueIdentifier_IDEActiveWorkspaceTabController_IDEWindowToolbarIsVisible_IDEWindowTabBarIsVisibleÓ /9&©012345678€ €€€€€€€€©:$<=>?$AB€€€€€&€q€€{€|€%_AssistantEditorsLayout_IDEShowNavigator[IDETabLabel_-IDEWorkspaceTabControllerUtilityAreaSplitView_IDENavigatorArea_,IDEWorkspaceTabControllerDesignAreaSplitView_IDEShowUtilities^IDETabFilePath]IDEEditorArea _PcmMsr.xcodeprojÓ QS&¡R€¡T€€%_DVTSplitViewItemsÒ X[¢YZ€€"€$Ó ]`¢^_€€¢ab€€ €!]DVTIdentifier_DVTViewMagnitudeP#@°ÒijklZ$classnameX$classes\NSDictionary¢kmXNSObjectÓ or¢^_€€¢at€€#€!#@n Òijxy^NSMutableArray£xzmWNSArrayÒij|}_NSMutableDictionary£|kmÓ „&¤€‚ƒ€'€(€)€*¤…†‡ˆ€+€C€`€a€%_ Xcode.IDEKit.Navigator.Structure_ Xcode.IDEKit.Navigator.BatchFind_SelectedNavigator_Xcode.IDEKit.Navigator.IssuesÓ –&¦‘’“”•€,€-€.€/€0€1¦—˜™™›œ€2€=€?€?€@€A€%_IDEExpandedItemsTree_,IDENavigatorExpandedItemsBeforeFilteringTree_IDESCMStatusFilteringEnabled_!IDERecentDocumentFilteringEnabled^IDEVisibleRect_IDESelectedTreeÓ ¥§¡¦€3¡¨€4€!_IDEValuesAsTreeÓ ¬®&¡­€5¡¯€6€%VPcmMsrÓ ³¶&¢´µ€7€8¢´¸€7€9€%P]PcmMsrLibraryÓ ½À&¢´¿€7€:¢´Â€7€;€%VPcmMsrÓ ÆÉ&¢Ç´€<€7¢´´€7€7€%_Supporting FilesÓ ÏÑ¡¦€3¡Ò€>€!Ó ÕÖ&  €%_{{0, 0}, {259, 754}}Ó ÛÝ¡¦€3¡Þ€B€!Ó áã&¡­€5¡´€7€%Ó çò&ªèéêëìíîïðñ€D€E€F€G€H€I€J€K€L€Mªó:õö÷øöö$ü€N€€P€Y€Z€\€Y€Y€€^€%_$IDEBatchFindNavigatorCollapsedGroups_#IDEBatchFindNavigatorScrollPosition_)IDEBatchFindNavigatorFindAttributedString_IDEBatchFindMatchStyle_'IDEBatchFindNavigatorSelectedRowIndexes_,IDEBatchFindNavigatorReplaceAttributedString_IDEBatchFindFindType_IDEBatchFindNavigatorFindMode_IDEBatchFindIgnoreCase_.IDEBatchFindNavigatorSelectedLocationsStateKeyÒ M \NSRangeCount€OÒij  _NSMutableIndexSet£m_NSMutableIndexSetZNSIndexSetÓ XNSString\NSAttributes€Q€X€SÒ YNS.string€R_PRODUCT_BUNDLE_IDENTIFIERÒij_NSMutableString£mXNSStringÓ !¡ €T¡"€U€!VNSFontÔ&'( )*+,VNSSizeXNSfFlagsVNSName#@& €V€W_.AppleSystemUIFontÒij/0VNSFont¢/mÒij23_NSMutableAttributedString£45m_NSMutableAttributedString_NSAttributedStringÔ8  9:;<?ZNSIndexSet¢@mZNSIndexSetÒ B€]€XÒ F€RPÓ HJ¡¦€3¡K€_€!Ó NO&  €%_ Xcode.IDEKit.Navigator.StructureÓ S]&©T”VWXYZ[\€b€0€c€d€e€f€g€h€i©™_`a™cd™f€?€j€k€m€?€n€o€?€p€%_IDEErrorFilteringEnabled_IDECollapsedFiles_IDEExpandedIssues^IDEShowsByType_IDESelectedNavigables_IDECollapsedTypes_IDERecentFilteringEnabled_IDECollapsedGroups_{{0, 0}, {259, 725}}Ò rs €lÒijuv\NSMutableSet£uwmUNSSetÒ ys €lÒ |[ €$Ò s €lÒ ‚s €lÓ …‡&¡R€¡ˆ€r€%Ò ‹[£ŒŽ€s€v€y€$Ó ‘”¢^_€€¢•–€t€u€!_IDENavigatorArea#@p@Ó ›ž¢^_€€¢Ÿ €w€x€!]IDEEditorArea#@‹€Ó ¥¨¢^_€€¢©–€z€u€!_IDEUtilitiesArea_T/Users/pjkerly/PCMRepository/ssg_intelpcm-main/MacMSRDriver/PcmMsr/PcmMsr-Info.plistÓ ¯¹&©°±²³´µ¶·¸€}€~€€€€€‚€ƒ€„€…©™»$::¿ÀÁ€?€†€€€Óèéñ€%_ShowDebuggerArea_IDEEditorMode_Standard]IDEShowEditor_VersionEditorSubmodeZEditorMode_IDEDefaultDebugArea_ DefaultPersistentRepresentations_DebuggerSplitViewZlayoutTreeÓ ÎÐ&¡Ï€‡¡Ñ€ˆ€%_%EditorLayout_PersistentRepresentationÓ Õ×&¡Ö€‰¡Ø€Š€%TMainÓ Üà£ÝÞ߀‹€Œ€£á:『€Ñ€!_)EditorLayout_StateSavingStateDictionaries_EditorLayout_Selected_EditorLayout_GeometryÒ éë¡ê€ÀÓ íõ&§îïðñòóô€€‘€’€“€”€•€–§ö÷øùùûü€—€˜ÃÍÍÎÏ€%\FileDataType[EditorState_ArchivableRepresentation_NavigableItemName_DocumentNavigableItemName_DocumentExtensionIdentifier[DocumentURL_com.apple.xcode.projectÓ  ¤   €™€š€›€œ¤ €€¥€¦€!_(Xcode3ProjectEditor.sourceList.splitview_,Xcode3ProjectEditorPreviousTargetEditorClass_,Xcode3ProjectEditorSelectedDocumentLocations_-Xcode3ProjectEditor_Xcode3BuildSettingsEditorÓ &¡€ž¡€Ÿ€%_DVTSplitViewItemsÒ [¢ € €£€$Ó #&¢^_€€¢'(€¡€¢€!_!sourceListSplitViewItemIdentifier#@e@Ó -0¢^_€€¢a2€€¤€!#@†(_Xcode3BuildSettingsEditorÒ 7ë¡8€§ÀÔ;<= >?@AYselectionYtimestamp[documentURL€ª€©€¨Á_Tfile:///Users/pjkerly/PCMRepository/ssg_intelpcm-main/MacMSRDriver/PcmMsr.xcodeproj/#A¼;AÅ{Ó EI&£FGH€«€¬€­£JKL€®€¯€°€%VEditorVTarget_"Xcode3BuildSettingsEditorLocations_Xcode3BuildSettingsEditor\PcmMsrDriverÒ Të¡U€±ÀÓ X_&¦YZ[\]^€²€³€´€µ€¶€·¦`ab::b€¸½¿€€¿€%_#Collapsed Build Property Categories_Selected Build Properties_$Xcode3BuildSettingsEditorDisplayMode_#Xcode3BuildPropertyValueDisplayMode_Xcode3BuildSettingsEditorMode_"Xcode3BuildPropertyNameDisplayModeÒ n[¯opqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqr€¹€º€»€¼€½€¾€¿€À€Á€Â€Ã€Ä€Å€Æ€Ç€È€É€Ê€Ë€Ì€Í€Î€Ï€Ð€Ñ€Ò€Ó€Ô€Õ€Ö€×€Ø€Ù€Ú€Û€Ü€Ý€Þ€ß€à€á€â€ã€ä€å€æ€ç€è€é€ê€ë€ì€í€î€ï€ð€ñ€ò€ó€ô€õ€ö€÷€ø€ù€ú€û€ü€ý€þ€ÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼€$Ò v€R_Architectures||ADDITIONAL_SDKSÒ y€R_Architectures||ARCHSÒ |€R_Architectures||SDKROOTÒ €R_#Architectures||SUPPORTED_PLATFORMSÒ ‚€R_Architectures||VALID_ARCHSÒ …€R_'Assets||ASSET_PACK_MANIFEST_URL_PREFIXÒ ˆ€R_,Assets||EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLEÒ ‹€R_#Assets||ENABLE_ON_DEMAND_RESOURCESÒ Ž€R_1Assets||ON_DEMAND_RESOURCES_INITIAL_INSTALL_TAGSÒ ‘€R_+Assets||ON_DEMAND_RESOURCES_PREFETCH_ORDERÒ ”€R_Build Locations||SYMROOTÒ —€R_Build Locations||OBJROOTÒ š€R_%Build Locations||SHARED_PRECOMPS_DIRÒ €R_Build Options||BUILD_VARIANTSÒ  €R_Build Options||GCC_VERSIONÒ £€R_/Build Options||EMBEDDED_CONTENT_CONTAINS_SWIFTÒ ¦€R_'Build Options||GENERATE_PROFILING_CODEÒ ©€R_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRÒ ¬€R_.Build Options||APPLICATION_EXTENSION_API_ONLYÒ ¯€R_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESÒ ²€R_ Build Options||VALIDATE_PRODUCTÒ µ€R_%Code Signing||CODE_SIGN_ENTITLEMENTSÒ ¸€R_!Code Signing||CODE_SIGN_IDENTITYÒ »€R_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHÒ ¾€R_$Code Signing||OTHER_CODE_SIGN_FLAGSÒ Á€R_#Code Signing||PROVISIONING_PROFILEÒ Ä€R_Deployment||STRIPFLAGSÒ Ç€R_Deployment||ALTERNATE_GROUPÒ Ê€R_Deployment||ALTERNATE_OWNERÒ Í€R_Deployment||ALTERNATE_MODEÒ ЀR_(Deployment||ALTERNATE_PERMISSIONS_FILESÒ Ó€R_!Deployment||COMBINE_HIDPI_IMAGESÒ Ö€R_ Deployment||DEPLOYMENT_LOCATIONÒ Ù€R_&Deployment||DEPLOYMENT_POSTPROCESSINGÒ Ü€R_Deployment||INSTALL_GROUPÒ ߀R_Deployment||INSTALL_OWNERÒ â€R_Deployment||INSTALL_MODE_FLAGÒ å€R_Deployment||DSTROOTÒ è€R_Deployment||INSTALL_PATHÒ ë€R_%Deployment||MACOSX_DEPLOYMENT_TARGETÒ î€R_%Deployment||PRODUCT_DEFINITION_PLISTÒ ñ€R_-Deployment||RESOURCES_TARGETED_DEVICE_FAMILYÒ ô€R_Deployment||SKIP_INSTALLÒ ÷€R_$Deployment||STRIP_INSTALLED_PRODUCTÒ ú€R_Deployment||STRIP_STYLEÒ ý€R_Deployment||SEPARATE_STRIPÒ €R_"Headers||COPY_HEADERS_RUN_UNIFDEFÒ €R_$Headers||COPY_HEADERS_UNIFDEF_FLAGSÒ €R_Kernel Module||MODULE_NAMEÒ  €R_Kernel Module||MODULE_STARTÒ  €R_Kernel Module||MODULE_STOPÒ €R_Kernel Module||MODULE_VERSIONÒ €R_Linking||BUNDLE_LOADERÒ €R_%Linking||DYLIB_COMPATIBILITY_VERSIONÒ €R_Linking||DYLIB_CURRENT_VERSIONÒ €R_Linking||DEAD_CODE_STRIPPINGÒ €R_'Linking||LINKER_DISPLAYS_MANGLED_NAMESÒ !€R_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSÒ $€R_Linking||LD_DYLIB_INSTALL_NAMEÒ '€R_!Linking||DYLIB_INSTALL_NAME_BASEÒ *€R_Linking||EXPORTED_SYMBOLS_FILEÒ -€R_Linking||LD_NO_PIEÒ 0€R_Linking||INIT_ROUTINEÒ 3€R_&Linking||LINK_WITH_STANDARD_LIBRARIESÒ 6€R_Linking||MACH_O_TYPEÒ 9€R_Linking||ORDER_FILEÒ <€R_Linking||OTHER_LIBTOOLFLAGSÒ ?€R_Linking||OTHER_LDFLAGSÒ B€R_Linking||OTHER_TAPI_FLAGSÒ E€R_%Linking||GENERATE_MASTER_OBJECT_FILEÒ H€R_Linking||PRELINK_LIBSÒ K€R_Linking||KEEP_PRIVATE_EXTERNSÒ N€R_7Linking||LD_QUOTE_LINKER_ARGUMENTS_FOR_COMPILER_DRIVERÒ Q€R_$Linking||REEXPORTED_FRAMEWORK_NAMESÒ T€R_"Linking||REEXPORTED_LIBRARY_NAMESÒ W€R_"Linking||REEXPORTED_LIBRARY_PATHSÒ Z€R_!Linking||LD_RUNPATH_SEARCH_PATHSÒ ]€R_Linking||SEPARATE_SYMBOL_EDITÒ `€R_Linking||PRELINK_FLAGSÒ c€R_!Linking||SUPPORTS_TEXT_BASED_APIÒ f€R_Linking||SECTORDER_FLAGSÒ i€R_Linking||TEXT_BASED_API_FILEÒ l€R_!Linking||UNEXPORTED_SYMBOLS_FILEÒ o€R_Linking||WARNING_LDFLAGSÒ r€R_Linking||LD_GENERATE_MAP_FILEÒ u€R_%Packaging||APPLY_RULES_IN_COPY_FILESÒ x€R_.Packaging||CREATE_INFOPLIST_SECTION_IN_BINARYÒ {€R_Packaging||DEFINES_MODULEÒ ~€R_ Packaging||EXECUTABLE_EXTENSIONÒ €R_Packaging||EXECUTABLE_PREFIXÒ „€R_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSÒ ‡€R_!Packaging||GENERATE_PKGINFO_FILEÒ Š€R_Packaging||FRAMEWORK_VERSIONÒ €R_Packaging||INFOPLIST_FILEÒ €R_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSÒ “€R_#Packaging||INFOPLIST_OUTPUT_FORMATÒ –€R_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSÒ ™€R_#Packaging||INFOPLIST_PREFIX_HEADERÒ œ€R_Packaging||MODULEMAP_FILEÒ Ÿ€R_ Packaging||INFOPLIST_PREPROCESSÒ ¢€R_&Packaging||COPYING_PRESERVES_HFS_DATAÒ ¥€R_'Packaging||PRIVATE_HEADERS_FOLDER_PATHÒ ¨€R_"Packaging||MODULEMAP_PRIVATE_FILEÒ «€R_%Packaging||PRODUCT_BUNDLE_IDENTIFIERÒ ®€R_Packaging||PRODUCT_MODULE_NAMEÒ ±€R_Packaging||PRODUCT_NAMEÒ ´€R_$Packaging||PLIST_FILE_OUTPUT_FORMATÒ ·€R_&Packaging||PUBLIC_HEADERS_FOLDER_PATHÒ º€R_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGÒ ½€R_Packaging||WRAPPER_EXTENSIONÒ À€R_'Search Paths||ALWAYS_SEARCH_USER_PATHSÒ ÀR_%Search Paths||FRAMEWORK_SEARCH_PATHSÒ Æ€R_"Search Paths||HEADER_SEARCH_PATHSÒ É€R_#Search Paths||LIBRARY_SEARCH_PATHSÒ Ì€R_Search Paths||REZ_SEARCH_PATHSÒ Ï€R_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ Ò€R_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ Õ€R_Search Paths||USE_HEADERMAPÒ Ø€R_'Search Paths||USER_HEADER_SEARCH_PATHSÒ Û€R_Testing||TEST_HOSTÒ Þ€R_2Testing||TREAT_MISSING_BASELINES_AS_TEST_FAILURESÒ á€R_$Versioning||CURRENT_PROJECT_VERSIONÒ ä€R_Versioning||VERSION_INFO_FILEÒ ç€R_%Versioning||VERSION_INFO_EXPORT_DECLÒ ê€R_ Versioning||VERSION_INFO_PREFIXÒ í€R_ Versioning||VERSION_INFO_SUFFIXÒ ð€R_Versioning||VERSIONING_SYSTEMÒ ó€R_!Versioning||VERSION_INFO_BUILDERÒ ö€R_/(null) - Deployment||IPHONEOS_DEPLOYMENT_TARGETÒ ù€R_+(null) - Deployment||TVOS_DEPLOYMENT_TARGETÒ ü€R_.(null) - Deployment||WATCHOS_DEPLOYMENT_TARGETÒ ÿ€R_?Apple LLVM 7.0 - Code Generation||CLANG_DEBUG_INFORMATION_LEVELÒ €R_?Apple LLVM 7.0 - Code Generation||CLANG_X86_VECTOR_INSTRUCTIONSÒ €R_€R_6Apple LLVM 7.0 - Custom Compiler Flags||WARNING_CFLAGSÒ A€R_4Apple LLVM 7.0 - Language||GCC_CHAR_IS_UNSIGNED_CHARÒ D€R_1Apple LLVM 7.0 - Language||GCC_ENABLE_ASM_KEYWORDÒ G€R_2Apple LLVM 7.0 - Language||GCC_C_LANGUAGE_STANDARDÒ J€R_,Apple LLVM 7.0 - Language||GCC_CW_ASM_SYNTAXÒ M€R_-Apple LLVM 7.0 - Language||GCC_INPUT_FILETYPEÒ P€R_:Apple LLVM 7.0 - Language||GCC_LINK_WITH_DYNAMIC_LIBRARIESÒ S€R_/Apple LLVM 7.0 - Language||GCC_ENABLE_TRIGRAPHSÒ V€R_BApple LLVM 7.0 - Language||GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLSÒ Y€R_BApple LLVM 7.0 - Language||GCC_INCREASE_PRECOMPILED_HEADER_SHARINGÒ \€R_7Apple LLVM 7.0 - Language||GCC_PRECOMPILE_PREFIX_HEADERÒ _€R_,Apple LLVM 7.0 - Language||GCC_PREFIX_HEADERÒ b€R_7Apple LLVM 7.0 - Language||GCC_ENABLE_BUILTIN_FUNCTIONSÒ e€R_4Apple LLVM 7.0 - Language||GCC_ENABLE_PASCAL_STRINGSÒ h€R_*Apple LLVM 7.0 - Language||GCC_SHORT_ENUMSÒ k€R_=Apple LLVM 7.0 - Language||GCC_USE_STANDARD_INCLUDE_SEARCHINGÒ n€R_Apple LLVM 7.0 - Language - Objective C||CLANG_ENABLE_OBJC_ARCÒ €R_4Apple LLVM 7.0 - Preprocessing||ENABLE_NS_ASSERTIONSÒ ’€R_:Apple LLVM 7.0 - Preprocessing||ENABLE_STRICT_OBJC_MSGSENDÒ •€R_QApple LLVM 7.0 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSÒ ˜€R_@Apple LLVM 7.0 - Warning Policies||GCC_WARN_INHIBIT_ALL_WARNINGSÒ ›€R_4Apple LLVM 7.0 - Warning Policies||GCC_WARN_PEDANTICÒ ž€R_?Apple LLVM 7.0 - Warning Policies||GCC_TREAT_WARNINGS_AS_ERRORSÒ ¡€R_KApple LLVM 7.0 - Warnings - All languages||GCC_WARN_CHECK_SWITCH_STATEMENTSÒ ¤€R_NApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_DEPRECATED_FUNCTIONSÒ §€R_LApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_DOCUMENTATION_COMMENTSÒ ª€R_@Apple LLVM 7.0 - Warnings - All languages||CLANG_WARN_EMPTY_BODYÒ ­€R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_FOUR_CHARACTER_CONSTANTSÒ °€R_:Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_SHADOWÒ ³€R_EApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_BOOL_CONVERSIONÒ ¶€R_IApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_CONSTANT_CONVERSIONÒ ¹€R_KApple LLVM 7.0 - Warnings - All languages||GCC_WARN_64_TO_32_BIT_CONVERSIONÒ ¼€R_EApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_ENUM_CONVERSIONÒ ¿€R_DApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_INT_CONVERSIONÒ €R_NApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONÒ Å€R_TApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSIONÒ È€R_SApple LLVM 7.0 - Warnings - All languages||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDÒ Ë€R_EApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_RETURN_TYPEÒ ΀R_GApple LLVM 7.0 - Warnings - All languages||GCC_WARN_MISSING_PARENTHESESÒ Ñ€R_TApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSÒ Ô€R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_PROTOTYPESÒ ×€R_IApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_NEWLINEÒ Ú€R_AApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_ASSIGN_ENUMÒ Ý€R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_POINTER_SIGNEDNESSÒ à€R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_SIGN_COMPAREÒ ã€R_TApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONÒ æ€R_aApple LLVM 7.0 - Warnings - All languages||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSÒ é€R_]Apple LLVM 7.0 - Warnings - All languages||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSÒ ì€R_MApple LLVM 7.0 - Warnings - All languages||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFÒ ï€R_GApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNINITIALIZED_AUTOSÒ ò€R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNKNOWN_PRAGMASÒ õ€R_FApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_UNREACHABLE_CODEÒ ø€R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_FUNCTIONÒ û€R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_LABELÒ þ€R_DApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_PARAMETERÒ €R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_VALUEÒ €R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_VARIABLEÒ €R_BApple LLVM 7.0 - Warnings - C++||CLANG_WARN__EXIT_TIME_DESTRUCTORSÒ  €R_@Apple LLVM 7.0 - Warnings - C++||GCC_WARN_NON_VIRTUAL_DESTRUCTORÒ  €R_BApple LLVM 7.0 - Warnings - C++||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSÒ €R_FApple LLVM 7.0 - Warnings - C++||GCC_WARN_ABOUT_INVALID_OFFSETOF_MACROÒ €R_? @AB°9E<FMXchildrenVparent[contentType_ documentArchivableRepresentation[orientation€ùûóÕ“” •–HI™šLøôÌÄ÷Ò Në¡OõÀÓ¢£ R:¦ö€ÈVPcmMsrÿÿÿÿÿÿÿÓ= <@¯°€¨Ë€Ö>? @AB\°EM°Mú€û€Ò aë¡;òÀÒijef_'IDEWorkspaceTabControllerLayoutTreeNode¢gm_'IDEWorkspaceTabControllerLayoutTreeNodeÒijij_#IDEWorkspaceTabControllerLayoutTree¢km_#IDEWorkspaceTabControllerLayoutTree_{{688, 4}, {1400, 873}}Ò në¡€ÀÓ r{&¨stuvwxyz¨$}~€‚ƒ€cft{ž€%_BreakpointsActivated_DefaultEditorStatesForURLs\ActiveScheme_ActiveRunDestination_DefaultEditorFrameSizeForURLs_0LastCompletedPersistentSchemeBasedActivityReport_DocumentWindows_RecentEditorDocumentURLsÓ Ž‘&¢  ¢’“ J€%_7Xcode.Xcode3ProjectSupport.EditorDocument.Xcode3Project_'Xcode.IDEKit.EditorDocument.PlistEditorÓ ˜›&¢™ü Ï¢œ9€%Ó¼ ½°¿¢€Ð _gfile:///Users/pjkerly/Downloads/IntelPerformanceCounterMonitor-PCM-V2.10/MacMSRDriver/PcmMsr.xcodeproj/Ó ¥ª&¤¦§¨©¤«¬­®8€%_(Xcode3ProjectEditor.sourceList.splitview_,Xcode3ProjectEditorPreviousTargetEditorClass_,Xcode3ProjectEditorSelectedDocumentLocations_-Xcode3ProjectEditor_Xcode3BuildSettingsEditorÓ µ·&¡¶¡¸€%_DVTSplitViewItemsÒ ¼[¢½¾€$Ó ÁÄ¢ÂÃ¢ÅÆ€!]DVTIdentifier_DVTViewMagnitude_!sourceListSplitViewItemIdentifier#@d`Ó ÍТÂâÑÒ€!P#@†`_Xcode3BuildSettingsEditorÒ Øë¡Ù ÀÔ;<= ÜÝÞA#"!Á_gfile:///Users/pjkerly/Downloads/IntelPerformanceCounterMonitor-PCM-V2.10/MacMSRDriver/PcmMsr.xcodeproj/#A¼;3æO¹Ó ãç&£äåæ$%&£èéê'()€%VEditorVTarget_"Xcode3BuildSettingsEditorLocations_Xcode3BuildSettingsEditor\PcmMsrDriverÒ òë¡ó*ÀÓ öý&¦÷øùúûü+,-./0¦:ÿb:b€1¿€3¿€%_Xcode3BuildSettingsEditorMode_Selected Build Properties_$Xcode3BuildSettingsEditorDisplayMode_#Xcode3BuildPropertyValueDisplayMode_#Collapsed Build Property Categories_"Xcode3BuildPropertyNameDisplayModeÒ  [¡ 2€$Ò €R_%Packaging||PRODUCT_BUNDLE_IDENTIFIERÒ [¯ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ                  456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./01234567€$Ò  €R_Architectures||ADDITIONAL_SDKSÒ  €R_Architectures||ARCHSÒ  !€R_Architectures||SDKROOTÒ  $€R_#Architectures||SUPPORTED_PLATFORMSÒ  '€R_Architectures||VALID_ARCHSÒ  *€R_'Assets||ASSET_PACK_MANIFEST_URL_PREFIXÒ  -€R_,Assets||EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLEÒ  0€R_#Assets||ENABLE_ON_DEMAND_RESOURCESÒ  3€R_1Assets||ON_DEMAND_RESOURCES_INITIAL_INSTALL_TAGSÒ  6€R_+Assets||ON_DEMAND_RESOURCES_PREFETCH_ORDERÒ  9€R_Build Locations||SYMROOTÒ  <€R_Build Locations||OBJROOTÒ  ?€R_%Build Locations||SHARED_PRECOMPS_DIRÒ  B€R_Build Options||BUILD_VARIANTSÒ  E€R_Build Options||GCC_VERSIONÒ  H€R_/Build Options||EMBEDDED_CONTENT_CONTAINS_SWIFTÒ  K€R_'Build Options||GENERATE_PROFILING_CODEÒ  N€R_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRÒ  Q€R_.Build Options||APPLICATION_EXTENSION_API_ONLYÒ  T€R_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESÒ  W€R_ Build Options||VALIDATE_PRODUCTÒ  Z€R_%Code Signing||CODE_SIGN_ENTITLEMENTSÒ  ]€R_!Code Signing||CODE_SIGN_IDENTITYÒ  `€R_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHÒ  c€R_$Code Signing||OTHER_CODE_SIGN_FLAGSÒ  f€R_#Code Signing||PROVISIONING_PROFILEÒ  i€R_Deployment||STRIPFLAGSÒ  l€R_Deployment||ALTERNATE_GROUPÒ  o€R_Deployment||ALTERNATE_OWNERÒ  r€R_Deployment||ALTERNATE_MODEÒ  u€R_(Deployment||ALTERNATE_PERMISSIONS_FILESÒ  x€R_!Deployment||COMBINE_HIDPI_IMAGESÒ  {€R_ Deployment||DEPLOYMENT_LOCATIONÒ  ~€R_&Deployment||DEPLOYMENT_POSTPROCESSINGÒ  €R_Deployment||INSTALL_GROUPÒ  „€R_Deployment||INSTALL_OWNERÒ  ‡€R_Deployment||INSTALL_MODE_FLAGÒ  Š€R_Deployment||DSTROOTÒ  €R_Deployment||INSTALL_PATHÒ  €R_%Deployment||MACOSX_DEPLOYMENT_TARGETÒ  “€R_%Deployment||PRODUCT_DEFINITION_PLISTÒ  –€R_-Deployment||RESOURCES_TARGETED_DEVICE_FAMILYÒ  ™€R_Deployment||SKIP_INSTALLÒ  œ€R_$Deployment||STRIP_INSTALLED_PRODUCTÒ  Ÿ€R_Deployment||STRIP_STYLEÒ  ¢€R_Deployment||SEPARATE_STRIPÒ  ¥€R_"Headers||COPY_HEADERS_RUN_UNIFDEFÒ  ¨€R_$Headers||COPY_HEADERS_UNIFDEF_FLAGSÒ  «€R_Kernel Module||MODULE_NAMEÒ  ®€R_Kernel Module||MODULE_STARTÒ  ±€R_Kernel Module||MODULE_STOPÒ  ´€R_Kernel Module||MODULE_VERSIONÒ  ·€R_Linking||BUNDLE_LOADERÒ  º€R_%Linking||DYLIB_COMPATIBILITY_VERSIONÒ  ½€R_Linking||DYLIB_CURRENT_VERSIONÒ  À€R_Linking||DEAD_CODE_STRIPPINGÒ  ÀR_'Linking||LINKER_DISPLAYS_MANGLED_NAMESÒ  Æ€R_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSÒ  É€R_Linking||LD_DYLIB_INSTALL_NAMEÒ  Ì€R_!Linking||DYLIB_INSTALL_NAME_BASEÒ  Ï€R_Linking||EXPORTED_SYMBOLS_FILEÒ  Ò€R_Linking||LD_NO_PIEÒ  Õ€R_Linking||INIT_ROUTINEÒ  Ø€R_&Linking||LINK_WITH_STANDARD_LIBRARIESÒ  Û€R_Linking||MACH_O_TYPEÒ  Þ€R_Linking||ORDER_FILEÒ  á€R_Linking||OTHER_LIBTOOLFLAGSÒ  ä€R_Linking||OTHER_LDFLAGSÒ  ç€R_Linking||OTHER_TAPI_FLAGSÒ  ê€R_%Linking||GENERATE_MASTER_OBJECT_FILEÒ  í€R_Linking||PRELINK_LIBSÒ  ð€R_Linking||KEEP_PRIVATE_EXTERNSÒ  ó€R_7Linking||LD_QUOTE_LINKER_ARGUMENTS_FOR_COMPILER_DRIVERÒ  ö€R_$Linking||REEXPORTED_FRAMEWORK_NAMESÒ  ù€R_"Linking||REEXPORTED_LIBRARY_NAMESÒ  ü€R_"Linking||REEXPORTED_LIBRARY_PATHSÒ  ÿ€R_!Linking||LD_RUNPATH_SEARCH_PATHSÒ  €R_Linking||SEPARATE_SYMBOL_EDITÒ  €R_Linking||PRELINK_FLAGSÒ  €R_!Linking||SUPPORTS_TEXT_BASED_APIÒ  €R_Linking||SECTORDER_FLAGSÒ  €R_Linking||TEXT_BASED_API_FILEÒ  €R_!Linking||UNEXPORTED_SYMBOLS_FILEÒ  €R_Linking||WARNING_LDFLAGSÒ  €R_Linking||LD_GENERATE_MAP_FILEÒ  €R_%Packaging||APPLY_RULES_IN_COPY_FILESÒ  €R_.Packaging||CREATE_INFOPLIST_SECTION_IN_BINARYÒ  €R_Packaging||DEFINES_MODULEÒ  #€R_ Packaging||EXECUTABLE_EXTENSIONÒ  &€R_Packaging||EXECUTABLE_PREFIXÒ  )€R_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSÒ  ,€R_!Packaging||GENERATE_PKGINFO_FILEÒ  /€R_Packaging||FRAMEWORK_VERSIONÒ  2€R_Packaging||INFOPLIST_FILEÒ  5€R_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSÒ  8€R_#Packaging||INFOPLIST_OUTPUT_FORMATÒ  ;€R_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSÒ  >€R_#Packaging||INFOPLIST_PREFIX_HEADERÒ  A€R_Packaging||MODULEMAP_FILEÒ  D€R_ Packaging||INFOPLIST_PREPROCESSÒ  G€R_&Packaging||COPYING_PRESERVES_HFS_DATAÒ  J€R_'Packaging||PRIVATE_HEADERS_FOLDER_PATHÒ  M€R_"Packaging||MODULEMAP_PRIVATE_FILEÒ  P€R_%Packaging||PRODUCT_BUNDLE_IDENTIFIERÒ  S€R_Packaging||PRODUCT_MODULE_NAMEÒ  V€R_Packaging||PRODUCT_NAMEÒ  Y€R_$Packaging||PLIST_FILE_OUTPUT_FORMATÒ  \€R_&Packaging||PUBLIC_HEADERS_FOLDER_PATHÒ  _€R_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGÒ  b€R_Packaging||WRAPPER_EXTENSIONÒ  e€R_'Search Paths||ALWAYS_SEARCH_USER_PATHSÒ  h€R_%Search Paths||FRAMEWORK_SEARCH_PATHSÒ  k€R_"Search Paths||HEADER_SEARCH_PATHSÒ  n€R_#Search Paths||LIBRARY_SEARCH_PATHSÒ  q€R_Search Paths||REZ_SEARCH_PATHSÒ  t€R_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ  w€R_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ  z€R_Search Paths||USE_HEADERMAPÒ  }€R_'Search Paths||USER_HEADER_SEARCH_PATHSÒ  €€R_Testing||TEST_HOSTÒ  ƒ€R_2Testing||TREAT_MISSING_BASELINES_AS_TEST_FAILURESÒ  †€R_$Versioning||CURRENT_PROJECT_VERSIONÒ  ‰€R_Versioning||VERSION_INFO_FILEÒ  Œ€R_%Versioning||VERSION_INFO_EXPORT_DECLÒ  €R_ Versioning||VERSION_INFO_PREFIXÒ  ’€R_ Versioning||VERSION_INFO_SUFFIXÒ  •€R_Versioning||VERSIONING_SYSTEMÒ  ˜€R_!Versioning||VERSION_INFO_BUILDERÒ  ›€R_/(null) - Deployment||IPHONEOS_DEPLOYMENT_TARGETÒ  ž€R_+(null) - Deployment||TVOS_DEPLOYMENT_TARGETÒ  ¡€R_.(null) - Deployment||WATCHOS_DEPLOYMENT_TARGETÒ  ¤€R_?Apple LLVM 7.0 - Code Generation||CLANG_DEBUG_INFORMATION_LEVELÒ  §€R_?Apple LLVM 7.0 - Code Generation||CLANG_X86_VECTOR_INSTRUCTIONSÒ  ª€R_Apple LLVM 7.0 - Language - Objective C||CLANG_ENABLE_OBJC_ARCÒ  4€R_4Apple LLVM 7.0 - Preprocessing||ENABLE_NS_ASSERTIONSÒ  7€R_:Apple LLVM 7.0 - Preprocessing||ENABLE_STRICT_OBJC_MSGSENDÒ  :€R_QApple LLVM 7.0 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSÒ  =€R_@Apple LLVM 7.0 - Warning Policies||GCC_WARN_INHIBIT_ALL_WARNINGSÒ  @€R_4Apple LLVM 7.0 - Warning Policies||GCC_WARN_PEDANTICÒ  C€R_?Apple LLVM 7.0 - Warning Policies||GCC_TREAT_WARNINGS_AS_ERRORSÒ  F€R_KApple LLVM 7.0 - Warnings - All languages||GCC_WARN_CHECK_SWITCH_STATEMENTSÒ  I€R_NApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_DEPRECATED_FUNCTIONSÒ  L€R_LApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_DOCUMENTATION_COMMENTSÒ  O€R_@Apple LLVM 7.0 - Warnings - All languages||CLANG_WARN_EMPTY_BODYÒ  R€R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_FOUR_CHARACTER_CONSTANTSÒ  U€R_:Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_SHADOWÒ  X€R_EApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_BOOL_CONVERSIONÒ  [€R_IApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_CONSTANT_CONVERSIONÒ  ^€R_KApple LLVM 7.0 - Warnings - All languages||GCC_WARN_64_TO_32_BIT_CONVERSIONÒ  a€R_EApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_ENUM_CONVERSIONÒ  d€R_DApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_INT_CONVERSIONÒ  g€R_NApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONÒ  j€R_TApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSIONÒ  m€R_SApple LLVM 7.0 - Warnings - All languages||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDÒ  p€R_EApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_RETURN_TYPEÒ  s€R_GApple LLVM 7.0 - Warnings - All languages||GCC_WARN_MISSING_PARENTHESESÒ  v€R_TApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSÒ  y€R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_PROTOTYPESÒ  |€R_IApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_NEWLINEÒ  €R_AApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_ASSIGN_ENUMÒ  ‚€R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_POINTER_SIGNEDNESSÒ  …€R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_SIGN_COMPAREÒ  ˆ€R_TApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONÒ  ‹€R_aApple LLVM 7.0 - Warnings - All languages||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSÒ  Ž€R_]Apple LLVM 7.0 - Warnings - All languages||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSÒ  ‘€R_MApple LLVM 7.0 - Warnings - All languages||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFÒ  ”€R_GApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNINITIALIZED_AUTOSÒ  —€R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNKNOWN_PRAGMASÒ  š€R_FApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_UNREACHABLE_CODEÒ  €R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_FUNCTIONÒ   €R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_LABELÒ  £€R_DApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_PARAMETERÒ  ¦€R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_VALUEÒ  ©€R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_VARIABLEÒ  ¬€R_BApple LLVM 7.0 - Warnings - C++||CLANG_WARN__EXIT_TIME_DESTRUCTORSÒ  ¯€R_@Apple LLVM 7.0 - Warnings - C++||GCC_WARN_NON_VIRTUAL_DESTRUCTORÒ  ²€R_BApple LLVM 7.0 - Warnings - C++||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSÒ  µ€R_FApple LLVM 7.0 - Warnings - C++||GCC_WARN_ABOUT_INVALID_OFFSETOF_MACROÒ  ¸€R_=€¨Á#A¼;AÄhìÓ A E&£FG D€«€¬?£ FK H@€¯A€%_"Xcode3BuildSettingsEditorLocations_Xcode3BuildSettingsEditorÒ Më¡ NBÀÓ Q X&¦YZ[\]^€²€³€´€µ€¶€·¦ Y Zb::bCH¿€€¿€%Ò a[¯ b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  € ‚ ƒ „ … † ‡ ˆ ‰ Š ‹ Œ Ž ‘ ’ “ ” • – — ˜ ™ š › œ ž Ÿ   ¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ­ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d eDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFG€$Ò  i€R_Architectures||ADDITIONAL_SDKSÒ  l€R_Architectures||ARCHSÒ  o€R_Architectures||SDKROOTÒ  r€R_#Architectures||SUPPORTED_PLATFORMSÒ  u€R_Architectures||VALID_ARCHSÒ  x€R_'Assets||ASSET_PACK_MANIFEST_URL_PREFIXÒ  {€R_,Assets||EMBED_ASSET_PACKS_IN_PRODUCT_BUNDLEÒ  ~€R_#Assets||ENABLE_ON_DEMAND_RESOURCESÒ  €R_1Assets||ON_DEMAND_RESOURCES_INITIAL_INSTALL_TAGSÒ  „€R_+Assets||ON_DEMAND_RESOURCES_PREFETCH_ORDERÒ  ‡€R_Build Locations||SYMROOTÒ  Š€R_Build Locations||OBJROOTÒ  €R_%Build Locations||SHARED_PRECOMPS_DIRÒ  €R_Build Options||BUILD_VARIANTSÒ  “€R_Build Options||GCC_VERSIONÒ  –€R_/Build Options||EMBEDDED_CONTENT_CONTAINS_SWIFTÒ  ™€R_'Build Options||GENERATE_PROFILING_CODEÒ  œ€R_@Build Options||PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIRÒ  Ÿ€R_.Build Options||APPLICATION_EXTENSION_API_ONLYÒ  ¢€R_2Build Options||SCAN_ALL_SOURCE_FILES_FOR_INCLUDESÒ  ¥€R_ Build Options||VALIDATE_PRODUCTÒ  ¨€R_%Code Signing||CODE_SIGN_ENTITLEMENTSÒ  «€R_!Code Signing||CODE_SIGN_IDENTITYÒ  ®€R_,Code Signing||CODE_SIGN_RESOURCE_RULES_PATHÒ  ±€R_$Code Signing||OTHER_CODE_SIGN_FLAGSÒ  ´€R_#Code Signing||PROVISIONING_PROFILEÒ  ·€R_Deployment||STRIPFLAGSÒ  º€R_Deployment||ALTERNATE_GROUPÒ  ½€R_Deployment||ALTERNATE_OWNERÒ  À€R_Deployment||ALTERNATE_MODEÒ  ÀR_(Deployment||ALTERNATE_PERMISSIONS_FILESÒ  Æ€R_!Deployment||COMBINE_HIDPI_IMAGESÒ  É€R_ Deployment||DEPLOYMENT_LOCATIONÒ  Ì€R_&Deployment||DEPLOYMENT_POSTPROCESSINGÒ  Ï€R_Deployment||INSTALL_GROUPÒ  Ò€R_Deployment||INSTALL_OWNERÒ  Õ€R_Deployment||INSTALL_MODE_FLAGÒ  Ø€R_Deployment||DSTROOTÒ  Û€R_Deployment||INSTALL_PATHÒ  Þ€R_%Deployment||MACOSX_DEPLOYMENT_TARGETÒ  á€R_%Deployment||PRODUCT_DEFINITION_PLISTÒ  ä€R_-Deployment||RESOURCES_TARGETED_DEVICE_FAMILYÒ  ç€R_Deployment||SKIP_INSTALLÒ  ê€R_$Deployment||STRIP_INSTALLED_PRODUCTÒ  í€R_Deployment||STRIP_STYLEÒ  ð€R_Deployment||SEPARATE_STRIPÒ  ó€R_"Headers||COPY_HEADERS_RUN_UNIFDEFÒ  ö€R_$Headers||COPY_HEADERS_UNIFDEF_FLAGSÒ  ù€R_Kernel Module||MODULE_NAMEÒ  ü€R_Kernel Module||MODULE_STARTÒ  ÿ€R_Kernel Module||MODULE_STOPÒ €R_Kernel Module||MODULE_VERSIONÒ €R_Linking||BUNDLE_LOADERÒ €R_%Linking||DYLIB_COMPATIBILITY_VERSIONÒ  €R_Linking||DYLIB_CURRENT_VERSIONÒ €R_Linking||DEAD_CODE_STRIPPINGÒ €R_'Linking||LINKER_DISPLAYS_MANGLED_NAMESÒ €R_,Linking||PRESERVE_DEAD_CODE_INITS_AND_TERMSÒ €R_Linking||LD_DYLIB_INSTALL_NAMEÒ €R_!Linking||DYLIB_INSTALL_NAME_BASEÒ €R_Linking||EXPORTED_SYMBOLS_FILEÒ  €R_Linking||LD_NO_PIEÒ #€R_Linking||INIT_ROUTINEÒ &€R_&Linking||LINK_WITH_STANDARD_LIBRARIESÒ )€R_Linking||MACH_O_TYPEÒ ,€R_Linking||ORDER_FILEÒ /€R_Linking||OTHER_LIBTOOLFLAGSÒ 2€R_Linking||OTHER_LDFLAGSÒ 5€R_Linking||OTHER_TAPI_FLAGSÒ 8€R_%Linking||GENERATE_MASTER_OBJECT_FILEÒ ;€R_Linking||PRELINK_LIBSÒ >€R_Linking||KEEP_PRIVATE_EXTERNSÒ A€R_7Linking||LD_QUOTE_LINKER_ARGUMENTS_FOR_COMPILER_DRIVERÒ D€R_$Linking||REEXPORTED_FRAMEWORK_NAMESÒ G€R_"Linking||REEXPORTED_LIBRARY_NAMESÒ J€R_"Linking||REEXPORTED_LIBRARY_PATHSÒ M€R_!Linking||LD_RUNPATH_SEARCH_PATHSÒ P€R_Linking||SEPARATE_SYMBOL_EDITÒ S€R_Linking||PRELINK_FLAGSÒ V€R_!Linking||SUPPORTS_TEXT_BASED_APIÒ Y€R_Linking||SECTORDER_FLAGSÒ \€R_Linking||TEXT_BASED_API_FILEÒ _€R_!Linking||UNEXPORTED_SYMBOLS_FILEÒ b€R_Linking||WARNING_LDFLAGSÒ e€R_Linking||LD_GENERATE_MAP_FILEÒ h€R_%Packaging||APPLY_RULES_IN_COPY_FILESÒ k€R_.Packaging||CREATE_INFOPLIST_SECTION_IN_BINARYÒ n€R_Packaging||DEFINES_MODULEÒ q€R_ Packaging||EXECUTABLE_EXTENSIONÒ t€R_Packaging||EXECUTABLE_PREFIXÒ w€R_+Packaging||INFOPLIST_EXPAND_BUILD_SETTINGSÒ z€R_!Packaging||GENERATE_PKGINFO_FILEÒ }€R_Packaging||FRAMEWORK_VERSIONÒ €€R_Packaging||INFOPLIST_FILEÒ ƒ€R_.Packaging||INFOPLIST_OTHER_PREPROCESSOR_FLAGSÒ †€R_#Packaging||INFOPLIST_OUTPUT_FORMATÒ ‰€R_.Packaging||INFOPLIST_PREPROCESSOR_DEFINITIONSÒ Œ€R_#Packaging||INFOPLIST_PREFIX_HEADERÒ €R_Packaging||MODULEMAP_FILEÒ ’€R_ Packaging||INFOPLIST_PREPROCESSÒ •€R_&Packaging||COPYING_PRESERVES_HFS_DATAÒ ˜€R_'Packaging||PRIVATE_HEADERS_FOLDER_PATHÒ ›€R_"Packaging||MODULEMAP_PRIVATE_FILEÒ ž€R_%Packaging||PRODUCT_BUNDLE_IDENTIFIERÒ ¡€R_Packaging||PRODUCT_MODULE_NAMEÒ ¤€R_Packaging||PRODUCT_NAMEÒ §€R_$Packaging||PLIST_FILE_OUTPUT_FORMATÒ ª€R_&Packaging||PUBLIC_HEADERS_FOLDER_PATHÒ ­€R_(Packaging||STRINGS_FILE_OUTPUT_ENCODINGÒ °€R_Packaging||WRAPPER_EXTENSIONÒ ³€R_'Search Paths||ALWAYS_SEARCH_USER_PATHSÒ ¶€R_%Search Paths||FRAMEWORK_SEARCH_PATHSÒ ¹€R_"Search Paths||HEADER_SEARCH_PATHSÒ ¼€R_#Search Paths||LIBRARY_SEARCH_PATHSÒ ¿€R_Search Paths||REZ_SEARCH_PATHSÒ €R_<Search Paths||EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ Å€R_<Search Paths||INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIESÒ È€R_Search Paths||USE_HEADERMAPÒ Ë€R_'Search Paths||USER_HEADER_SEARCH_PATHSÒ ΀R_Testing||TEST_HOSTÒ Ñ€R_2Testing||TREAT_MISSING_BASELINES_AS_TEST_FAILURESÒ Ô€R_$Versioning||CURRENT_PROJECT_VERSIONÒ ×€R_Versioning||VERSION_INFO_FILEÒ Ú€R_%Versioning||VERSION_INFO_EXPORT_DECLÒ Ý€R_ Versioning||VERSION_INFO_PREFIXÒ à€R_ Versioning||VERSION_INFO_SUFFIXÒ ã€R_Versioning||VERSIONING_SYSTEMÒ æ€R_!Versioning||VERSION_INFO_BUILDERÒ é€R_/(null) - Deployment||IPHONEOS_DEPLOYMENT_TARGETÒ ì€R_+(null) - Deployment||TVOS_DEPLOYMENT_TARGETÒ ï€R_.(null) - Deployment||WATCHOS_DEPLOYMENT_TARGETÒ ò€R_?Apple LLVM 7.0 - Code Generation||CLANG_DEBUG_INFORMATION_LEVELÒ õ€R_?Apple LLVM 7.0 - Code Generation||CLANG_X86_VECTOR_INSTRUCTIONSÒ ø€R_Apple LLVM 7.0 - Language - Objective C||CLANG_ENABLE_OBJC_ARCÒ ‚€R_4Apple LLVM 7.0 - Preprocessing||ENABLE_NS_ASSERTIONSÒ …€R_:Apple LLVM 7.0 - Preprocessing||ENABLE_STRICT_OBJC_MSGSENDÒ ˆ€R_QApple LLVM 7.0 - Preprocessing||GCC_PREPROCESSOR_DEFINITIONS_NOT_USED_IN_PRECOMPSÒ ‹€R_@Apple LLVM 7.0 - Warning Policies||GCC_WARN_INHIBIT_ALL_WARNINGSÒ Ž€R_4Apple LLVM 7.0 - Warning Policies||GCC_WARN_PEDANTICÒ ‘€R_?Apple LLVM 7.0 - Warning Policies||GCC_TREAT_WARNINGS_AS_ERRORSÒ ”€R_KApple LLVM 7.0 - Warnings - All languages||GCC_WARN_CHECK_SWITCH_STATEMENTSÒ —€R_NApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_DEPRECATED_FUNCTIONSÒ š€R_LApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_DOCUMENTATION_COMMENTSÒ €R_@Apple LLVM 7.0 - Warnings - All languages||CLANG_WARN_EMPTY_BODYÒ  €R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_FOUR_CHARACTER_CONSTANTSÒ £€R_:Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_SHADOWÒ ¦€R_EApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_BOOL_CONVERSIONÒ ©€R_IApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_CONSTANT_CONVERSIONÒ ¬€R_KApple LLVM 7.0 - Warnings - All languages||GCC_WARN_64_TO_32_BIT_CONVERSIONÒ ¯€R_EApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_ENUM_CONVERSIONÒ ²€R_DApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_INT_CONVERSIONÒ µ€R_NApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_IMPLICIT_SIGN_CONVERSIONÒ ¸€R_TApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSIONÒ »€R_SApple LLVM 7.0 - Warnings - All languages||GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETEDÒ ¾€R_EApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_RETURN_TYPEÒ Á€R_GApple LLVM 7.0 - Warnings - All languages||GCC_WARN_MISSING_PARENTHESESÒ Ä€R_TApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERSÒ Ç€R_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_PROTOTYPESÒ Ê€R_IApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_MISSING_NEWLINEÒ Í€R_AApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_ASSIGN_ENUMÒ ЀR_LApple LLVM 7.0 - Warnings - All languages||GCC_WARN_ABOUT_POINTER_SIGNEDNESSÒ Ó€R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_SIGN_COMPAREÒ Ö€R_TApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSIONÒ Ù€R_aApple LLVM 7.0 - Warnings - All languages||GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORSÒ Ü€R_]Apple LLVM 7.0 - Warnings - All languages||GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORSÒ ߀R_MApple LLVM 7.0 - Warnings - All languages||GCC_WARN_TYPECHECK_CALLS_TO_PRINTFÒ â€R_GApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNINITIALIZED_AUTOSÒ å€R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNKNOWN_PRAGMASÒ è€R_FApple LLVM 7.0 - Warnings - All languages||CLANG_WARN_UNREACHABLE_CODEÒ ë€R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_FUNCTIONÒ î€R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_LABELÒ ñ€R_DApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_PARAMETERÒ ô€R_@Apple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_VALUEÒ ÷€R_CApple LLVM 7.0 - Warnings - All languages||GCC_WARN_UNUSED_VARIABLEÒ ú€R_BApple LLVM 7.0 - Warnings - C++||CLANG_WARN__EXIT_TIME_DESTRUCTORSÒ ý€R_@Apple LLVM 7.0 - Warnings - C++||GCC_WARN_NON_VIRTUAL_DESTRUCTORÒ €R_BApple LLVM 7.0 - Warnings - C++||GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONSÒ €R_FApple LLVM 7.0 - Warnings - C++||GCC_WARN_ABOUT_INVALID_OFFSETOF_MACROÒ €R_?@ŒŽ€%#@R: \PcmMsrDriverÓ FJ&£‚ƒ£KLM‘’€%#?ðc % ÒR STWNS.dataOabplist00Ô;Troot€#-27EKR[boqsu†Ž™œž £¥§©°¸ÁÈÑØáäæè!$-4<ILNPU]`eruz’§¹¼Á?ÓÒijVW]NSMutableData£VXmVNSDataÓ Za&¦\]_`•–ƒ—˜¦!bdebb„¿™›¿¿€%_"IDEActivityReportStringSegmentType_"IDEActivityReportStringSegmentDate_'IDEActivityReportStringSegmentDateStyle_'IDEActivityReportStringSegmentTimeStyleÒn opWNS.time#A¼;cø\ÍšÒijrsVNSDate¢rm_Today at 4:15 PMêÒ w[¡ €€$Ò {[¤ü}òÏMuŸ€$Ó¼ ½°¿„€Ð _nfile:///Users/pjkerly/Downloads/IntelPerformanceCounterMonitor-PCM-V2.10/MacMSRDriver/PcmMsr/PcmMsr-Info.plist_NSKeyedArchiverш‰UState€"+5:? … ‹ ˜   « ² · ¹ » À Â Å Ç î   ! # % ' ) + - / > @ C F H J L N P ‘   Ä ó  1 L Y l n p r t v x z | ~ ‘ “ • — ™ › Ÿ ¡ £ ¥ ¾ Ñ Ý O b q  ‚ • ¢ ¥ § ª ¬ ® Â Ë Ð Ò Ô Ö ã è ê ì ñ ó õ ÷    " + 6 ? L Q Z g l n p u w y { „ œ £ « ´ Ê Ñ Þ ç é ë í ï ø ú ü þ%H\|‰–˜šœž ¢¯±³µ·¹»½Ô"FUgtwy|~€’Ÿ¢¤§©«²¿ÄÆÈÍÏÑÓÔâïôöøýÿ  %')+>KNPSUWdefhi€’•—™¦©«®°²¿ÔÖØÚÜÞàâäæèýÿ   :`Œ¥Ïþ5Nˆ•— ´»ÏÚçðýÿ 4=OV_loqtvx— §°³µ·ÌÕÜáê )>@Q\egikt„˜šœ¥§¨µ¸º½¿ÁÎÏÐÒõ!#%':<>@BDFHJLNi}‘ ¸Ìèý )6=CLMOXY[degpqs€ƒ…ˆŠŒ•œž ¢¤±¶¸º¿ÁÃÅØáîóõ÷üþ&+-/468:M¤±ÄÆÈÊÌÎÐÒÔÖéëíïñóöùüÿ-;R]s–ªµÂÅÇÊÌÎö  !(*,.579<>j‚𣦍«¸ÇÉËÍÏÑÓÕäæèëîñô÷ù-A]{‡¡®·¹»½¿ÈÊÌÎÑÓþ-\Œ™œž¡£¥¹ÂÇÉËÍÚßáãèêìî(-/168:<Eajmorƒ—£¥§©¬  "$&-/135<Ch„‘šŸ¢¯¼¾ÀÂÄÆÈÕ×ÚÝßáäæ (Ou•ºÃ Ï Ñ Ó Õ × Ù Û Ý ß á ã å ç é ë í ï ñ ó õ ÷ ù û ý ÿ!!!!! ! ! !!!!!!!!!!!!#!%!'!)!+!-!/!1!3!5!7!9!;!=!?!A!C!E!G!I!K!M!O!Q!S!U!W!Y![!]!`!c!f!i!l!o!r!u!x!{!~!!„!‡!Š!!!“!–!™!œ!Ÿ!¢!¥!¨!«!®!±!´!·!º!½!À!Ã!Æ!É!Ì!Ï!Ò!Õ!Ø!Û!Þ!á!ä!ç!ê!í!ð!ó!ö!ù!ü!ÿ"""" """"""" "#"&")","/"2"5"8";">"A"D"G"J"M"P"S"V"Y"\"_"b"e"h"k"n"q"t"w"z"}"€"ƒ"†"‰"Œ""’"•"˜"›"ž"¡"¤"§"ª"­"°"³"¶"¹"¼"¿"Â"Å"È"Ë"Î"Ñ"Ô"×"Ú"Ý"à"ã"æ"é"ì"ï"ò"õ"ø"û"þ#### # #######"#%#(#+#.#1#4#7#:#=#@#C#F#I#L#O#R#U#X#[#^#a#d#g#j#m#p#s#v#y#|##‚#…#ˆ#‹#Ž#‘#”#–#Ÿ#¡#Ã#Ì#Î#æ#ï#ñ$ $$$<$E$G$e$n$p$š$£$¥$Ô$Ý$ß%%%%D%M%O%}%†%ˆ%¤%­%¯%Ë%Ô%Ö%þ&& &*&3&5&S&\&^&&™&›&Å&Î&Ð''''O'X'Z''˜'š'½'Æ'È'ð'ù'û((((*(Y(b(d(‹(”(–(¼(Å(Ç(á(ê(ì) )))5)>)@)^)g)i)”))Ÿ)Ã)Ì)Î)ñ)ú)ü*%*.*0*M*V*X*u*~*€*¡*ª*¬*Ã*Ì*Î*ê*ó*õ++&+(+P+Y+[+‹+”+–+²+»+½+ä+í+ï, ,,,3,<,>,c,l,n,•,ž, ,¾,Ç,É,è,ñ,ó----=-F-H-b-k-m-•-ž- -Â-Ë-Í-í-ö-ø.".+.-.\.e.g.‰.’.”.¸.Á.Ã.å.î.ð////*/3/5/^/g/i//Š/Œ/£/¬/®/Í/Ö/Ø/ò/û/ý00#0%0M0V0X0q0z0|00¦0¨0â0ë0í1111D1M1O1t1}11£1¬1®1Ï1Ø1Ú1ô1ý1ÿ2#2,2.2J2S2U2u2~2€2¤2­2¯2Ë2Ô2Ö2÷333*33353f3o3q3Ž3—3™3¼3Å3Ç3ç3ð3ò4 4)4+4O4X4Z4z4ƒ4…4¢4«4­4Þ4ç4é5555K5T5V5|5…5‡5¤5­5¯5Ò5Û5Ý6666;6D6F6k6t6v6ž6§6©6Ë6Ô6Ö6ñ6ú6ü7#7,7.7W7`7b77–7˜7¸7Á7Ã7í7ö7ø8 8)8+8P8Y8[88Š8Œ8®8·8¹8ø999B9K9M9l9u9w9¡9ª9¬9Â9Ë9Í:: : :4:=:?:`:i:k:“:œ:ž:Á:Ê:Ì:ï:ø:ú;;$;&;J;S;U;‡;;’;À;É;Ë;ü<<> >>Q>Z>\>¡>ª>¬>î>÷>ù?&?/?1?g?p?r?«?´?¶?ú@@@7@@@B@}@†@ˆ@Ç@Ð@ÒAAAAUA^A`A—A A¢AáAêAìB%B.B0BgBpBrB¦B¯B±BæBïBñC C)C+C[CdCfC£C¬C®CàCéCëD0D9D;D€D‰D‹DÅDÎDÐDÿEE EDEMEOE†EE‘E¾EÇEÉF FFFSF\F^F“FœFžFÛFäFæGG&G(G…GŽGGÕGÞGàHH%H'HeHnHpH¶H¿HÁII IIPIYI[I’I›IIÚIãIåJ9JBJDJ‡JJ’JÉJÒJÔKKK!KoKxKzKËKÔKÖL%L.L0LsL|L~LÍLÖLØMMM MhMqMsM¿MÈMÊNN!N#NkNtNvN½NÆNÈOO"O$O{O„O†OÜOåOçP/P8P:P„PPPæPïPñQ@QIQKQ—Q Q¢QæQïQñR@RIRKRŽR—R™RðRùRûS_ShSjSÊSÓSÕT%T.T0TzTƒT…TËTÔTÖUU(U*UpUyU{U¾UÇUÉVVVV^VgViV¯V¸VºVÿWW WMWVWXWW¦W¨WñWúWüX;XDXFX’X›XXëXôXöYLYUYWY­Y¶Y¸ZZZZgZpZrZ¼ZÅZÇ[[[[`[i[k[Æ[Ï[Ñ\)\2\4\Œ\•\—\í\ö\ø],]5]7]m]v]x]·]À]Â^^^^_^h^j^±^º^¼__ _ _C_L_N_“_œ_ž_æ_ï_ñ`C`L`N`˜`¡`£`î`÷`ùaAaJaLa—a a¢aòaûaýbIbRbTb¯b¸bºccccncwcycÉcÒcÔd&d/d1d‚d‹dŽd‘d“dœdždÆdÏdÔdÝdýee$e:eGeHeIeKe`ese‚e•e±e´e·eºe½eÀeòeûeþfffff"f%f'f*f1f:fYf^f}f†f“f•f˜fšf£f¹f¾fÔfÝgg g8g?gyg†gŽgšgœgŸg¡gªg°gµg¾gÁgÄgÇgÞgëgôg÷gúgýhh h hhhhh h*h5hMhZhchfhihlhohxhzh|hhhƒh¡h¾háhúiiiiiiiii!i5iBiEiGiJiMiOiXi]i`icieiriwiyi{i€iƒi†iˆi‘iši§i¬i®i°iµi¸i»i½iÇiÐiÝiÞißiáiîiñióiöiùiûjj j jjjj#j%j'j,j/j2j4j>jGjTjYj[j]jbjejhjjj|j…j–j«jÅjàjãjåjèjëkk kk kCkOkQkTkWkZkokrkukxk{k~k‡kŠkkkk k¢k¥k¬kµkÂkÄkÇkÉkâkåkçkêkìkõkøkûkþll1l6l`lill”lºlÔlÝlàlâlålòmmm m mmmmmm,m.m1m4m7m:m=m@mCmEm\mym†mm½mðnnn*n/n2n5n:n=n@nBn|n¦n³n¸n»n¾nÃnÆnÉnËnØnÚnÝnàoJoWo`ocofoiolouoxo{o~ooƒo®oÝp p<pIpLpOpRpUpWpkptpyp|pppŽp“p–p™pžp¡p¤p¦p´pÇpëpôqqq q qqqqqq#q?qHqKqNqQqbqeqhqkqnqØqáqîqõqøqûqþrrr rrrrrCr_rlrurxr{r~r‹r˜r›ržr¡r¤r§rªr·r¹r¼r¿rÁrÄrÇrÉréss,sRsxss¦s©s¬s®s·s¹sásêuöuùuüuÿvvvv vvvvvvv v#v&v)v,v/v2v5v8v;v>vAvDvGvJvMvPvSvVvYv\v_vbvevhvkvnvqvtvwvzv}v€vƒv†v‰vŒvv’v•v˜v›vžv¡v¤v§vªv­v°v³v¶v¹v¼v¿vÂvÅvÈvËvÎvÑvÔv×vÚvÝvàvãvævévìvïvòvõvøvûvþwwww w wwwwwww"w%w(w+w.w1w4w7w:w=w@wCwFwIwLwOwRwUwXw[w^wawdwgwjwmwpwswvwyw|ww‚w…wˆw‹wŽw‘w”w—wšww w£w¦w©w¬w¯w²wµw¸w»w¾wÁwÄwÇwÊwÍwÐwÓwÖwÙwÜwßwâwåwèwëwîwñwôw÷wúwýxxxx x xxxxxxx!x$x'x*x-x0x3x6x9x<x?xBxExHxKxNxQxTxWxZx]x`xcxfxixlxoxrxuxxx{x~xx„x‡xŠxxx“x–x™xœxŸx¢x¥x¨x«x®x±x´x·xºx½xÀxÃxÆxÉxÌxÏxÒxÕxØxÛxÞxáxäxçxêxíxðxóxöxùxüxÿyyy yy1y:y<yTy]y_yyy‚y„yªy³yµyÓyÜyÞzzzzBzKzMzsz|z~z²z»z½zëzôzö{{{{9{B{D{l{u{w{˜{¡{£{Á{Ê{Ì{þ|| |3|<|>||Š|Œ|½|Æ|È|ý}}}+}4}6}^}g}i}}–}˜}Ç}Ð}Ò}ù~~~*~3~5~O~X~Z~y~‚~„~£~¬~®~Ì~Õ~×  1:<_hj“œž»ÄÆãì€€1€:€<€X€a€c€‹€”€–€¾€Ç€É€ù )+R[]xƒ¡ª¬ÑÚÜ‚‚ ‚‚,‚5‚7‚V‚_‚a‚‚ˆ‚Š‚«‚´‚¶‚Ђقۃƒ ƒƒ0ƒ9ƒ;ƒ[ƒdƒfƒƒ™ƒ›ƒÊƒÓƒÕƒ÷„„„&„/„1„S„\„^„t„}„„˜„¡„£„Ì„Õ„×„ï„ø„ú…………;…D…F…`…i…k…ˆ…‘…“…»…Ä…Æ…ß…è…ê† †††P†Y†[†‚†‹††²†»†½†â†ë†í‡‡‡‡=‡F‡H‡b‡k‡m‡‘‡š‡œ‡¸‡Á‡Ã‡ã‡ì‡îˆˆˆˆ9ˆBˆDˆeˆnˆpˆ˜ˆ¡ˆ£ˆÔˆÝˆßˆü‰‰‰*‰3‰5‰U‰^‰`‰Ž‰—‰™‰½‰Æ‰È‰è‰ñ‰óŠŠŠŠLŠUŠWŠ}Š†ŠˆŠ¹ŠÂŠÄŠêŠóŠõ‹‹‹‹@‹I‹K‹t‹}‹‹©‹²‹´‹Ù‹â‹äŒ ŒŒŒ9ŒBŒDŒ_ŒhŒjŒ‘ŒšŒœŒÅŒÎŒÐŒû&/1[dfŽ—™¾ÇÉïøúŽŽ%Ž'ŽfŽoŽqް޹޻ŽÚŽãŽå09;py{¢«­Î×Ù /8:]fh‰’”¸ÁÃõþ‘‘.‘7‘9‘j‘s‘u‘·‘À‘Â’’ ’’N’W’Y’‘’š’œ’ß’è’ê“/“8“:“q“z“|“¿“ȓʔ”””\”e”g””””Ÿ”Õ”Þ”à••"•$•h•q•s•¥•®•°•ë•ô•ö–5–>–@–u–~–€–Ö̖Η———O—X—Z—“—œ—ž—Õ—Þ—à˜˜˜˜T˜]˜_˜Ž˜—˜™˜É˜Ò˜Ô™™™™N™W™Y™ž™§™©™î™÷™ùš3š<š>šmšvšxš²š»š½šôšýšÿ›,›5›7›w›€›‚›Á›Ê›Ìœœ œ œIœRœTœ‹œ”œ–œóœüœþCLNŠ“•ÓÜÞž$ž-ž/žrž{ž}ž¾žÇžÉŸŸ Ÿ ŸHŸQŸSŸ§Ÿ°Ÿ²ŸõŸþ  7 @ B „   Ý æ è¡9¡B¡D¡“¡œ¡ž¡á¡ê¡ì¢;¢D¢F¢ƒ¢Œ¢Ž¢Ö¢ß¢á£-£6£8£†££‘£Ù£â£ä¤+¤4¤6¤‡¤¤’¤é¤ò¤ô¥J¥S¥U¥¥¦¥¨¥ò¥û¥ý¦T¦]¦_¦®¦·¦¹§§§§T§]§_§®§·§¹§ü¨¨¨^¨g¨i¨Í¨Ö¨Ø©8©A©C©“©œ©ž©è©ñ©óª9ªBªDªª–ª˜ªÞªçªé«,«5«7«~«‡«‰«Ì«Õ«×¬¬&¬(¬m¬v¬x¬»¬Ä¬Æ­ ­­­_­h­j­©­²­´®® ® ®Y®b®d®º®Ã®Å¯¯$¯&¯t¯}¯¯Õ¯Þ¯à°*°3°5°}°†°ˆ°Î°×°Ù±4±=±?±—± ±¢±ú²²²[²d²f²š²£²¥²Û²ä²æ³%³.³0³‚³‹³³Í³Ö³Ø´´(´*´o´x´z´±´º´¼µµ µ µTµ]µ_µ±µºµ¼¶¶¶¶\¶e¶g¶¯¶¸¶º····`·i·k···À·Â¸¸&¸(¸~¸‡¸‰¸Ü¸å¸ç¹7¹@¹B¹”¹¹Ÿ¹ð¹ý¹þ¹ÿºººººººº(º*º-º0º3º5ºQºZº]º`ºcºtºwºzº|ººˆº•ºœºžº º£ºªº­º¯º²º´ºÙºõºþ»»»»»!»#»%»'»)»+»-»:»=»@»C»E»G»J»L»U½a½d½g½j½m½p½s½v½y½|½½‚½…½ˆ½‹½Ž½‘½”½—½š½½ ½£½¦½©½¬½¯½²½µ½¸½»½¾½Á½Ä½Ç½Ê½Í½Ð½Ó½Ö½Ù½Ü½ß½â½å½è½ë½î½ñ½ô½÷½ú½ý¾¾¾¾ ¾ ¾¾¾¾¾¾¾!¾$¾'¾*¾-¾0¾3¾6¾9¾<¾?¾B¾E¾H¾K¾N¾Q¾T¾W¾Z¾]¾`¾c¾f¾i¾l¾o¾r¾u¾x¾{¾~¾¾„¾‡¾Š¾¾¾“¾–¾™¾œ¾Ÿ¾¢¾¥¾¨¾«¾®¾±¾´¾·¾º¾½¾À¾Ã¾Æ¾É¾Ì¾Ï¾Ò¾Õ¾Ø¾Û¾Þ¾á¾ä¾ç¾ê¾í¾ð¾ó¾ö¾ù¾ü¾ÿ¿¿¿¿ ¿¿¿¿¿¿¿ ¿#¿&¿)¿,¿/¿2¿5¿8¿;¿>¿A¿D¿G¿J¿M¿P¿S¿V¿Y¿\¿_¿b¿e¿h¿k¿n¿q¿t¿w¿z¿}¿€¿ƒ¿†¿‰¿Œ¿¿’¿•¿˜¿›¿ž¿¡¿¤¿§¿ª¿­¿°¿³¿¶¿¹¿¼¿¿¿Â¿Å¿È¿Ë¿Î¿Ñ¿Ô¿×¿Ú¿Ý¿à¿ã¿æ¿é¿ì¿ï¿ò¿õ¿ø¿û¿þÀÀÀÀ À ÀÀÀÀÀÀÀ"À%À(À+À.À1À4À7À:À=À@ÀCÀFÀIÀLÀOÀRÀUÀXÀ[À^ÀaÀdÀgÀjÀmÀoÀxÀzÀœÀ¥À§À¿ÀÈÀÊÀäÀíÀïÁÁÁ Á>ÁGÁIÁsÁ|Á~Á­Á¶Á¸ÁÞÁçÁéÂÂ&Â(ÂVÂ_ÂaÂ}†ˆ¤­¯Â×ÂàÂâÃà ÃÃ,Ã5Ã7ÃiÃrÃtÞçéÃìÃõÃ÷Ä(Ä1Ä3ÄhÄqÄsÄ–ÄŸÄ¡ÄÉÄÒÄÔÄøÅÅÅ2Å;Å=ÅdÅmÅoઊźÅÃÅÅÅäÅíÅïÆÆÆÆ7Æ@ÆBÆmÆvÆxƜƥƧÆÊÆÓÆÕÆþÇÇ Ç&Ç/Ç1ÇNÇWÇYÇzǃDžǜǥǧÇÃÇÌÇÎÇöÇÿÈÈ)È2È4ÈdÈmÈoÈ‹È”È–È½ÈÆÈÈÈãÈìÈîÉ ÉÉÉ<ÉEÉGÉnÉwÉyɗɠɢÉÁÉÊÉÌÉêÉóÉõÊÊÊ!Ê;ÊDÊFÊnÊwÊyÊ›Ê¤Ê¦ÊÆÊÏÊÑÊûËËË5Ë>Ë@ËbËkËmˑ˚˜˾ËÇËÉËßËèËêÌÌ ÌÌ7Ì@ÌBÌZÌcÌeÌ|̦̯̱̅̇ÌËÌÔÌÖÌóÌüÌþÍ&Í/Í1ÍJÍSÍUÍvÍÍÍ»ÍÄÍÆÍíÍöÍøÎÎ&Î(ÎMÎVÎXÎ|΅·ΨαγÎÍÎÖÎØÎüÏÏÏ#Ï,Ï.ÏNÏWÏYÏ}φψϤϭϯÏÐÏÙÏÛÐÐ ÐÐ?ÐHÐJÐgÐpÐrЕОРÐÀÐÉÐËÐùÑÑÑ(Ñ1Ñ3ÑSÑ\Ñ^Ñ{фцѷÑÀÑÂÑèÑñÑóÒ$Ò-Ò/ÒUÒ^Ò`Ò}Ò†ÒˆÒ«Ò´Ò¶ÒßÒèÒêÓÓÓÓDÓMÓOÓwÓ€Ó‚Ó¤Ó­Ó¯ÓÊÓÓÓÕÓüÔÔÔ0Ô9Ô;ÔfÔoÔqÔ‘ÔšÔœÔÆÔÏÔÑÔùÕÕÕ)Õ2Õ4ÕZÕcÕeÕ‡ÕÕ’ÕÑÕÚÕÜÖÖ$Ö&ÖEÖNÖPÖzÖƒÖ…Ö›Ö¤Ö¦ÖÛÖäÖæ× ×××9×B×D×l×u×wךףץ×È×Ñ×Ó×ô×ý×ÿØ#Ø,Ø.Ø`ØiØkؙآؤØÕØÞØàÙ"Ù+Ù-ÙoÙxÙzÙ¹ÙÂÙÄÙüÚÚÚJÚSÚUÚšÚ£Ú¥ÚÜÚåÚçÛ*Û3Û5ÛzÛƒÛ…ÛÇÛÐÛÒÛÿÜÜ Ü@ÜIÜKÜ„ÜÜÜÓÜÜÜÞÝÝÝÝVÝ_ÝaݠݩݫÝàÝéÝëÞ.Þ7Þ9ÞpÞyÞ{ÞºÞÃÞÅÞþßß ß@ßIßKß߈ߊ߿ßÈßÊßùààà4à=à?à|à…à‡à¹àÂàÄá áááYábádážá§á©áØáááãââ&â(â_âhâjâ—â â¢âââëâíã,ã5ã7ãlãuãwã´ã½ã¿ãöãÿää^ägäiä®ä·ä¹äõäþåå>åGåIåå˜åšåÝåæåèæ)æ2æ4ækætævæ³æ¼æ¾çççç`çiçkç¢ç«ç­çïçøçúèHèQèSè¤è­è¯èþéé éLéUéWé¦é¯é±éîé÷éùêAêJêLê˜ê¡ê£êñêúêüëDëMëOë–ëŸë¡ëòëûëýìTì]ì_ìµì¾ìÀíííí]ífíhí¿íÈíÊîî"î$îpîyî{î¿îÈîÊïï"ï$ïgïpïrïÉïÒïÔð8ðAðCð£ð¬ð®ðþññ ñSñ\ñ^ñ¤ñ­ñ¯ñøòòòIòRòTò—ò ò¢òéòòòôó7ó@óBóˆó‘ó“óØóáóãô&ô/ô1ôvôôôÊôÓôÕõõõõkõtõvõÄõÍõÏö%ö.ö0ö†öö‘ößöèöê÷@÷I÷K÷•÷ž÷ ÷è÷ñ÷óø9øBøDøŸø¨øªùù ù ùeùnùpùÆùÏùÑúúúúFúOúQúú™ú›úíúöúøû8ûAûCûŠû“û•ûÚûãûåüü%ü'ülüuüwü¿üÈüÊýý%ý'ýqýzý|ýÇýÐýÒþþ#þ%þpþyþ{þËþÔþÖÿ"ÿ+ÿ-ÿˆÿ‘ÿ“ÿéÿòÿôGPR¢«­ÿ [dgjluwŸ¬±´·¼¿ÂÄÑÓÖÙJWY\_½ÊÑÔ×Úáäçêì .QZ]`cx†‰ŒŽ—˜š£¦©«¿Öãêíðóúý&Gjsx{~€‰ŠŒ•˜›²ÉÖÙÜßâäòÿ  #&),/25FIKMPSVY[]gr‹¢·Ìäý .56ERUX[^`moru‚‡Š’•˜š §°¹ÆÍÐÓÖÝàãæè6OXcfilortˆ‹Ž‘˜›ž¡£Ìú&/6=JQTWZadgjluw}Š‘”—𡤧ª¬µ¸ÅÒÙÜßâéìïòôý    z } † ” › ¢ ¯ ¼ ¿ Â Å È Ë Î Û Þ á ä ç ê í ï  9 c  – ž § ª ³ º ¿ Ò Ô Ý à â ä í ö ù ü ÿ       Š œ ¡ §Š ©pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/000077500000000000000000000000001445420033100227515ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/000077500000000000000000000000001445420033100263775ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcdebugger/000077500000000000000000000000001445420033100305165ustar00rootroot00000000000000Breakpoints.xcbkptlist000066400000000000000000000075111445420033100350350ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcdebugger pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/000077500000000000000000000000001445420033100303615ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes/PcmMsr.xcscheme000066400000000000000000000037251445420033100333120ustar00rootroot00000000000000 PcmMsrLibrary.xcscheme000066400000000000000000000037241445420033100345570ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes xcschememanagement.plist000066400000000000000000000013711445420033100352150ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/aiott.xcuserdatad/xcschemes SchemeUserState PcmMsr.xcscheme orderHint 0 PcmMsrLibrary.xcscheme orderHint 1 SuppressBuildableAutocreation 81ADBF11156EEB93006D9B47 primary 81DEAF55157008B7005E8EC6 primary 81F91BBB156D9BF8007DD788 primary pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/pjkerly.xcuserdatad/000077500000000000000000000000001445420033100267375ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/pjkerly.xcuserdatad/xcschemes/000077500000000000000000000000001445420033100307215ustar00rootroot00000000000000PcmMsrDriver.xcscheme000066400000000000000000000054561445420033100347520ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/pjkerly.xcuserdatad/xcschemes PcmMsrLibrary.xcscheme000066400000000000000000000054531445420033100351200ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/pjkerly.xcuserdatad/xcschemes xcschememanagement.plist000066400000000000000000000012451445420033100355550ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr.xcodeproj/xcuserdata/pjkerly.xcuserdatad/xcschemes SchemeUserState PcmMsrDriver.xcscheme orderHint 0 PcmMsrLibrary.xcscheme orderHint 1 SuppressBuildableAutocreation 81ADBF11156EEB93006D9B47 primary 81F91BBB156D9BF8007DD788 primary pcm-202307/src/MacMSRDriver/PcmMsr/000077500000000000000000000000001445420033100166125ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr/CMakeLists.txt000066400000000000000000000023411445420033100213520ustar00rootroot00000000000000message(STATUS ${IOKIT_LIBRARY}) add_executable( PcmMsrDriver MACOSX_BUNDLE PcmMsr.cpp PcmMSrClient.cpp PcmMsrDriver_info.c PcmMsr-Info.plist ) set_target_properties(PcmMsrDriver PROPERTIES BUNDLE_EXTENSION kext MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/PcmMsr-Info.plist) target_include_directories(PcmMsrDriver PRIVATE ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/Kernel.framework/PrivateHeaders ${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/Kernel.framework/Headers ) target_compile_definitions(PcmMsrDriver PRIVATE -DKERNEL -DKERNEL_PRIVATE -DDRIVER_PRIVATE -DAPPLE -DNeXT ) target_compile_options(PcmMsrDriver PRIVATE "-ffreestanding" "$<$:-fapple-kext>" ) target_link_libraries(PcmMsrDriver PRIVATE "-lkmodc++" "-lkmod" "-lcc_kext" "-nostdlib" "-Xlinker -export_dynamic" "-Xlinker -kext" ) # PcmMsrDriver.kext is built here and located in 'build/bin' set(LIB_EXT_PATH "/Library/Extensions") install(TARGETS PcmMsrDriver DESTINATION "${LIB_EXT_PATH}/") install(CODE "execute_process(COMMAND kmutil load -b com.intel.driver.PcmMsr)") pcm-202307/src/MacMSRDriver/PcmMsr/PcmMsr-Info.plist000066400000000000000000000031311445420033100217570ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleExecutable PcmMsrDriver CFBundleIdentifier com.intel.driver.PcmMsr CFBundleInfoDictionaryVersion 6.0 CFBundleName PcmMsrDriver CFBundlePackageType KEXT CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 IOKitPersonalities PcmMsrClient CFBundleIdentifier com.intel.driver.PcmMsr IOClass com_intel_driver_PcmMsr IOMatchCategory com_intel_driver_PcmMsr IOProbeScore 1000 IOProviderClass IOResources IOResourceMatch IOKit IOUserClientClass com_intel_driver_PcmMsrClient OSBundleLibraries com.apple.kpi.bsd 10.9 com.apple.kpi.mach 10.9 com.apple.kpi.unsupported 10.9 com.apple.kpi.iokit 10.9 com.apple.kpi.libkern 10.9 pcm-202307/src/MacMSRDriver/PcmMsr/PcmMsr-Prefix.pch000066400000000000000000000001341445420033100217400ustar00rootroot00000000000000// // Prefix header for all source files of the 'PcmMsr' target in the 'PcmMsr' project // pcm-202307/src/MacMSRDriver/PcmMsr/PcmMsr.cpp000066400000000000000000000177301445420033100205270ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #include #include #include "PcmMsr.h" PcmMsrDriverClassName *g_pci_driver = NULL; #define wrmsr(msr,lo,hi) \ asm volatile ("wrmsr" : : "c" (msr), "a" (lo), "d" (hi)) #define rdmsr(msr,lo,hi) \ asm volatile ("\trdmsr\n" : "=a" (lo), "=d" (hi) : "c" (msr)) #define cpuid(func1, func2, a, b, c, d) \ asm volatile ("cpuid" : "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (func1), "c" (func2)); extern "C" { extern void mp_rendezvous_no_intrs(void (*func)(void *), void *arg); extern int cpu_number(void); } inline uint64_t RDMSR(uint32_t msr) { uint64_t value; uint32_t low, hi; rdmsr(msr, low, hi); value = ((uint64_t) hi << 32) | low; return value; } inline void WRMSR(uint32_t msr, uint64_t value) { uint32_t low, hi; low = (uint32_t)value; hi = (uint32_t) (value >> 32); wrmsr(msr, low, hi); } void cpuReadMSR(void* pIData){ pcm_msr_data_t* data = (pcm_msr_data_t*)pIData; int cpu = cpu_number(); if(data->cpu_num == cpu) { data->value = RDMSR(data->msr_num); } } void cpuWriteMSR(void* pIDatas){ pcm_msr_data_t* idatas = (pcm_msr_data_t*)pIDatas; int cpu = cpu_number(); if(idatas->cpu_num == cpu) { WRMSR(idatas->msr_num, idatas->value); } } void cpuGetTopoData(void* pTopos){ TopologyEntry* entries = (TopologyEntry*)pTopos; int cpu = cpu_number(); int info[4]; entries[cpu].os_id = cpu; cpuid(0xB, 1, info[0], info[1], info[2], info[3]); entries[cpu].socket = info[3] >> info[0] & 0xF; cpuid(0xB, 0, info[0], info[1], info[2], info[3]); entries[cpu].core_id = info[3] >> info[0] & 0xF; } OSDefineMetaClassAndStructors(com_intel_driver_PcmMsr, IOService) #define super IOService bool PcmMsrDriverClassName::start(IOService* provider){ bool success; success = super::start(provider); if (!g_pci_driver) { g_pci_driver = this; } if (success) { registerService(); } return success; } int32_t PcmMsrDriverClassName::getNumCores() { int32_t ncpus = 0; size_t ncpus_size = sizeof(ncpus); if(sysctlbyname("hw.logicalcpu", &ncpus, &ncpus_size, NULL, 0)) { IOLog("%s[%p]::%s() -- sysctl failure retrieving hw.logicalcpu", getName(), this, __FUNCTION__); ncpus = 0; } return ncpus; } bool PcmMsrDriverClassName::init(OSDictionary *dict) { bool result = super::init(dict); if (result) { num_cores = getNumCores(); } return result && num_cores; } void PcmMsrDriverClassName::free() { super::free(); } // We override handleOpen, handleIsOpen, and handleClose to allow multiple clients to access the driver // simultaneously. We always return true for these because we don't care who is accessing and we // don't know how many people will be accessing it. bool PcmMsrDriverClassName::handleOpen(IOService * forClient, IOOptionBits opts, void* args){ return true; } bool PcmMsrDriverClassName::handleIsOpen(const IOService* forClient) const{ return true; } void PcmMsrDriverClassName::handleClose(IOService* forClient, IOOptionBits opts){ } IOReturn PcmMsrDriverClassName::readMSR(pcm_msr_data_t* idatas,pcm_msr_data_t* odatas){ // All the msr_nums should be the same, so we just use the first one to pass to all cores IOReturn ret = kIOReturnBadArgument; if(idatas->cpu_num < num_cores) { mp_rendezvous_no_intrs(cpuReadMSR, (void*)idatas); odatas->cpu_num = idatas->cpu_num; odatas->msr_num = idatas->msr_num; odatas->value = idatas->value; ret = kIOReturnSuccess; } else { IOLog("Tried to read from a core with id higher than max core id.\n"); } return ret; } IOReturn PcmMsrDriverClassName::writeMSR(pcm_msr_data_t* idata){ IOReturn ret = kIOReturnBadArgument; if(idata->cpu_num < num_cores) { mp_rendezvous_no_intrs(cpuWriteMSR, (void*)idata); ret = kIOReturnSuccess; } else { IOLog("Tried to write to a core with id higher than max core id.\n"); } return ret; } IOReturn PcmMsrDriverClassName::buildTopology(TopologyEntry* odata, uint32_t input_num_cores) { size_t topologyBufferSize; // TODO figure out when input_num_cores is used rather than num_cores if (os_mul_overflow(sizeof(TopologyEntry), (size_t) num_cores, &topologyBufferSize)) { return kIOReturnBadArgument; } TopologyEntry *topologies = (TopologyEntry *)IOMallocAligned(topologyBufferSize, 32); if (topologies == nullptr) { return kIOReturnNoMemory; } mp_rendezvous_no_intrs(cpuGetTopoData, (void*)topologies); for(uint32_t i = 0; i < num_cores && i < input_num_cores; i++) { odata[i].core_id = topologies[i].core_id; odata[i].os_id = topologies[i].os_id; odata[i].socket = topologies[i].socket; } IOFreeAligned(topologies, topologyBufferSize); return kIOReturnSuccess; } IOReturn PcmMsrDriverClassName::getNumInstances(uint32_t* num_insts){ *num_insts = num_clients; return kIOReturnSuccess; } IOReturn PcmMsrDriverClassName::incrementNumInstances(uint32_t* num_insts){ *num_insts = ++num_clients; return kIOReturnSuccess; } IOReturn PcmMsrDriverClassName::decrementNumInstances(uint32_t* num_insts){ *num_insts = --num_clients; return kIOReturnSuccess; } // read uint32_t PcmMsrDriverClassName::read(uint32_t pci_address) { uint32_t value = 0; __asm__("\t" "movw $0xCF8,%%dx\n\t" "andb $0xFC,%%al\n\t" "outl %%eax,%%dx\n\t" "movl $0xCFC,%%edx\n\t" "in %%dx,%%eax\n" : "=a"(value) : "a"(pci_address) : "%edx"); return value; } // write void PcmMsrDriverClassName::write(uint32_t pci_address, uint32_t value) { __asm__("\t" "movw $0xCF8,%%dx\n\t" "andb $0xFC,%%al\n\t" "outl %%eax,%%dx\n\t" "movl $0xCFC,%%edx\n\t" "movl %%ebx,%%eax\n\t" "outl %%eax,%%dx\n" : : "a"(pci_address), "b"(value) : "%edx"); } // mapMemory void* PcmMsrDriverClassName::mapMemory (uint32_t address, UInt8 **virtual_address) { PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); IOMemoryMap *memory_map = NULL; IOMemoryDescriptor *memory_descriptor = NULL; #ifndef __clang_analyzer__ // address a false-positive memory_descriptor = IOMemoryDescriptor::withPhysicalAddress(address, 4096, kIODirectionInOut); #endif if (memory_descriptor) { IOReturn ioErr = memory_descriptor->prepare(kIODirectionInOut); if (ioErr == kIOReturnSuccess) { memory_map = memory_descriptor->map(); if (memory_map) { if (virtual_address) { *virtual_address = (UInt8*)memory_map->getVirtualAddress(); } else { IOLog("%s[%p]::%s() -- virtual_address is null\n", getName(), this, __FUNCTION__); } } else { IOLog("%s[%p]::%s() -- IOMemoryDescriptor::map() failure\n", getName(), this, __FUNCTION__); } } else { IOLog("%s[%p]::%s() -- IOMemoryDescriptor::prepare() failure\n", getName(), this, __FUNCTION__); } if (!memory_map) { memory_descriptor->release(); } } else { IOLog("%s[%p]::%s() -- IOMemoryDescriptor::withPhysicalAddress() failure\n", getName(), this, __FUNCTION__); } return (void*)memory_map; } // unmapMemory void PcmMsrDriverClassName::unmapMemory (void *memory_map) { PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); IOMemoryMap *m_map = (IOMemoryMap*)memory_map; if (m_map) { m_map->getMemoryDescriptor()->complete(); #ifndef __clang_analyzer__ // address a false-positive m_map->getMemoryDescriptor()->release(); #endif m_map->unmap(); m_map->release(); } return; } pcm-202307/src/MacMSRDriver/PcmMsr/PcmMsr.h000066400000000000000000000031101445420033100201570ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #include #include "UserKernelShared.h" class PcmMsrDriverClassName : public IOService { OSDeclareDefaultStructors(com_intel_driver_PcmMsr) public: // IOService methods virtual bool start(IOService* provider) override; virtual IOReturn writeMSR(pcm_msr_data_t* data); virtual IOReturn readMSR(pcm_msr_data_t* idata,pcm_msr_data_t* odata); virtual IOReturn buildTopology(TopologyEntry* odata, uint32_t input_num_cores); virtual bool init(OSDictionary *dict) override; virtual void free(void) override; virtual bool handleOpen(IOService* forClient, IOOptionBits opts, void* args) override; virtual bool handleIsOpen(const IOService* forClient) const override; virtual void handleClose(IOService* forClient, IOOptionBits opts) override; virtual int32_t getNumCores(); virtual IOReturn incrementNumInstances(uint32_t* num_instances); virtual IOReturn decrementNumInstances(uint32_t* num_instances); virtual IOReturn getNumInstances(uint32_t* num_instances); // PCI classes static uint32_t read(uint32_t pci_address); static void write(uint32_t pci_address, uint32_t value); void* mapMemory(uint32_t address, UInt8 **virtual_address); void unmapMemory(void* memory_map); private: // number of providers currently using the driver uint32_t num_clients = 0; int32_t num_cores; }; #ifdef DEBUG #define _DEBUG 1 #else #define _DEBUG 0 #endif #define PRINT_DEBUG if (_DEBUG) IOLog pcm-202307/src/MacMSRDriver/PcmMsr/PcmMsrClient.cpp000066400000000000000000000254511445420033100216650ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #include #include #include #include "PcmMsrClient.h" #define super IOUserClient OSDefineMetaClassAndStructors(com_intel_driver_PcmMsrClient, IOUserClient) const IOExternalMethodDispatch PcmMsrClientClassName::sMethods[kNumberOfMethods] = { { (IOExternalMethodAction) &PcmMsrClientClassName::sOpenDriver, 0, 0, 0, 0}, { (IOExternalMethodAction) &PcmMsrClientClassName::sCloseDriver, 0, 0, 0, 0}, { (IOExternalMethodAction) &PcmMsrClientClassName::sReadMSR, 0, kIOUCVariableStructureSize, 0, kIOUCVariableStructureSize}, { (IOExternalMethodAction) &PcmMsrClientClassName::sWriteMSR, 0, kIOUCVariableStructureSize, 0, 0}, { (IOExternalMethodAction) &PcmMsrClientClassName::sBuildTopology, 0, 0, 0, kIOUCVariableStructureSize}, { (IOExternalMethodAction) &PcmMsrClientClassName::sGetNumInstances, 0, 0, 1, 0}, { (IOExternalMethodAction) &PcmMsrClientClassName::sIncrementNumInstances, 0, 0, 1, 0}, { (IOExternalMethodAction) &PcmMsrClientClassName::sDecrementNumInstances, 0, 0, 1, 0}, { (IOExternalMethodAction) &PcmMsrClientClassName::sRead, 1, 0, 1, 0 }, { (IOExternalMethodAction) &PcmMsrClientClassName::sWrite, 2, 0, 0, 0 }, { (IOExternalMethodAction) &PcmMsrClientClassName::sMapMemory, 1, 0, 2, 0 }, { (IOExternalMethodAction) &PcmMsrClientClassName::sUnmapMemory, 1, 0, 0, 0 }, { (IOExternalMethodAction) &PcmMsrClientClassName::sReadMemory, 1, 0, 1, 0 } }; IOReturn PcmMsrClientClassName::externalMethod(uint32_t selector, IOExternalMethodArguments* args, IOExternalMethodDispatch* dispatch, OSObject* target, void* reference) { if (selector < (uint32_t) kNumberOfMethods) { dispatch = (IOExternalMethodDispatch *) &sMethods[selector]; if (!target) { target = this; } } return super::externalMethod(selector, args, dispatch, target, reference); } bool PcmMsrClientClassName::start(IOService* provider) { bool result = false; fProvider = OSDynamicCast(PcmMsrDriverClassName, provider); if (fProvider != NULL) { result = super::start(provider); } else IOLog("PcmMsrClientClassName::start failed.\n"); return result; } IOReturn PcmMsrClientClassName::clientClose(void) { closeUserClient(); if (!terminate()) { IOLog("PcmMsrClientClassName::clientClose failed.\n"); } return kIOReturnSuccess; } bool PcmMsrClientClassName::didTerminate(IOService* provider, IOOptionBits options, bool* defer) { closeUserClient(); *defer = false; return super::didTerminate(provider, options, defer); } IOReturn PcmMsrClientClassName::sOpenDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { return target->openUserClient(); } IOReturn PcmMsrClientClassName::openUserClient(void) { IOReturn result = kIOReturnSuccess; if (fProvider == NULL || isInactive()) { result = kIOReturnNotAttached; IOLog("%s::%s returned kIOReturnNotAttached.\n", getName(), __FUNCTION__); } else if (!fProvider->open(this)) { result = kIOReturnExclusiveAccess; IOLog("%s::%s returned kIOReturnExclusiveAccess.\n", getName(), __FUNCTION__); } return result; } IOReturn PcmMsrClientClassName::checkActiveAndOpened (const char* memberFunction) { if (fProvider == NULL || isInactive()) { IOLog("%s::%s returned kIOReturnNotAttached.\n", getName(), memberFunction); return (IOReturn)kIOReturnNotAttached; } else if (!fProvider->isOpen(this)) { IOLog("%s::%s returned kIOReturnNotOpen.\n", getName(), memberFunction); return (IOReturn)kIOReturnNotOpen; } return kIOReturnSuccess; } IOReturn PcmMsrClientClassName::sCloseDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { return target->closeUserClient(); } IOReturn PcmMsrClientClassName::closeUserClient(void) { IOReturn result = checkActiveAndOpened (__FUNCTION__); if (result == kIOReturnSuccess) fProvider->close(this); return result; } IOReturn PcmMsrClientClassName::sReadMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments){ return target->readMSR((pcm_msr_data_t*) arguments->structureInput, (pcm_msr_data_t*) arguments->structureOutput); } IOReturn PcmMsrClientClassName::readMSR(pcm_msr_data_t* idata, pcm_msr_data_t* odata) { IOReturn result = checkActiveAndOpened (__FUNCTION__); if (result == kIOReturnSuccess) result = fProvider->readMSR(idata, odata); return result; } IOReturn PcmMsrClientClassName::sWriteMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments){ return target -> writeMSR((pcm_msr_data_t*)arguments->structureInput); } IOReturn PcmMsrClientClassName::writeMSR(pcm_msr_data_t* data) { IOReturn result = checkActiveAndOpened (__FUNCTION__); if (result == kIOReturnSuccess) result = fProvider->writeMSR(data); return result; } IOReturn PcmMsrClientClassName::sBuildTopology(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ return target -> buildTopology((TopologyEntry*)args->structureOutput, args->structureOutputSize); } IOReturn PcmMsrClientClassName::buildTopology(TopologyEntry* data, size_t output_size) { uint32_t num_cores = (uint32_t) (output_size / sizeof(TopologyEntry) ); IOReturn result = checkActiveAndOpened (__FUNCTION__); if (result == kIOReturnSuccess) result = fProvider->buildTopology(data, num_cores); return result; } IOReturn PcmMsrClientClassName::sGetNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ return target->getNumInstances((uint32_t*)&args->scalarOutput[0]); } IOReturn PcmMsrClientClassName::getNumInstances(uint32_t* num_insts){ return fProvider->getNumInstances(num_insts); } IOReturn PcmMsrClientClassName::sIncrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ return target->incrementNumInstances((uint32_t*)&args->scalarOutput[0]); } IOReturn PcmMsrClientClassName::incrementNumInstances(uint32_t* num_insts){ return fProvider->incrementNumInstances(num_insts); } IOReturn PcmMsrClientClassName::sDecrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args){ return target->decrementNumInstances((uint32_t*)&args->scalarOutput[0]); } IOReturn PcmMsrClientClassName::decrementNumInstances(uint32_t* num_insts){ return fProvider->decrementNumInstances(num_insts); } extern PcmMsrDriverClassName* g_pci_driver; // read32 IOReturn PcmMsrClientClassName::sRead(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { return target->read(arguments->scalarInput, arguments->scalarInputCount, arguments->scalarOutput, arguments->scalarOutputCount); } IOReturn PcmMsrClientClassName::read(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize) { PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); if (inputSize != 1) { IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); return kIOReturnBadArgument; } uint32_t addr = (uint32_t)input[0]; PRINT_DEBUG("addr: %x\n", addr); if (g_pci_driver) { output[0] = g_pci_driver->read(addr); } IOLog("val: %llx\n", output[0]); return kIOReturnSuccess; } // write32 IOReturn PcmMsrClientClassName::sWrite(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { return target->write(arguments->scalarInput, arguments->scalarInputCount); } IOReturn PcmMsrClientClassName::write(const uint64_t* input, uint32_t inputSize) { PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); if (inputSize != 2) { IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); return kIOReturnBadArgument; } uint32_t addr = (uint32_t)input[0]; uint32_t val = (uint32_t)input[1]; PRINT_DEBUG("addr: %x, val: %x\n", addr, val); if (g_pci_driver) { g_pci_driver->write(addr, val); } return kIOReturnSuccess; } // mapMemory IOReturn PcmMsrClientClassName::sMapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { return target->mapMemory(arguments->scalarInput, arguments->scalarInputCount, arguments->scalarOutput, arguments->scalarOutputCount); } IOReturn PcmMsrClientClassName::mapMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize) { PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); if (inputSize != 1) { IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); return kIOReturnBadArgument; } uint32_t address = (uint32_t)input[0]; PRINT_DEBUG("address: %x\n", address); if (g_pci_driver) { uint8_t* virtual_address = NULL; void* memory_map = g_pci_driver->mapMemory(address, (uint8_t**)&virtual_address); output[0] = (uint64_t)memory_map; output[1] = (uint64_t)virtual_address; PRINT_DEBUG("memory_map: %p\n", memory_map); PRINT_DEBUG("virtual_address: %p\n", virtual_address); } return kIOReturnSuccess; } // unmapMemory IOReturn PcmMsrClientClassName::sUnmapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { return target->unmapMemory(arguments->scalarInput, arguments->scalarInputCount); } IOReturn PcmMsrClientClassName::unmapMemory(const uint64_t* input, uint32_t inputSize) { PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); if (inputSize != 1) { IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); return kIOReturnBadArgument; } void* memory_map = (void*)input[0]; PRINT_DEBUG("memory_map: %p\n", memory_map); if (g_pci_driver) { g_pci_driver->unmapMemory(memory_map); } return kIOReturnSuccess; } // readMemory IOReturn PcmMsrClientClassName::sReadMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments) { return target->readMemory(arguments->scalarInput, arguments->scalarInputCount, arguments->scalarOutput, arguments->scalarOutputCount); } IOReturn PcmMsrClientClassName::readMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize) { PRINT_DEBUG("%s[%p]::%s()\n", getName(), this, __FUNCTION__); if (inputSize != 1) { IOLog("%s[%p]::%s(): returning kIOReturnBadArgument.\n", getName(), this, __FUNCTION__); return kIOReturnBadArgument; } uint8_t* address = (uint8_t*)input[0]; PRINT_DEBUG("address: %p\n", address); uint32_t val = 0; if (g_pci_driver) { val = *(uint32_t*)address; } output[0] = (uint64_t)val; PRINT_DEBUG("val: %x\n", val); return kIOReturnSuccess; } pcm-202307/src/MacMSRDriver/PcmMsr/PcmMsrClient.h000066400000000000000000000070171445420033100213300ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #include #include #include "PcmMsr.h" #define PcmMsrClientClassName com_intel_driver_PcmMsrClient class PcmMsrClientClassName : public IOUserClient { OSDeclareDefaultStructors(com_intel_driver_PcmMsrClient) protected: PcmMsrDriverClassName* fProvider; static const IOExternalMethodDispatch sMethods[kNumberOfMethods]; public: virtual bool start(IOService *provider) override; virtual IOReturn clientClose(void) override; virtual bool didTerminate(IOService* provider, IOOptionBits opts, bool* defer) override; protected: IOReturn checkActiveAndOpened (const char* memberFunction); virtual IOReturn externalMethod(uint32_t selector, IOExternalMethodArguments* arguments, IOExternalMethodDispatch* dispatch, OSObject* target, void* reference) override; static IOReturn sOpenDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn openUserClient(void); static IOReturn sCloseDriver(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn closeUserClient(void); static IOReturn sReadMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn readMSR(pcm_msr_data_t* idata, pcm_msr_data_t* odata); static IOReturn sWriteMSR(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn writeMSR(pcm_msr_data_t* data); static IOReturn sBuildTopology(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn buildTopology(TopologyEntry* data, size_t output_size); static IOReturn sGetNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn getNumInstances(uint32_t* num_insts); static IOReturn sIncrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn incrementNumInstances(uint32_t* num_insts); static IOReturn sDecrementNumInstances(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* args); virtual IOReturn decrementNumInstances(uint32_t* num_insts); // PCI functions static IOReturn sRead(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); virtual IOReturn read(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize); static IOReturn sWrite(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); virtual IOReturn write(const uint64_t* input, uint32_t inputSize); static IOReturn sMapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); virtual IOReturn mapMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize); static IOReturn sUnmapMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); virtual IOReturn unmapMemory(const uint64_t* input, uint32_t inputSize); static IOReturn sReadMemory(PcmMsrClientClassName* target, void* reference, IOExternalMethodArguments* arguments); virtual IOReturn readMemory(const uint64_t* input, uint32_t inputSize, uint64_t* output, uint32_t outputSize); }; pcm-202307/src/MacMSRDriver/PcmMsr/PcmMsrDriver_info.c000066400000000000000000000006471445420033100223550ustar00rootroot00000000000000#include extern kern_return_t _start(kmod_info_t *ki, void *data); extern kern_return_t _stop(kmod_info_t *ki, void *data); __attribute__((visibility("default"))) KMOD_EXPLICIT_DECL(com.intel.driver.PcmMsrDriver, "1.0.0d1", _start, _stop) __private_extern__ kmod_start_func_t *_realmain = 0; __private_extern__ kmod_stop_func_t *_antimain = 0; __private_extern__ int _kext_apple_cc = __APPLE_CC__ ; pcm-202307/src/MacMSRDriver/PcmMsr/UserKernelShared.h000066400000000000000000000020301445420033100221640ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, Intel Corporation // written by Austen Ott // #define PcmMsrDriverClassName com_intel_driver_PcmMsr #define kPcmMsrDriverClassName "com_intel_driver_PcmMsr" #ifndef USER_KERNEL_SHARED #define USER_KERNEL_SHARED #define PCM_API // kIOMainPortDefault is not supported before macOS Monterey #if (MAC_OS_X_VERSION_MAX_ALLOWED < 120000) #define kIOMainPortDefault kIOMasterPortDefault #endif #include #include "../../topologyentry.h" using namespace pcm; typedef struct { uint64_t value; uint32_t cpu_num; uint32_t msr_num; } pcm_msr_data_t; typedef struct { uint64_t value; uint32_t msr_num; bool mask; char padding[115]; } k_pcm_msr_data_t; enum { kOpenDriver, kCloseDriver, kReadMSR, kWriteMSR, kBuildTopology, kGetNumInstances, kIncrementNumInstances, kDecrementNumInstances, // PCI functions kRead, kWrite, kMapMemory, kUnmapMemory, kReadMemory, kNumberOfMethods }; #endif pcm-202307/src/MacMSRDriver/PcmMsr/en.lproj/000077500000000000000000000000001445420033100203415ustar00rootroot00000000000000pcm-202307/src/MacMSRDriver/PcmMsr/en.lproj/InfoPlist.strings000066400000000000000000000000551445420033100236630ustar00rootroot00000000000000/* Localized versions of Info.plist keys */ pcm-202307/src/MacMSRDriver/kextload.sh000066400000000000000000000002741445420033100175630ustar00rootroot00000000000000#!/usr/bin/env bash cp -R ../../build/bin/PcmMsrDriver.kext /Library/Extensions/. chown -R root:wheel /Library/Extensions/PcmMsrDriver.kext kextload /Library/Extensions/PcmMsrDriver.kext pcm-202307/src/MacMSRDriver/kextunload.sh000066400000000000000000000001631445420033100201230ustar00rootroot00000000000000#!/usr/bin/env bash kextunload /Library/Extensions/PcmMsrDriver.kext rm -rf /Library/Extensions/PcmMsrDriver.kext pcm-202307/src/PMURegisterDeclarations/000077500000000000000000000000001445420033100176525ustar00rootroot00000000000000pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-4F-1.json000066400000000000000000000136101445420033100237040ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "AnyThread": {"Config": 0, "Position": 21, "Width": 1}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "InTXCheckpointed": {"Config": 0, "Position": 33, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 2, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 6, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 10, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "TIDEnable": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "Filter0": {"Config": 1, "Position": 0, "Width": 64, "DefaultValue": 0}, "TID": {"Config": 1, "Position": 0, "Width": 6, "DefaultValue": 0}, "Filter1": {"Config": 2, "Position": 0, "Width": 64, "DefaultValue": 59} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "xpi" : { "__comment" : "this is for UPI LL and QPI LL uncore PMUs", "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-55-4.json000066400000000000000000000161661445420033100237000ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "AnyThread": {"Config": 0, "Position": 21, "Width": 1}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "InTXCheckpointed": {"Config": 0, "Position": 33, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 2, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 6, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 10, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "TIDEnable": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "Filter0": {"Config": 1, "Position": 0, "Width": 64, "DefaultValue": 0}, "TID": {"Config": 1, "Position": 0, "Width": 9, "DefaultValue": 0}, "Filter1": {"Config": 2, "Position": 0, "Width": 64, "DefaultValue": 59} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "xpi" : { "__comment" : "this is for UPI LL and QPI LL uncore PMUs", "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "m2m" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "m3upi" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "iio" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 12, "DefaultValue": 0}, "PortMask": {"Config": 0, "Position": 36, "Width": 8}, "FCMask": {"Config": 0, "Position": 44, "Width": 3} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-55-7.json000066400000000000000000000161661445420033100237030ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "AnyThread": {"Config": 0, "Position": 21, "Width": 1}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "InTXCheckpointed": {"Config": 0, "Position": 33, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 2, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 6, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 10, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "TIDEnable": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "Filter0": {"Config": 1, "Position": 0, "Width": 64, "DefaultValue": 0}, "TID": {"Config": 1, "Position": 0, "Width": 9, "DefaultValue": 0}, "Filter1": {"Config": 2, "Position": 0, "Width": 64, "DefaultValue": 59} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "xpi" : { "__comment" : "this is for UPI LL and QPI LL uncore PMUs", "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "m2m" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "m3upi" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "iio" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 12, "DefaultValue": 0}, "PortMask": {"Config": 0, "Position": 36, "Width": 8}, "FCMask": {"Config": 0, "Position": 44, "Width": 3} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-55-B.json000066400000000000000000000161661445420033100237160ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "AnyThread": {"Config": 0, "Position": 21, "Width": 1}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "InTXCheckpointed": {"Config": 0, "Position": 33, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 2, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 6, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 10, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "TIDEnable": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "Filter0": {"Config": 1, "Position": 0, "Width": 64, "DefaultValue": 0}, "TID": {"Config": 1, "Position": 0, "Width": 9, "DefaultValue": 0}, "Filter1": {"Config": 2, "Position": 0, "Width": 64, "DefaultValue": 59} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "xpi" : { "__comment" : "this is for UPI LL and QPI LL uncore PMUs", "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "m2m" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "m3upi" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "iio" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 12, "DefaultValue": 0}, "PortMask": {"Config": 0, "Position": 36, "Width": 8}, "FCMask": {"Config": 0, "Position": 44, "Width": 3} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-6A-6.json000066400000000000000000000202431445420033100237060ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "InTXCheckpointed": {"Config": 0, "Position": 33, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed3" : { "OS": {"Config": 0, "Position": 12, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 13, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 15, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "PerfMetrics": {"Config": 2, "Position": 0, "Width": 1, "DefaultValue": 0, "__comment": "fake field to tell the collector to also print the L1 top-down metrics, not just raw slots count"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "TIDEnable": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 26}, "TID": {"Config": 1, "Position": 0, "Width": 9, "DefaultValue": 0} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "xpi" : { "__comment" : "this is for UPI LL and QPI LL uncore PMUs", "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 24} } }, "m2m" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 8} } }, "m3upi" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "pcu" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0} } }, "iio" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 12, "DefaultValue": 0}, "PortMask": {"Config": 0, "Position": 36, "Width": 12}, "FCMask": {"Config": 0, "Position": 48, "Width": 3} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-86-5.json000066400000000000000000000136761445420033100237100ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 26} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "m2m" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 8} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "pcu" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0} } }, "iio" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 12, "DefaultValue": 0}, "PortMask": {"Config": 0, "Position": 36, "Width": 12}, "FCMask": {"Config": 0, "Position": 48, "Width": 3} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-8E-C.json000066400000000000000000000077011445420033100237350ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "AnyThread": {"Config": 0, "Position": 21, "Width": 1}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 2, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 6, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "AnyThread": {"Config": 0, "Position": 10, "Width": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-8F-6.json000066400000000000000000000210311445420033100237110ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "InTXCheckpointed": {"Config": 0, "Position": 33, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed3" : { "OS": {"Config": 0, "Position": 12, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 13, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 15, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "PerfMetrics": {"Config": 2, "Position": 0, "Width": 1, "DefaultValue": 0, "__comment": "fake field to tell the collector to also print the L1 top-down metrics, not just raw slots count"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "TIDEnable": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 0}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 26}, "TID": {"Config": 1, "Position": 0, "Width": 10, "DefaultValue": 0} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "xpi" : { "__comment" : "this is for UPI LL and QPI LL uncore PMUs", "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 24} } }, "m2m" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 8} } }, "m3upi" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "mdf" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "pcu" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0} } }, "iio" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 12, "DefaultValue": 0}, "PortMask": {"Config": 0, "Position": 36, "Width": 12}, "FCMask": {"Config": 0, "Position": 48, "Width": 3} } } } pcm-202307/src/PMURegisterDeclarations/GenuineIntel-6-8F-8.json000066400000000000000000000210311445420033100237130ustar00rootroot00000000000000{ "core" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "User": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 1}, "OS": {"Config": 0, "Position": 17, "Width": 1, "DefaultValue": 1}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1}, "PinControl": {"Config": 0, "Position": 19, "Width": 1, "DefaultValue": 0}, "APICInt": {"Config": 0, "Position": 20, "Width": 1, "DefaultValue": 0}, "Enable": {"Config": 0, "Position": 22, "Width": 1, "DefaultValue": 1}, "Invert": {"Config": 0, "Position": 23, "Width": 1}, "CounterMask": {"Config": 0, "Position": 24, "Width": 8}, "InTX": {"Config": 0, "Position": 32, "Width": 1, "DefaultValue": 0}, "InTXCheckpointed": {"Config": 0, "Position": 33, "Width": 1, "DefaultValue": 0}, "MSRIndex": { "0x1a6" : {"Config": 1, "Position": 0, "Width": 64}, "0x1a7" : {"Config": 2, "Position": 0, "Width": 64}, "0x3f6" : {"Config": 3, "Position": 0, "Width": 64}, "0x3f7" : {"Config": 4, "Position": 0, "Width": 64} } }, "fixed0" : { "OS": {"Config": 0, "Position": 0, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 1, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 3, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed1" : { "OS": {"Config": 0, "Position": 4, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 5, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 7, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed2" : { "OS": {"Config": 0, "Position": 8, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 9, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 11, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"} }, "fixed3" : { "OS": {"Config": 0, "Position": 12, "Width": 1, "DefaultValue": 1}, "User": {"Config": 0, "Position": 13, "Width": 1, "DefaultValue": 1}, "EnablePMI": {"Config": 0, "Position": 15, "Width": 1, "DefaultValue": 0}, "EventCode": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "UMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "EdgeDetect": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "Invert": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "CounterMask": {"Config": 0, "Position": -1, "__comment": "position=-1 means field ignored"}, "PerfMetrics": {"Config": 2, "Position": 0, "Width": 1, "DefaultValue": 0, "__comment": "fake field to tell the collector to also print the L1 top-down metrics, not just raw slots count"} } }, "cha" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "TIDEnable": {"Config": 0, "Position": 16, "Width": 1, "DefaultValue": 0}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 26}, "TID": {"Config": 1, "Position": 0, "Width": 10, "DefaultValue": 0} } }, "imc" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "xpi" : { "__comment" : "this is for UPI LL and QPI LL uncore PMUs", "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 24} } }, "m2m" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0}, "UMaskExt": {"Config": 0, "Position": 32, "Width": 8} } }, "m3upi" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "mdf" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "irp" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 8, "DefaultValue": 0} } }, "pcu" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0} } }, "iio" : { "programmable" : { "EventCode": {"Config": 0, "Position": 0, "Width": 8}, "UMask": {"Config": 0, "Position": 8, "Width": 8}, "EdgeDetect": {"Config": 0, "Position": 18, "Width": 1, "DefaultValue": 0}, "Threshold": {"Config": 0, "Position": 24, "Width": 12, "DefaultValue": 0}, "PortMask": {"Config": 0, "Position": 36, "Width": 12}, "FCMask": {"Config": 0, "Position": 48, "Width": 3} } } } pcm-202307/src/WinMSRDriver/000077500000000000000000000000001445420033100154465ustar00rootroot00000000000000pcm-202307/src/WinMSRDriver/MSR.inf000066400000000000000000000041031445420033100166030ustar00rootroot00000000000000; ; MSR.inf ; [Version] Signature="$WINDOWS NT$" Class=Sample ; TODO: edit Class ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} ; TODO: edit ClassGuid Provider=%ManufacturerName% CatalogFile=MSR.cat DriverVer= ; TODO: set DriverVer in stampinf property pages [DestinationDirs] DefaultDestDir = 12 MSR_Device_CoInstaller_CopyFiles = 11 ; ================= Class section ===================== [ClassInstall32] Addreg=SampleClassReg [SampleClassReg] HKR,,,0,%ClassName% HKR,,Icon,,-5 [SourceDisksNames] 1 = %DiskName%,,,"" WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames [SourceDisksFiles] MSR.sys = 1,, ;***************************************** ; Install Section ;***************************************** [Manufacturer] %ManufacturerName%=Standard,NT$ARCH$ [Standard.NT$ARCH$] %MSR.DeviceDesc%=MSR_Device, Root\MSR ; TODO: edit hw-id [MSR_Device.NT] CopyFiles=Drivers_Dir [Drivers_Dir] MSR.sys ;-------------- Service installation [MSR_Device.NT.Services] AddService = MSR,%SPSVCINST_ASSOCSERVICE%, MSR_Service_Inst ; -------------- MSR driver install sections [MSR_Service_Inst] DisplayName = %MSR.SVCDESC% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 3 ; SERVICE_DEMAND_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %12%\MSR.sys ; ;--- MSR_Device Coinstaller installation ------ ; [MSR_Device.NT.CoInstallers] AddReg=MSR_Device_CoInstaller_AddReg CopyFiles=MSR_Device_CoInstaller_CopyFiles [MSR_Device_CoInstaller_AddReg] HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" [MSR_Device_CoInstaller_CopyFiles] WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll [MSR_Device.NT.Wdf] KmdfService = MSR, MSR_wdfsect [MSR_wdfsect] KmdfLibraryVersion = $KMDFVERSION$ [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 ManufacturerName="" ;TODO: Replace with your manufacturer name ClassName="Samples" ; TODO: edit ClassName DiskName = "MSR Installation Disk" MSR.DeviceDesc = "MSR Device" MSR.SVCDESC = "MSR Service" pcm-202307/src/WinMSRDriver/MSR.vcxproj000066400000000000000000000167411445420033100175350ustar00rootroot00000000000000 Debug Win32 Release Win32 Debug x64 Release x64 Debug ARM Release ARM Debug ARM64 Release ARM64 {0A94F800-4821-4654-B0A9-53D02F51A81E} {1bc93793-694f-48fe-9372-81e2b05556fd} v4.5 12.0 Debug Win32 MSR Windows10 true WindowsKernelModeDriver10.0 Driver KMDF Universal Windows10 false WindowsKernelModeDriver10.0 Driver KMDF Universal Windows10 true WindowsKernelModeDriver10.0 Driver KMDF Universal false WindowsKernelModeDriver10.0 Driver KMDF Universal Windows10 true WindowsKernelModeDriver10.0 Driver KMDF Universal Windows10 false WindowsKernelModeDriver10.0 Driver KMDF Universal Windows10 true WindowsKernelModeDriver10.0 Driver KMDF Universal Windows10 false WindowsKernelModeDriver10.0 Driver KMDF Universal DbgengKernelDebugger C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\um;$(IncludePath) DbgengKernelDebugger DbgengKernelDebugger DbgengKernelDebugger DbgengKernelDebugger DbgengKernelDebugger DbgengKernelDebugger DbgengKernelDebugger SHA256 pcm-202307/src/WinMSRDriver/makefile000066400000000000000000000000421445420033100171420ustar00rootroot00000000000000!INCLUDE $(NTMAKEENV)\makefile.defpcm-202307/src/WinMSRDriver/msr.h000066400000000000000000000004361445420033100164230ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation #ifndef MSR_INCLUDED #define MSR_INCLUDED /* written by Roman Dementiev */ #include #include #include #include #include "msrstruct.h" #endif pcm-202307/src/WinMSRDriver/msrmain.c000066400000000000000000000252511445420033100172650ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* WARNING: This driver code is only for testing purposes, not for production use */ #include "msr.h" #include "ntdef.h" #include /*! \file msrmain.cpp \brief Test Windows 7 Model Specific Driver implementation */ #define NT_DEVICE_NAME L"\\Driver\\RDMSR" #define DOS_DEVICE_NAME L"\\DosDevices\\RDMSR" struct DeviceExtension { HANDLE devMemHandle; HANDLE counterSetHandle; }; DRIVER_INITIALIZE DriverEntry; __drv_dispatchType(IRP_MJ_CREATE) __drv_dispatchType(IRP_MJ_CLOSE) DRIVER_DISPATCH dummyFunction; __drv_dispatchType(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH deviceControl; DRIVER_UNLOAD MSRUnload; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #pragma alloc_text(PAGE,MSRUnload) #pragma alloc_text(PAGE,dummyFunction) #pragma alloc_text(PAGE,deviceControl) #endif NTSTATUS DriverEntry( __in PDRIVER_OBJECT DriverObject, __in PUNICODE_STRING RegistryPath ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING UnicodeString; UNICODE_STRING dosDeviceName; PDEVICE_OBJECT MSRSystemDeviceObject = NULL; struct DeviceExtension * pExt = NULL; UNICODE_STRING devMemPath; OBJECT_ATTRIBUTES attr; UNREFERENCED_PARAMETER(RegistryPath); RtlInitUnicodeString(&UnicodeString, NT_DEVICE_NAME); RtlInitUnicodeString(&dosDeviceName, DOS_DEVICE_NAME); status = IoCreateDevice(DriverObject, sizeof(struct DeviceExtension), &UnicodeString, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &MSRSystemDeviceObject ); if (!NT_SUCCESS(status)) return status; DriverObject->DriverUnload = MSRUnload; DriverObject->MajorFunction[IRP_MJ_CLOSE] = dummyFunction; DriverObject->MajorFunction[IRP_MJ_CREATE] = dummyFunction; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = deviceControl; pExt = DriverObject->DeviceObject->DeviceExtension; RtlInitUnicodeString(&devMemPath, L"\\Device\\PhysicalMemory"); InitializeObjectAttributes(&attr, &devMemPath, OBJ_KERNEL_HANDLE, (HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL); status = ZwOpenSection(&pExt->devMemHandle, SECTION_MAP_READ | SECTION_MAP_WRITE, &attr); if (!NT_SUCCESS(status)) { DbgPrint("Error: failed ZwOpenSection(devMemHandle) => %08X\n", status); return status; } pExt->counterSetHandle = NULL; IoCreateSymbolicLink(&dosDeviceName, &UnicodeString); return status; } NTSTATUS dummyFunction(PDEVICE_OBJECT DeviceObject, PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); PAGED_CODE(); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID MSRUnload(PDRIVER_OBJECT DriverObject) { PDEVICE_OBJECT deviceObject = DriverObject->DeviceObject; UNICODE_STRING nameString; PAGED_CODE(); RtlInitUnicodeString(&nameString, DOS_DEVICE_NAME); IoDeleteSymbolicLink(&nameString); if (deviceObject != NULL) { IoDeleteDevice(deviceObject); } } NTSTATUS deviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) { NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION IrpStackLocation = NULL; struct MSR_Request * input_msr_req = NULL; struct PCICFG_Request * input_pcicfg_req = NULL; struct MMAP_Request* input_mmap_req = NULL; ULONG64 * output = NULL; GROUP_AFFINITY old_affinity, new_affinity; ULONG inputSize = 0; PCI_SLOT_NUMBER slot; unsigned size = 0; PROCESSOR_NUMBER ProcNumber; struct DeviceExtension* pExt = NULL; LARGE_INTEGER offset; SIZE_T mmapSize = 0; PVOID baseAddress = NULL; pExt = DeviceObject->DeviceExtension; PAGED_CODE(); IrpStackLocation = IoGetCurrentIrpStackLocation(Irp); if (IrpStackLocation) { inputSize = IrpStackLocation->Parameters.DeviceIoControl.InputBufferLength; if (IrpStackLocation->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(ULONG64)) { input_msr_req = (struct MSR_Request *)Irp->AssociatedIrp.SystemBuffer; input_pcicfg_req = (struct PCICFG_Request *)Irp->AssociatedIrp.SystemBuffer; input_mmap_req = (struct MMAP_Request*)Irp->AssociatedIrp.SystemBuffer; output = (ULONG64 *)Irp->AssociatedIrp.SystemBuffer; RtlSecureZeroMemory(&ProcNumber, sizeof(PROCESSOR_NUMBER)); switch (IrpStackLocation->Parameters.DeviceIoControl.IoControlCode) { case IO_CTL_MSR_WRITE: if (inputSize < sizeof(struct MSR_Request)) { status = STATUS_INVALID_PARAMETER; break; } RtlSecureZeroMemory(&new_affinity, sizeof(GROUP_AFFINITY)); RtlSecureZeroMemory(&old_affinity, sizeof(GROUP_AFFINITY)); KeGetProcessorNumberFromIndex(input_msr_req->core_id, &ProcNumber); new_affinity.Group = ProcNumber.Group; new_affinity.Mask = 1ULL << (ProcNumber.Number); KeSetSystemGroupAffinityThread(&new_affinity, &old_affinity); __writemsr(input_msr_req->msr_address, input_msr_req->write_value); KeRevertToUserGroupAffinityThread(&old_affinity); Irp->IoStatus.Information = 0; // result size break; case IO_CTL_MSR_READ: if (inputSize < sizeof(struct MSR_Request)) { status = STATUS_INVALID_PARAMETER; break; } RtlSecureZeroMemory(&new_affinity, sizeof(GROUP_AFFINITY)); RtlSecureZeroMemory(&old_affinity, sizeof(GROUP_AFFINITY)); KeGetProcessorNumberFromIndex(input_msr_req->core_id, &ProcNumber); new_affinity.Group = ProcNumber.Group; new_affinity.Mask = 1ULL << (ProcNumber.Number); KeSetSystemGroupAffinityThread(&new_affinity, &old_affinity); *output = __readmsr(input_msr_req->msr_address); KeRevertToUserGroupAffinityThread(&old_affinity); Irp->IoStatus.Information = sizeof(ULONG64); // result size break; case IO_CTL_MMAP_SUPPORT: *output = 1; Irp->IoStatus.Information = sizeof(ULONG64); // result size break; case IO_CTL_MMAP: offset = input_mmap_req->address; mmapSize = input_mmap_req->size; status = ZwMapViewOfSection(pExt->devMemHandle, ZwCurrentProcess(), &baseAddress, 0L, PAGE_SIZE, &offset, &mmapSize, ViewUnmap, 0, PAGE_READWRITE); if (status != STATUS_SUCCESS || baseAddress == NULL) { DbgPrint("Error: ZwMapViewOfSection failed, %lld %lld (%ld).\n", offset.QuadPart, mmapSize, status); } else { *output = (ULONG64)baseAddress; Irp->IoStatus.Information = sizeof(PVOID); // result size } break; case IO_CTL_MUNMAP: status = ZwUnmapViewOfSection(ZwCurrentProcess(), (PVOID) input_mmap_req->address.QuadPart); break; case IO_CTL_PMU_ALLOC_SUPPORT: *output = 1; Irp->IoStatus.Information = sizeof(ULONG64); // result size break; case IO_CTL_PMU_ALLOC: if (pExt->counterSetHandle == NULL) { status = HalAllocateHardwareCounters(NULL, 0, NULL, &(pExt->counterSetHandle)); } *output = status; Irp->IoStatus.Information = sizeof(ULONG64); // result size break; case IO_CTL_PMU_FREE: if (pExt->counterSetHandle != NULL) { status = HalFreeHardwareCounters(pExt->counterSetHandle); if (status == STATUS_SUCCESS) { pExt->counterSetHandle = NULL; } } *output = status; Irp->IoStatus.Information = sizeof(ULONG64); // result size break; case IO_CTL_PCICFG_WRITE: if (inputSize < sizeof(struct PCICFG_Request) || (input_pcicfg_req->bytes != 4 && input_pcicfg_req->bytes != 8)) { status = STATUS_INVALID_PARAMETER; break; } slot.u.AsULONG = 0; slot.u.bits.DeviceNumber = input_pcicfg_req->dev; slot.u.bits.FunctionNumber = input_pcicfg_req->func; size = HalSetBusDataByOffset(PCIConfiguration, input_pcicfg_req->bus, slot.u.AsULONG, &(input_pcicfg_req->write_value), input_pcicfg_req->reg, input_pcicfg_req->bytes); if (size != input_pcicfg_req->bytes) { status = STATUS_INVALID_PARAMETER; break; } Irp->IoStatus.Information = 0; // result size break; case IO_CTL_PCICFG_READ: if (inputSize < sizeof(struct PCICFG_Request) || (input_pcicfg_req->bytes != 4 && input_pcicfg_req->bytes != 8)) { status = STATUS_INVALID_PARAMETER; break; } slot.u.AsULONG = 0; slot.u.bits.DeviceNumber = input_pcicfg_req->dev; slot.u.bits.FunctionNumber = input_pcicfg_req->func; size = HalGetBusDataByOffset(PCIConfiguration, input_pcicfg_req->bus, slot.u.AsULONG, output, input_pcicfg_req->reg, input_pcicfg_req->bytes); if (size != input_pcicfg_req->bytes) { status = STATUS_INVALID_PARAMETER; break; } Irp->IoStatus.Information = sizeof(ULONG64); // result size break; default: status = STATUS_INVALID_DEVICE_REQUEST; } } else status = STATUS_INVALID_PARAMETER; } else status = STATUS_INVALID_DEVICE_REQUEST; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } pcm-202307/src/WinMSRDriver/msrstruct.h000066400000000000000000000031611445420033100176660ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* written by Roman Dementiev */ #ifndef MSR_STRUCT_HEADER #define MSR_STRUCT_HEADER #ifndef CTL_CODE #include #endif #define MSR_DEV_TYPE 50000 #define IO_CTL_MSR_READ CTL_CODE(MSR_DEV_TYPE, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_MSR_WRITE CTL_CODE(MSR_DEV_TYPE, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_PCICFG_READ CTL_CODE(MSR_DEV_TYPE, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_PCICFG_WRITE CTL_CODE(MSR_DEV_TYPE, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_MMAP_SUPPORT CTL_CODE(MSR_DEV_TYPE, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_MMAP CTL_CODE(MSR_DEV_TYPE, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_MUNMAP CTL_CODE(MSR_DEV_TYPE, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_PMU_ALLOC_SUPPORT CTL_CODE(MSR_DEV_TYPE, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_PMU_ALLOC CTL_CODE(MSR_DEV_TYPE, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IO_CTL_PMU_FREE CTL_CODE(MSR_DEV_TYPE, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS) struct MSR_Request { int core_id; ULONG64 msr_address; ULONG64 write_value; /* value to write if write requet ignored if read request */ }; struct PCICFG_Request { ULONG bus, dev, func, reg, bytes; // "bytes" can be only 4 or 8 /* value to write if write request ignored if read request */ ULONG64 write_value; }; struct MMAP_Request { LARGE_INTEGER address; SIZE_T size; }; #endif pcm-202307/src/WinMSRDriver/sources000066400000000000000000000003331445420033100170530ustar00rootroot00000000000000TARGETNAME=msr TARGETTYPE=DRIVER NTDDI_VERSION=NTDDI_WIN7 MSC_WARNING_LEVEL=/W3 /WX INCLUDES=\ $(DDK_INC_PATH); TARGETLIBS=\ $(DDK_LIB_PATH)\ntoskrnl.lib SOURCES=msrmain.c PRECOMPILED_INCLUDE=msr.h pcm-202307/src/bw.cpp000066400000000000000000000156631445420033100143020ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev, // Patrick Konsor // #include #include "bw.h" #include "pci.h" #include "utils.h" #include namespace pcm { constexpr auto PCM_CLIENT_IMC_BAR_OFFSET = 0x0048; constexpr auto PCM_TGL_IMC_STEP = 0x10000; unsigned int PCM_TGL_IMC_DRAM_DATA_READS[2] = { 0x5058, 0xd858 }; unsigned int PCM_TGL_IMC_DRAM_DATA_WRITES[2] = { 0x50A0, 0xd8A0 }; unsigned int PCM_TGL_IMC_MMAP_SIZE[2] = { 0x5000 + 0x1000, 0xd000 + 0x1000 }; unsigned int PCM_TGL_IMC_EVENT_BASE[2] = { 0x5000, 0xd000 }; uint64 getClientIMCStartAddr() { PciHandleType imcHandle(0, 0, 0, 0); // memory controller device coordinates: domain 0, bus 0, device 0, function 0 uint64 imcbar = 0; imcHandle.read64(PCM_CLIENT_IMC_BAR_OFFSET, &imcbar); // std::cout << "DEBUG: imcbar=" << std::hex << imcbar << "\n" << std::dec << std::flush; if (!imcbar) { std::cerr << "ERROR: imcbar is zero.\n"; throw std::exception(); } return imcbar & (~(4096ULL - 1ULL)); // round down to 4K } TGLClientBW::TGLClientBW() { const auto startAddr = getClientIMCStartAddr(); for (size_t i = 0; i < mmioRange.size(); ++i) { for (size_t model = 0; model < mmioRange[i].size(); ++model) { mmioRange[i][model] = std::make_shared(startAddr + i * PCM_TGL_IMC_STEP + PCM_TGL_IMC_EVENT_BASE[model], PCM_TGL_IMC_MMAP_SIZE[model] - PCM_TGL_IMC_EVENT_BASE[model]); } } } uint64 TGLClientBW::getImcReads() { uint64 result = 0; for (auto & r : mmioRange) for (size_t model = 0; model < r.size(); ++model) { result += r[model]->read64(PCM_TGL_IMC_DRAM_DATA_READS[model] - PCM_TGL_IMC_EVENT_BASE[model]); } return result; } uint64 TGLClientBW::getImcWrites() { uint64 result = 0; for (auto & r : mmioRange) for (size_t model = 0; model < r.size(); ++model) { result += r[model]->read64(PCM_TGL_IMC_DRAM_DATA_WRITES[model] - PCM_TGL_IMC_EVENT_BASE[model]); } return result; } #define PCM_CLIENT_IMC_DRAM_GT_REQUESTS (0x5040) #define PCM_CLIENT_IMC_DRAM_IA_REQUESTS (0x5044) #define PCM_CLIENT_IMC_DRAM_IO_REQUESTS (0x5048) #define PCM_CLIENT_IMC_DRAM_DATA_READS (0x5050) #define PCM_CLIENT_IMC_DRAM_DATA_WRITES (0x5054) #define PCM_CLIENT_IMC_MMAP_SIZE (0x6000) #define PCM_CLIENT_IMC_EVENT_BASE (0x5000) ClientBW::ClientBW() { mmioRange = std::make_shared(getClientIMCStartAddr() + PCM_CLIENT_IMC_EVENT_BASE, PCM_CLIENT_IMC_MMAP_SIZE - PCM_CLIENT_IMC_EVENT_BASE); } uint64 ClientBW::getImcReads() { return mmioRange->read32(PCM_CLIENT_IMC_DRAM_DATA_READS - PCM_CLIENT_IMC_EVENT_BASE); } uint64 ClientBW::getImcWrites() { return mmioRange->read32(PCM_CLIENT_IMC_DRAM_DATA_WRITES - PCM_CLIENT_IMC_EVENT_BASE); } uint64 ClientBW::getGtRequests() { return mmioRange->read32(PCM_CLIENT_IMC_DRAM_GT_REQUESTS - PCM_CLIENT_IMC_EVENT_BASE); } uint64 ClientBW::getIaRequests() { return mmioRange->read32(PCM_CLIENT_IMC_DRAM_IA_REQUESTS - PCM_CLIENT_IMC_EVENT_BASE); } uint64 ClientBW::getIoRequests() { return mmioRange->read32(PCM_CLIENT_IMC_DRAM_IO_REQUESTS - PCM_CLIENT_IMC_EVENT_BASE); } #define PCM_ADL_IMC_EVENT_BASE (0xd000) #define PCM_ADL_IMC_DRAM_DATA_READS (0x858) #define PCM_ADL_IMC_DRAM_DATA_WRITES (0x8A0) ADLClientBW::ADLClientBW() { mmioRange = std::make_shared(getClientIMCStartAddr() + PCM_ADL_IMC_EVENT_BASE, 0x1000); } uint64 ADLClientBW::getImcReads() { return mmioRange->read32(PCM_ADL_IMC_DRAM_DATA_READS); } uint64 ADLClientBW::getImcWrites() { return mmioRange->read32(PCM_ADL_IMC_DRAM_DATA_WRITES); } #define PCM_SERVER_IMC_DRAM_DATA_READS (0x2290) #define PCM_SERVER_IMC_DRAM_DATA_WRITES (0x2298) #define PCM_SERVER_IMC_PMM_DATA_READS (0x22a0) #define PCM_SERVER_IMC_PMM_DATA_WRITES (0x22a8) #define PCM_SERVER_IMC_MMAP_SIZE (0x4000) std::vector getServerBars(const size_t regBase, const uint32 numIMC, const uint32 root_segment_ubox0, const uint32 root_bus_ubox0) { std::vector result; PciHandleType ubox0Handle(root_segment_ubox0, root_bus_ubox0, SERVER_UBOX0_REGISTER_DEV_ADDR, SERVER_UBOX0_REGISTER_FUNC_ADDR); uint32 mmioBase = 0; ubox0Handle.read32(0xd0, &mmioBase); // std::cout << "mmioBase is 0x" << std::hex << mmioBase << std::dec << std::endl; for (uint32 i = 0; i < numIMC; ++i) { uint32 memOffset = 0; ubox0Handle.read32(regBase + i * 4, &memOffset); // std::cout << "memOffset for imc "< result = getServerBars(0xd4, 1, root_segment_ubox0, root_bus_ubox0); assert(result.size() == 1); return result[0]; } std::vector getServerMemBars(const uint32 numIMC, const uint32 root_segment_ubox0, const uint32 root_bus_ubox0) { return getServerBars(0xd8, numIMC, root_segment_ubox0, root_bus_ubox0); } ServerBW::ServerBW(const uint32 numIMC, const uint32 root_segment_ubox0, const uint32 root_bus_ubox0) { auto memBars = getServerMemBars(numIMC, root_segment_ubox0, root_bus_ubox0); for (auto & memBar: memBars) { mmioRanges.push_back(std::make_shared(memBar, PCM_SERVER_IMC_MMAP_SIZE)); } } uint64 ServerBW::getImcReads() { uint64 result = 0; for (auto & mmio: mmioRanges) { // std::cout << "PCM_SERVER_IMC_DRAM_DATA_READS: " << mmio->read64(PCM_SERVER_IMC_DRAM_DATA_READS) << std::endl; result += mmio->read64(PCM_SERVER_IMC_DRAM_DATA_READS); } return result; } uint64 ServerBW::getImcWrites() { uint64 result = 0; for (auto & mmio : mmioRanges) { result += mmio->read64(PCM_SERVER_IMC_DRAM_DATA_WRITES); } return result; } uint64 ServerBW::getPMMReads() { uint64 result = 0; for (auto & mmio : mmioRanges) { result += mmio->read64(PCM_SERVER_IMC_PMM_DATA_READS); } return result; } uint64 ServerBW::getPMMWrites() { uint64 result = 0; for (auto & mmio : mmioRanges) { result += mmio->read64(PCM_SERVER_IMC_PMM_DATA_WRITES); } return result; } } // namespace pcm pcm-202307/src/bw.h000066400000000000000000000041441445420033100137370ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012-2022, Intel Corporation // written by Roman Dementiev // #pragma once /*! \file bw.h \brief Interfaces to access free-running bandwidth counters */ #include #include #include #include "mmio.h" namespace pcm { class FreeRunningBWCounters { public: virtual uint64 getImcReads() { return 0; } virtual uint64 getImcWrites() { return 0; } virtual uint64 getGtRequests() { return 0; } virtual uint64 getIaRequests() { return 0; } virtual uint64 getIoRequests() { return 0; } virtual uint64 getPMMReads() { return 0; } virtual uint64 getPMMWrites() { return 0; } virtual ~FreeRunningBWCounters() {} }; class TGLClientBW : public FreeRunningBWCounters { std::array, 2>, 2> mmioRange; public: TGLClientBW(); uint64 getImcReads() override; uint64 getImcWrites() override; }; class ADLClientBW : public FreeRunningBWCounters { std::shared_ptr mmioRange; public: ADLClientBW(); uint64 getImcReads() override; uint64 getImcWrites() override; }; class ClientBW : public FreeRunningBWCounters { std::shared_ptr mmioRange; public: ClientBW(); uint64 getImcReads() override; uint64 getImcWrites() override; uint64 getGtRequests() override; uint64 getIaRequests() override; uint64 getIoRequests() override; }; std::vector getServerMemBars(const uint32 numIMC, const uint32 root_segment_ubox0, const uint32 root_bus_ubox0); size_t getServerSCFBar(const uint32 root_segment_ubox0, const uint32 root_bus_ubox0); class ServerBW { std::vector > mmioRanges; ServerBW(); public: ServerBW(const uint32 numIMC, const uint32 root_segment_ubox0, const uint32 root_bus_ubox0); uint64 getImcReads(); uint64 getImcWrites(); uint64 getPMMReads(); uint64 getPMMWrites(); }; } // namespace pcm pcm-202307/src/client/000077500000000000000000000000001445420033100144315ustar00rootroot00000000000000pcm-202307/src/client/.cproject000066400000000000000000000132221445420033100162430ustar00rootroot00000000000000 pcm-202307/src/client/.project000066400000000000000000000044351445420033100161060ustar00rootroot00000000000000 client org.eclipse.cdt.managedbuilder.core.genmakebuilder clean,full,incremental, ?name? org.eclipse.cdt.make.core.append_environment true org.eclipse.cdt.make.core.autoBuildTarget all org.eclipse.cdt.make.core.buildArguments org.eclipse.cdt.make.core.buildCommand make org.eclipse.cdt.make.core.cleanBuildTarget clean org.eclipse.cdt.make.core.contents org.eclipse.cdt.make.core.activeConfigSettings org.eclipse.cdt.make.core.enableAutoBuild false org.eclipse.cdt.make.core.enableCleanBuild true org.eclipse.cdt.make.core.enableFullBuild true org.eclipse.cdt.make.core.fullBuildTarget all org.eclipse.cdt.make.core.stopOnError true org.eclipse.cdt.make.core.useDefaultBuildCmd true org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder full,incremental, org.eclipse.cdt.core.cnature org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature pcm-202307/src/client/client.cpp000066400000000000000000000063321445420033100164170ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Steven Briscoe #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../daemon/common.h" #include "client.h" namespace PCMDaemon { Client::Client() : pollIntervalMs_(0), shmIdLocation_(DEFAULT_SHM_ID_LOCATION), shmAttached_(false), lastUpdatedClientTsc_(0) {} void Client::setSharedMemoryIdLocation(const std::string& location) { if(shmAttached_) { throw std::runtime_error("Shared memory segment already attached. You must call this method before the .connect() method."); } shmIdLocation_ = location; } void Client::setPollInterval(int pollMs) { pollIntervalMs_ = pollMs; } void Client::connect() { setupSharedMemory(); //Set last updated timestamp to avoid a detected change //when the client starts lastUpdatedClientTsc_ = sharedPCMState_->lastUpdateTscEnd; } PCMDaemon::SharedPCMState& Client::read() { if(pollIntervalMs_ <= 0) { throw std::runtime_error("The poll interval is not set or is negative."); } if(!shmAttached_) { throw std::runtime_error("Not attached to shared memory segment. Call .connect() method."); } while(true) { // Check client version matches daemon version if(strlen(sharedPCMState_->version) > 0 && strcmp(sharedPCMState_->version, VERSION) != 0) { std::cout << sharedPCMState_->lastUpdateTscEnd << " " << lastUpdatedClientTsc_ << "\n"; std::stringstream ss; ss << "Out of date PCM daemon client. Client version: " << VERSION << " Daemon version: " << sharedPCMState_->version; throw std::runtime_error(ss.str()); } if(countersHaveUpdated()) { //There is new data lastUpdatedClientTsc_ = sharedPCMState_->lastUpdateTscEnd; return *sharedPCMState_; } else { //Nothing has changed since we last checked usleep(pollIntervalMs_ * 1000); } } } bool Client::countersHaveUpdated() { return lastUpdatedClientTsc_ != sharedPCMState_->lastUpdateTscEnd; } void Client::setupSharedMemory() { int sharedMemoryId; FILE *fp = fopen (shmIdLocation_.c_str(), "r"); if (!fp) { std::cerr << "Failed to open to shared memory key location: " << shmIdLocation_ << "\n"; exit(EXIT_FAILURE); } const int maxCharsToRead = 11; char readBuffer[maxCharsToRead + 1]; std::fill((char*)readBuffer, ((char*)readBuffer) + sizeof(readBuffer), 0); const auto nread = fread(&readBuffer, maxCharsToRead, 1, fp); if (nread == 0 && feof(fp) == 0) { fclose (fp); std::stringstream ss; ss << "fread failed for " << shmIdLocation_; throw std::runtime_error(ss.str()); } fclose (fp); assert(nread <= maxCharsToRead); sharedMemoryId = atoi(readBuffer); sharedPCMState_ = (PCMDaemon::SharedPCMState*)shmat(sharedMemoryId, NULL, 0); if (sharedPCMState_ == (void *)-1) { std::stringstream ss; ss << "Failed to attach shared memory segment (errno=" << errno << ") " << strerror(errno); throw std::runtime_error(ss.str()); } shmAttached_ = true; } } pcm-202307/src/client/client.h000066400000000000000000000013351445420033100160620ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Steven Briscoe #include #include #include #ifndef CLIENT_H_ #define CLIENT_H_ #include "../daemon/common.h" namespace PCMDaemon { class Client { public: Client(); void setSharedMemoryIdLocation(const std::string& location); void setPollInterval(int pollMs); void connect(); PCMDaemon::SharedPCMState& read(); bool countersHaveUpdated(); private: void setupSharedMemory(); int pollIntervalMs_; std::string shmIdLocation_; bool shmAttached_; PCMDaemon::SharedPCMState* sharedPCMState_ = nullptr; PCMDaemon::uint64 lastUpdatedClientTsc_; }; } #endif /* CLIENT_H_ */ pcm-202307/src/client/main.cpp000066400000000000000000000373641445420033100160760ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Steven Briscoe //Test program for PCM Daemon client #include #include #include #include #include "client.h" void printTitle(std::string title) { std::cout << std::setw(26) << std::left << title; } int main(int argc, char * argv[]) { if (argc < 2) { std::cerr << "Usage: " << argv[0] << " pollMs\n"; return -1; } try { PCMDaemon::Client client; // client.setSharedMemoryIdLocation("/tmp/test-file"); client.connect(); client.setPollInterval(atoi(argv[1])); int coutPrecision = 2; while (true) { PCMDaemon::SharedPCMState& state = client.read(); PCMDaemon::SharedPCMCounters& counters = state.pcm; std::cout << "\n----- Something changed -----\n\n"; // Display internal metrics printTitle("Last updated TSC"); std::cout << state.lastUpdateTscEnd << "\n"; printTitle("Timestamp"); std::cout << state.timestamp << "\n"; printTitle("Cycles to get counters"); std::cout << state.cyclesToGetPCMState << "\n"; printTitle("Poll interval (ms)"); std::cout << state.pollMs << "\n"; std::cout << "\n\n"; // Display system counters printTitle("Num. of cores"); std::cout << counters.system.numOfCores << "\n"; printTitle("Num. of online cores"); std::cout << counters.system.numOfOnlineCores << "\n"; printTitle("Num. of sockets"); std::cout << counters.system.numOfSockets << "\n"; printTitle("Num. of online sockets"); std::cout << counters.system.numOfOnlineSockets << "\n"; printTitle("QPI/UPI links per socket"); std::cout << counters.system.numOfQPILinksPerSocket << "\n"; std::cout << "\n\n"; // Display core counters printTitle("Core ID"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].coreId << " "; } std::cout << "\n"; printTitle("Socket ID"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].socketId << " "; } std::cout << "\n"; printTitle("IPC"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].instructionsPerCycle << " "; } std::cout << "\n"; printTitle("Cycles"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].cycles << " "; } std::cout << "\n"; printTitle("Inst. Ret."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].instructionsRetired << " "; } std::cout << "\n"; printTitle("Exec usg."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].execUsage << " "; } std::cout << "\n"; printTitle("Rela. Freq."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].relativeFrequency << " "; } std::cout << "\n"; printTitle("Active Rela. Freq"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].activeRelativeFrequency << " "; } std::cout << "\n"; printTitle("L3 C Miss"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l3CacheMisses << " "; } std::cout << "\n"; printTitle("L3 C Reference"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l3CacheReference << " "; } std::cout << "\n"; printTitle("L2 C Miss"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l2CacheMisses << " "; } std::cout << "\n"; printTitle("L3 Hit Ratio"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l3CacheHitRatio << " "; } std::cout << "\n"; printTitle("L2 Hit Ratio"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l2CacheHitRatio << " "; } std::cout << "\n"; printTitle("L3 C MPI"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l3CacheMPI << " "; } std::cout << "\n"; printTitle("L2 C MPI"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l2CacheMPI << " "; } std::cout << "\n"; printTitle("L3 Occu. Avail."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l3CacheOccupancyAvailable << " "; } std::cout << "\n"; printTitle("L3 Occu."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].l3CacheOccupancy << " "; } std::cout << "\n"; printTitle("L. Mem. BW Avail."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].localMemoryBWAvailable << " "; } std::cout << "\n"; printTitle("L. Mem. BW"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].localMemoryBW << " "; } std::cout << "\n"; printTitle("R. Mem. BW Avail."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].remoteMemoryBWAvailable << " "; } std::cout << "\n"; printTitle("R. Mem. BW"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].remoteMemoryBW << " "; } std::cout << "\n"; printTitle("L. Mem. Accesses"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].localMemoryAccesses << " "; } std::cout << "\n"; printTitle("R. Mem. Accesses"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].remoteMemoryAccesses << " "; } std::cout << "\n"; printTitle("Thermal headroom"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineCores; ++i) { std::cout << std::setprecision(coutPrecision) << counters.core.cores[i].thermalHeadroom << " "; } std::cout << "\n"; std::cout << "\n\n"; // Display memory counters printTitle("PMM Metrics Avail."); std::cout << std::setprecision(coutPrecision) << counters.memory.pmmMetricsAvailable << " "; std::cout << "\n"; printTitle("DRAM Read p/Sock."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.memory.sockets[i].read << " "; } std::cout << "\n"; printTitle("DRAM Write p/Sock."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.memory.sockets[i].write << " "; } std::cout << "\n"; if (counters.memory.pmmMetricsAvailable) { printTitle("PMM Read p/Sock."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.memory.sockets[i].pmmRead << " "; } std::cout << "\n"; printTitle("PMM Write p/Sock."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.memory.sockets[i].pmmWrite << " "; } std::cout << "\n"; printTitle("PMM Memory Mode hit rate p/Sock. "); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.memory.sockets[i].pmmMemoryModeHitRate << " "; } std::cout << "\n"; } printTitle("Mem Total p/Sock."); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.memory.sockets[i].total << " "; } std::cout << "\n"; printTitle("DRAM Read Sys."); std::cout << std::setprecision(coutPrecision) << counters.memory.system.read << " "; std::cout << "\n"; printTitle("DRAM Write Sys."); std::cout << std::setprecision(coutPrecision) << counters.memory.system.write << " "; std::cout << "\n"; if (counters.memory.pmmMetricsAvailable) { printTitle("PMM Read Sys."); std::cout << std::setprecision(coutPrecision) << counters.memory.system.pmmRead << " "; std::cout << "\n"; printTitle("PMM Write Sys."); std::cout << std::setprecision(coutPrecision) << counters.memory.system.pmmWrite << " "; std::cout << "\n"; } printTitle("Mem Total Sys."); std::cout << std::setprecision(coutPrecision) << counters.memory.system.total << " "; std::cout << "\n"; printTitle("Mem Energy Avail."); std::cout << std::setprecision(coutPrecision) << counters.memory.dramEnergyMetricsAvailable << " "; std::cout << "\n"; if (counters.memory.dramEnergyMetricsAvailable) { printTitle("Mem Energy p/Sock"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.memory.sockets[i].dramEnergy << " "; } std::cout << "\n"; } std::cout << "\n\n"; // Display QPI counters printTitle("QPI/UPI in. Avail."); std::cout << std::setprecision(coutPrecision) << counters.qpi.incomingQPITrafficMetricsAvailable << " "; std::cout << "\n"; if (counters.qpi.incomingQPITrafficMetricsAvailable) { printTitle("QPI/UPI No. of Links"); std::cout << std::setprecision(coutPrecision) << counters.system.numOfQPILinksPerSocket << "\n"; printTitle("QPI/UPI in. p/Sock"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.qpi.incoming[i].total << " "; } std::cout << "\n"; printTitle("QPI/UPI in. p/Link/Sock"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << "Socket: " << i << " (bytes)\t\t"; for (PCMDaemon::uint32 l = 0; l < counters.system.numOfQPILinksPerSocket; ++l) { std::cout << std::setw(12) << std::left << std::setprecision(coutPrecision) << counters.qpi.incoming[i].links[l].bytes << " "; } std::cout << "\n"; printTitle(""); std::cout << "Socket: " << i << " (utilization)\t"; for (PCMDaemon::uint32 l = 0; l < counters.system.numOfQPILinksPerSocket; ++l) { std::cout << std::setw(12) << std::left << std::setprecision(coutPrecision) << counters.qpi.incoming[i].links[l].utilization << " "; } std::cout << "\n"; printTitle(""); } std::cout << "\n"; printTitle("QPI/UPI in. Total"); std::cout << std::setprecision(coutPrecision) << counters.qpi.incomingTotal << " "; std::cout << "\n\n"; } printTitle("QPI/UPI out. Avail."); std::cout << std::setprecision(coutPrecision) << counters.qpi.outgoingQPITrafficMetricsAvailable << " "; std::cout << "\n"; if (counters.qpi.outgoingQPITrafficMetricsAvailable) { printTitle("QPI/UPI No. of Links"); std::cout << std::setprecision(coutPrecision) << counters.system.numOfQPILinksPerSocket << "\n"; printTitle("QPI/UPI out. p/Sock"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << std::setprecision(coutPrecision) << counters.qpi.outgoing[i].total << " "; } std::cout << "\n"; printTitle("QPI/UPI out. p/Link/Sock"); for (PCMDaemon::uint32 i = 0; i < counters.system.numOfOnlineSockets; ++i) { std::cout << "Socket: " << i << " (bytes)\t\t"; for (PCMDaemon::uint32 l = 0; l < counters.system.numOfQPILinksPerSocket; ++l) { std::cout << std::setw(12) << std::left << std::setprecision(coutPrecision) << counters.qpi.outgoing[i].links[l].bytes << " "; } std::cout << "\n"; printTitle(""); std::cout << "Socket: " << i << " (utilization)\t"; for (PCMDaemon::uint32 l = 0; l < counters.system.numOfQPILinksPerSocket; ++l) { std::cout << std::setw(12) << std::left << std::setprecision(coutPrecision) << counters.qpi.outgoing[i].links[l].utilization << " "; } std::cout << "\n"; printTitle(""); } std::cout << "\n"; printTitle("QPI/UPI out. Total"); std::cout << std::setprecision(coutPrecision) << counters.qpi.outgoingTotal << " "; std::cout << "\n"; } std::cout << std::flush; } } catch (const std::runtime_error & e) { std::cerr << "PCM Error in client. Exception " << e.what() << "\n"; return -1; } return 0; } pcm-202307/src/cpuasynchcounter.h000066400000000000000000000144031445420033100167230ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // // asynchronous CPU conters // // contact: Thomas Willhalm #ifndef CPUASYNCHCOUNTER_HEADER #define CPUASYNCHCOUNTER_HEADER /*! \file cpuasynchcounter.h \brief Implementation of a POSIX thread that periodically saves the current state of counters and exposes them to other threads */ #include #include #include "cpucounters.h" #define DELAY 1 // in seconds using namespace pcm; void * UpdateCounters(void *); class AsynchronCounterState { PCM * m; CoreCounterState * cstates1, * cstates2; SocketCounterState * skstates1, * skstates2; SystemCounterState sstate1, sstate2; pthread_t UpdateThread; pthread_mutex_t CounterMutex; friend void * UpdateCounters(void *); AsynchronCounterState(const AsynchronCounterState &) = delete; const AsynchronCounterState & operator = (const AsynchronCounterState &) = delete; public: AsynchronCounterState() { m = PCM::getInstance(); PCM::ErrorCode status = m->program(); if (status != PCM::Success) { std::cerr << "\nCannot access CPU counters. Try to run 'pcm 1' to check the PMU access status.\n\n"; exit(-1); } cstates1 = new CoreCounterState[m->getNumCores()]; cstates2 = new CoreCounterState[m->getNumCores()]; skstates1 = new SocketCounterState[m->getNumSockets()]; skstates2 = new SocketCounterState[m->getNumSockets()]; for (uint32 i = 0; i < m->getNumCores(); ++i) { cstates1[i] = getCoreCounterState(i); cstates2[i] = getCoreCounterState(i); } for (uint32 i = 0; i < m->getNumSockets(); ++i) { skstates1[i] = getSocketCounterState(i); skstates2[i] = getSocketCounterState(i); } pthread_mutex_init(&CounterMutex, NULL); pthread_create(&UpdateThread, NULL, UpdateCounters, this); } ~AsynchronCounterState() { pthread_cancel(UpdateThread); if (pthread_mutex_destroy(&CounterMutex) != 0) std::cerr << "pthread_mutex_destroy failed\n"; try { m->cleanup(); } catch (const std::runtime_error & e) { std::cerr << "PCM Error in ~AsynchronCounterState(). Exception " << e.what() << "\n"; } delete[] cstates1; delete[] cstates2; delete[] skstates1; delete[] skstates2; } uint32 getNumCores() { return m->getNumCores(); } uint32 getNumSockets() { return m->getNumSockets(); } uint32 getQPILinksPerSocket() { return m->getQPILinksPerSocket(); } uint32 getSocketId(uint32 c) { return m->getSocketId(c); } const char * getXpi() { return m->xPI(); } template T get(uint32 core) { pthread_mutex_lock(&CounterMutex); T value = func(cstates2[core]); pthread_mutex_unlock(&CounterMutex); return value; } template T get(uint32 core) { pthread_mutex_lock(&CounterMutex); T value = func(cstates1[core], cstates2[core]); pthread_mutex_unlock(&CounterMutex); return value; } template T get(int param, uint32 core) { pthread_mutex_lock(&CounterMutex); T value = func(param, cstates1[core], cstates2[core]); pthread_mutex_unlock(&CounterMutex); return value; } template T getSocket(uint32 socket) { pthread_mutex_lock(&CounterMutex); T value = func(skstates2[socket]); pthread_mutex_unlock(&CounterMutex); return value; } template T getSocket(uint32 socket) { pthread_mutex_lock(&CounterMutex); T value = func(skstates1[socket], skstates2[socket]); pthread_mutex_unlock(&CounterMutex); return value; } template T getSocket(int param, uint32 socket) { pthread_mutex_lock(&CounterMutex); T value = func(param, skstates1[socket], skstates2[socket]); pthread_mutex_unlock(&CounterMutex); return value; } template T getSocket(uint32 socket, uint32 param) { pthread_mutex_lock(&CounterMutex); T value = func(socket, param, sstate1, sstate2); pthread_mutex_unlock(&CounterMutex); return value; } template T getSystem() { pthread_mutex_lock(&CounterMutex); T value = func(sstate1, sstate2); pthread_mutex_unlock(&CounterMutex); return value; } template T getSystem(int param) { pthread_mutex_lock(&CounterMutex); T value = func(param, sstate1, sstate2); pthread_mutex_unlock(&CounterMutex); return value; } }; void * UpdateCounters(void * state) { AsynchronCounterState * s = (AsynchronCounterState *)state; while (true) { if (pthread_mutex_lock(&(s->CounterMutex)) != 0) std::cerr << "pthread_mutex_lock failed\n"; for (uint32 core = 0; core < s->m->getNumCores(); ++core) { s->cstates1[core] = std::move(s->cstates2[core]); s->cstates2[core] = s->m->getCoreCounterState(core); } for (uint32 socket = 0; socket < s->m->getNumSockets(); ++socket) { s->skstates1[socket] = std::move(s->skstates2[socket]); s->skstates2[socket] = s->m->getSocketCounterState(socket); } s->sstate1 = std::move(s->sstate2); s->sstate2 = s->m->getSystemCounterState(); if (pthread_mutex_unlock(&(s->CounterMutex)) != 0) std::cerr << "pthread_mutex_unlock failed\n"; sleep(1); } return NULL; } #endif pcm-202307/src/cpucounters.cpp000066400000000000000000013102131445420033100162320ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // Otto Bruggeman // Thomas Willhalm // Pat Fay // Austen Ott // Jim Harris (FreeBSD) /*! \file cpucounters.cpp \brief The bulk of PCM implementation */ //#define PCM_TEST_FALLBACK_TO_ATOM #include #include #ifdef PCM_EXPORTS // pcm-lib.h includes cpucounters.h #include "windows\pcm-lib.h" #else #include "cpucounters.h" #endif #include "msr.h" #include "pci.h" #include "types.h" #include "utils.h" #include "topology.h" #if defined (__FreeBSD__) || defined(__DragonFly__) #include #include #include #include #include #include #include #include #endif #ifdef _MSC_VER #include #include #include #include #include "winring0/OlsApiInit.h" #include "windows/windriver.h" #else #include #if defined(__FreeBSD__) || (defined(__DragonFly__) && __DragonFly_version >= 400707) #include #include #include #endif #include #include #ifdef __linux__ #include #include #include #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #endif namespace pcm { #ifdef __APPLE__ // convertUnknownToInt is used in the safe sysctl call to convert an unknown size to an int int convertUnknownToInt(size_t size, char* value); #endif #ifdef _MSC_VER HMODULE hOpenLibSys = NULL; #ifndef NO_WINRING bool PCM::initWinRing0Lib() { const BOOL result = InitOpenLibSys(&hOpenLibSys); if (result == FALSE) { DeinitOpenLibSys(&hOpenLibSys); hOpenLibSys = NULL; return false; } BYTE major, minor, revision, release; GetDriverVersion(&major, &minor, &revision, &release); TCHAR buffer[128]; _stprintf_s(buffer, 128, TEXT("\\\\.\\WinRing0_%d_%d_%d"),(int)major,(int)minor, (int)revision); restrictDriverAccess(buffer); return true; } #endif // NO_WINRING #endif #if defined(__FreeBSD__) #define cpu_set_t cpuset_t #endif class TemporalThreadAffinity // speedup trick for Linux, FreeBSD, DragonFlyBSD, Windows { TemporalThreadAffinity(); // forbidden #if defined(__FreeBSD__) || (defined(__DragonFly__) && __DragonFly_version >= 400707) cpu_set_t old_affinity; bool restore; public: TemporalThreadAffinity(uint32 core_id, bool checkStatus = true, const bool restore_ = true) : restore(restore_) { assert(core_id < 1024); auto res = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &old_affinity); if (res != 0) { std::cerr << "ERROR: pthread_getaffinity_np for core " << core_id << " failed with code " << res << "\n"; throw std::exception(); } cpu_set_t new_affinity; CPU_ZERO(&new_affinity); CPU_SET(core_id, &new_affinity); // CPU_CMP() returns true if old_affinity is NOT equal to new_affinity if (!(CPU_CMP(&old_affinity, &new_affinity))) { restore = false; return; // the same affinity => return } res = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &new_affinity); if (res != 0 && checkStatus) { std::cerr << "ERROR: pthread_setaffinity_np for core " << core_id << " failed with code " << res << "\n"; throw std::exception(); } } ~TemporalThreadAffinity() { if (restore) pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &old_affinity); } bool supported() const { return true; } #elif defined(__linux__) cpu_set_t * old_affinity; static constexpr auto maxCPUs = 8192; const size_t set_size; bool restore; public: TemporalThreadAffinity(const uint32 core_id, bool checkStatus = true, const bool restore_ = true) : set_size(CPU_ALLOC_SIZE(maxCPUs)), restore(restore_) { assert(core_id < maxCPUs); old_affinity = CPU_ALLOC(maxCPUs); assert(old_affinity); auto res = pthread_getaffinity_np(pthread_self(), set_size, old_affinity); if (res != 0) { std::cerr << "ERROR: pthread_getaffinity_np for core " << core_id << " failed with code " << res << "\n"; throw std::exception(); } cpu_set_t * new_affinity = CPU_ALLOC(maxCPUs); assert(new_affinity); CPU_ZERO_S(set_size, new_affinity); CPU_SET_S(core_id, set_size, new_affinity); if (CPU_EQUAL_S(set_size, old_affinity, new_affinity)) { CPU_FREE(new_affinity); restore = false; return; } res = pthread_setaffinity_np(pthread_self(), set_size, new_affinity); CPU_FREE(new_affinity); if (res != 0 && checkStatus) { std::cerr << "ERROR: pthread_setaffinity_np for core " << core_id << " failed with code " << res << "\n"; throw std::exception(); } } ~TemporalThreadAffinity() { if (restore) pthread_setaffinity_np(pthread_self(), set_size, old_affinity); CPU_FREE(old_affinity); } bool supported() const { return true; } #elif defined(_MSC_VER) ThreadGroupTempAffinity affinity; public: TemporalThreadAffinity(uint32 core, bool checkStatus = true, const bool restore = true) : affinity(core, checkStatus, restore) { } bool supported() const { return true; } #else // not implemented for os x public: TemporalThreadAffinity(uint32) { } TemporalThreadAffinity(uint32, bool) {} bool supported() const { return false; } #endif }; PCM * PCM::instance = NULL; /* static int bitCount(uint64 n) { int count = 0; while (n) { count += static_cast(n & 0x00000001); n >>= static_cast(1); } return count; } */ std::mutex instanceCreationMutex; PCM * PCM::getInstance() { // lock-free read // cppcheck-suppress identicalConditionAfterEarlyExit if (instance) return instance; std::unique_lock _(instanceCreationMutex); // cppcheck-suppress identicalConditionAfterEarlyExit if (instance) return instance; return instance = new PCM(); } uint64 PCM::extractCoreGenCounterValue(uint64 val) { if (canUsePerf) return val; if(core_gen_counter_width) return extract_bits(val, 0, core_gen_counter_width-1); return val; } uint64 PCM::extractCoreFixedCounterValue(uint64 val) { if (canUsePerf) return val; if(core_fixed_counter_width) return extract_bits(val, 0, core_fixed_counter_width-1); return val; } uint64 PCM::extractUncoreGenCounterValue(uint64 val) { if(uncore_gen_counter_width) return extract_bits(val, 0, uncore_gen_counter_width-1); return val; } uint64 PCM::extractUncoreFixedCounterValue(uint64 val) { if(uncore_fixed_counter_width) return extract_bits(val, 0, uncore_fixed_counter_width-1); return val; } uint64 PCM::extractQOSMonitoring(uint64 val) { //Check if any of the error bit(63) or Unavailable bit(62) of the IA32_QM_CTR MSR are 1 if(val & (3ULL<<62)) { // invalid reading return static_cast(PCM_INVALID_QOS_MONITORING_DATA); } // valid reading return extract_bits(val,0,61); } int32 extractThermalHeadroom(uint64 val) { if(val & (1ULL<<31ULL)) { // valid reading return static_cast(extract_bits(val, 16, 22)); } // invalid reading return static_cast(PCM_INVALID_THERMAL_HEADROOM); } uint64 get_frequency_from_cpuid(); #if defined(__FreeBSD__) || defined(__DragonFly__) void pcm_cpuid_bsd(int leaf, PCM_CPUID_INFO& info, int core) { cpuctl_cpuid_args_t cpuid_args_freebsd; char cpuctl_name[64]; snprintf(cpuctl_name, 64, "/dev/cpuctl%d", core); auto fd = ::open(cpuctl_name, O_RDWR); cpuid_args_freebsd.level = leaf; ::ioctl(fd, CPUCTL_CPUID, &cpuid_args_freebsd); for (int i = 0; i < 4; ++i) { info.array[i] = cpuid_args_freebsd.data[i]; } ::close(fd); } #endif /* Adding the new version of cpuid with leaf and subleaf as an input */ void pcm_cpuid(const unsigned leaf, const unsigned subleaf, PCM_CPUID_INFO & info) { #ifdef _MSC_VER __cpuidex(info.array, leaf, subleaf); #else __asm__ __volatile__ ("cpuid" : \ "=a" (info.reg.eax), "=b" (info.reg.ebx), "=c" (info.reg.ecx), "=d" (info.reg.edx) : "a" (leaf), "c" (subleaf)); #endif } #ifdef __linux__ bool isNMIWatchdogEnabled(const bool silent); bool keepNMIWatchdogEnabled(); #endif void PCM::readCoreCounterConfig(const bool complainAboutMSR) { if (max_cpuid >= 0xa) { // get counter related info PCM_CPUID_INFO cpuinfo; pcm_cpuid(0xa, cpuinfo); perfmon_version = extract_bits_ui(cpuinfo.array[0], 0, 7); core_gen_counter_num_max = extract_bits_ui(cpuinfo.array[0], 8, 15); core_gen_counter_width = extract_bits_ui(cpuinfo.array[0], 16, 23); if (perfmon_version > 1) { core_fixed_counter_num_max = extract_bits_ui(cpuinfo.array[3], 0, 4); core_fixed_counter_width = extract_bits_ui(cpuinfo.array[3], 5, 12); } else if (1 == perfmon_version) { core_fixed_counter_num_max = 3; core_fixed_counter_width = core_gen_counter_width; } if (isForceRTMAbortModeAvailable()) { uint64 TSXForceAbort = 0; if (MSR.empty()) { if (complainAboutMSR) { std::cerr << "PCM Error: Can't determine the number of available counters reliably because of no access to MSR.\n"; } } else if (MSR[0]->read(MSR_TSX_FORCE_ABORT, &TSXForceAbort) == sizeof(uint64)) { TSXForceAbort &= 1; /* TSXForceAbort is 0 (default mode) => the number of useful gen counters is 3 TSXForceAbort is 1 => the number of gen counters is unchanged */ if (TSXForceAbort == 0) { core_gen_counter_num_max = 3; } } else { std::cerr << "PCM Error: Can't determine the number of available counters reliably because reading MSR_TSX_FORCE_ABORT failed.\n"; } } #if defined(__linux__) const auto env = std::getenv("PCM_NO_AWS_WORKAROUND"); auto aws_workaround = true; if (env != nullptr && std::string(env) == std::string("1")) { aws_workaround = false; } if (aws_workaround == true && vm == true && linux_arch_perfmon == true && core_gen_counter_num_max > 3) { core_gen_counter_num_max = 3; std::cerr << "INFO: Reducing the number of programmable counters to 3 to workaround the fixed cycle counter virtualization issue on AWS.\n"; std::cerr << " You can disable the workaround by setting PCM_NO_AWS_WORKAROUND=1 environment variable\n"; } if (isNMIWatchdogEnabled(true) && keepNMIWatchdogEnabled()) { --core_gen_counter_num_max; std::cerr << "INFO: Reducing the number of programmable counters to " << core_gen_counter_num_max << " because NMI watchdog is enabled.\n"; } #endif } } bool PCM::isFixedCounterSupported(unsigned c) { if (max_cpuid >= 0xa) { PCM_CPUID_INFO cpuinfo; pcm_cpuid(0xa, cpuinfo); return extract_bits_ui(cpuinfo.reg.ecx, c, c) || (extract_bits_ui(cpuinfo.reg.edx, 4, 0) > c); } return false; } bool PCM::isHWTMAL1Supported() const { #ifdef PCM_USE_PERF if (perfEventTaskHandle.empty() == false) { return false; // per PID/task perf collection does not support HW TMA L1 } #endif static int supported = -1; if (supported < 0) { supported = 0; PCM_CPUID_INFO cpuinfo; pcm_cpuid(1, cpuinfo); if (extract_bits_ui(cpuinfo.reg.ecx, 15, 15) && MSR.size()) { uint64 perf_cap; if (MSR[0]->read(MSR_PERF_CAPABILITIES, &perf_cap) == sizeof(uint64)) { supported = (int)extract_bits(perf_cap, 15, 15); } } if (hybrid) { supported = 0; } } return supported > 0; } void PCM::readCPUMicrocodeLevel() { if (MSR.empty()) return; const int ref_core = 0; TemporalThreadAffinity affinity(ref_core); if (affinity.supported() && isCoreOnline(ref_core)) { // see "Update Signature and Verification" and "Determining the Signature" // sections in Intel SDM how to read ucode level if (MSR[ref_core]->write(MSR_IA32_BIOS_SIGN_ID, 0) == sizeof(uint64)) { PCM_CPUID_INFO cpuinfo; pcm_cpuid(1, cpuinfo); // cpuid instructions updates MSR_IA32_BIOS_SIGN_ID uint64 result = 0; if (MSR[ref_core]->read(MSR_IA32_BIOS_SIGN_ID, &result) == sizeof(uint64)) { cpu_microcode_level = result >> 32; } } } } int32 PCM::getMaxCustomCoreEvents() { return core_gen_counter_num_max; } int PCM::getCPUModelFromCPUID() { static int result = -1; if (result < 0) { PCM_CPUID_INFO cpuinfo; pcm_cpuid(1, cpuinfo); result = (((cpuinfo.array[0]) & 0xf0) >> 4) | ((cpuinfo.array[0] & 0xf0000) >> 12); } return result; } bool PCM::detectModel() { char buffer[1024]; union { char cbuf[16]; int ibuf[16 / sizeof(int)]; } buf; PCM_CPUID_INFO cpuinfo; pcm_cpuid(0, cpuinfo); std::fill(buffer, buffer + 1024, 0); std::fill(buf.cbuf, buf.cbuf + 16, 0); buf.ibuf[0] = cpuinfo.array[1]; buf.ibuf[1] = cpuinfo.array[3]; buf.ibuf[2] = cpuinfo.array[2]; if (strncmp(buf.cbuf, "GenuineIntel", 4 * 3) != 0) { std::cerr << getUnsupportedMessage() << "\n"; return false; } max_cpuid = cpuinfo.array[0]; pcm_cpuid(1, cpuinfo); cpu_family = (((cpuinfo.array[0]) >> 8) & 0xf) | ((cpuinfo.array[0] & 0xf00000) >> 16); cpu_model = (((cpuinfo.array[0]) & 0xf0) >> 4) | ((cpuinfo.array[0] & 0xf0000) >> 12); cpu_stepping = cpuinfo.array[0] & 0x0f; if (cpuinfo.reg.ecx & (1UL << 31UL)) { vm = true; std::cerr << "Detected a hypervisor/virtualization technology. Some metrics might not be available due to configuration or availability of virtual hardware features.\n"; } readCoreCounterConfig(); if (cpu_family != 6) { std::cerr << getUnsupportedMessage() << " CPU Family: " << cpu_family << "\n"; return false; } pcm_cpuid(7, 0, cpuinfo); std::cerr << "\n===== Processor information =====\n"; #ifdef __linux__ auto checkLinuxCpuinfoFlag = [](const std::string& flag) -> bool { std::ifstream linuxCpuinfo("/proc/cpuinfo"); if (linuxCpuinfo.is_open()) { std::string line; while (std::getline(linuxCpuinfo, line)) { auto tokens = split(line, ':'); if (tokens.size() >= 2 && tokens[0].find("flags") == 0) { for (const auto & curFlag : split(tokens[1], ' ')) { if (flag == curFlag) { return true; } } } } linuxCpuinfo.close(); } return false; }; linux_arch_perfmon = checkLinuxCpuinfoFlag("arch_perfmon"); std::cerr << "Linux arch_perfmon flag : " << (linux_arch_perfmon ? "yes" : "no") << "\n"; if (vm == true && linux_arch_perfmon == false) { std::cerr << "ERROR: vPMU is not enabled in the hypervisor. Please see details in https://software.intel.com/content/www/us/en/develop/documentation/vtune-help/top/set-up-analysis-target/on-virtual-machine.html \n"; std::cerr << " you can force-continue by setting PCM_IGNORE_ARCH_PERFMON=1 environment variable.\n"; auto env = std::getenv("PCM_IGNORE_ARCH_PERFMON"); auto ignore_arch_perfmon = false; if (env != nullptr && std::string(env) == std::string("1")) { ignore_arch_perfmon = true; } if (!ignore_arch_perfmon) { return false; } } #endif hybrid = (cpuinfo.reg.edx & (1 << 15)) ? true : false; std::cerr << "Hybrid processor : " << (hybrid ? "yes" : "no") << "\n"; std::cerr << "IBRS and IBPB supported : " << ((cpuinfo.reg.edx & (1 << 26)) ? "yes" : "no") << "\n"; std::cerr << "STIBP supported : " << ((cpuinfo.reg.edx & (1 << 27)) ? "yes" : "no") << "\n"; std::cerr << "Spec arch caps supported : " << ((cpuinfo.reg.edx & (1 << 29)) ? "yes" : "no") << "\n"; std::cerr << "Max CPUID level : " << max_cpuid << "\n"; std::cerr << "CPU model number : " << cpu_model << "\n"; return true; } bool PCM::isRDTDisabled() const { static int flag = -1; if (flag < 0) { // flag not yet initialized const char * varname = "PCM_NO_RDT"; char* env = nullptr; #ifdef _MSC_VER _dupenv_s(&env, NULL, varname); #else env = std::getenv(varname); #endif if (env != nullptr && std::string(env) == std::string("1")) { std::cout << "Disabling RDT usage because PCM_NO_RDT=1 environment variable is set.\n"; flag = 1; } else { flag = 0; } #ifdef _MSC_VER free(env); #endif } return flag > 0; } bool PCM::QOSMetricAvailable() const { if (isRDTDisabled()) return false; #ifndef __linux__ if (isSecureBoot()) return false; #endif PCM_CPUID_INFO cpuinfo; pcm_cpuid(0x7,0,cpuinfo); return (cpuinfo.reg.ebx & (1<<12))?true:false; } bool PCM::L3QOSMetricAvailable() const { if (isRDTDisabled()) return false; #ifndef __linux__ if (isSecureBoot()) return false; #endif PCM_CPUID_INFO cpuinfo; pcm_cpuid(0xf,0,cpuinfo); return (cpuinfo.reg.edx & (1<<1))?true:false; } bool PCM::L3CacheOccupancyMetricAvailable() const { PCM_CPUID_INFO cpuinfo; if (!(QOSMetricAvailable() && L3QOSMetricAvailable())) return false; pcm_cpuid(0xf,0x1,cpuinfo); return (cpuinfo.reg.edx & 1)?true:false; } bool PCM::CoreLocalMemoryBWMetricAvailable() const { if (cpu_model == SKX && cpu_stepping < 5) return false; // SKZ4 errata PCM_CPUID_INFO cpuinfo; if (!(QOSMetricAvailable() && L3QOSMetricAvailable())) return false; pcm_cpuid(0xf,0x1,cpuinfo); return (cpuinfo.reg.edx & 2)?true:false; } bool PCM::CoreRemoteMemoryBWMetricAvailable() const { if (cpu_model == SKX && cpu_stepping < 5) return false; // SKZ4 errata PCM_CPUID_INFO cpuinfo; if (!(QOSMetricAvailable() && L3QOSMetricAvailable())) return false; pcm_cpuid(0xf, 0x1, cpuinfo); return (cpuinfo.reg.edx & 4) ? true : false; } unsigned PCM::getMaxRMID() const { unsigned maxRMID = 0; PCM_CPUID_INFO cpuinfo; pcm_cpuid(0xf,0,cpuinfo); maxRMID = (unsigned)cpuinfo.reg.ebx + 1; return maxRMID; } void PCM::initRDT() { if (!(QOSMetricAvailable() && L3QOSMetricAvailable())) return; #ifdef __linux__ auto env = std::getenv("PCM_USE_RESCTRL"); if (env != nullptr && std::string(env) == std::string("1")) { std::cerr << "INFO: using Linux resctrl driver for RDT metrics (L3OCC, LMB, RMB) because environment variable PCM_USE_RESCTRL=1\n"; resctrl.init(); useResctrl = true; return; } if (resctrl.isMounted()) { std::cerr << "INFO: using Linux resctrl driver for RDT metrics (L3OCC, LMB, RMB) because resctrl driver is mounted.\n"; resctrl.init(); useResctrl = true; return; } if (isSecureBoot()) { std::cerr << "INFO: using Linux resctrl driver for RDT metrics (L3OCC, LMB, RMB) because Secure Boot mode is enabled.\n"; resctrl.init(); useResctrl = true; return; } #endif std::cerr << "Initializing RMIDs" << std::endl; unsigned maxRMID; /* Calculate maximum number of RMID supported by socket */ maxRMID = getMaxRMID(); // std::cout << "Maximum RMIDs per socket in the system : " << maxRMID << "\n"; std::vector rmid(num_sockets); for(int32 i = 0; i < num_sockets; i ++) rmid[i] = maxRMID - 1; /* Associate each core with 1 RMID */ for(int32 core = 0; core < num_cores; core ++ ) { if(!isCoreOnline(core)) continue; uint64 msr_pqr_assoc = 0 ; uint64 msr_qm_evtsel = 0 ; MSR[core]->lock(); //Read 0xC8F MSR for each core MSR[core]->read(IA32_PQR_ASSOC, &msr_pqr_assoc); //std::cout << "initRMID reading IA32_PQR_ASSOC 0x" << std::hex << msr_pqr_assoc << std::dec << "\n"; //std::cout << "Socket Id : " << topology[core].socket; msr_pqr_assoc &= 0xffffffff00000000ULL; msr_pqr_assoc |= (uint64)(rmid[topology[core].socket] & ((1ULL<<10)-1ULL)); //std::cout << "initRMID writing IA32_PQR_ASSOC 0x" << std::hex << msr_pqr_assoc << std::dec << "\n"; //Write 0xC8F MSR with new RMID for each core MSR[core]->write(IA32_PQR_ASSOC,msr_pqr_assoc); msr_qm_evtsel = static_cast(rmid[topology[core].socket] & ((1ULL<<10)-1ULL)); msr_qm_evtsel <<= 32; //Write 0xC8D MSR with new RMID for each core //std::cout << "initRMID writing IA32_QM_EVTSEL 0x" << std::hex << msr_qm_evtsel << std::dec << "\n"; MSR[core]->write(IA32_QM_EVTSEL,msr_qm_evtsel); MSR[core]->unlock(); /* Initializing the memory bandwidth counters */ if (CoreLocalMemoryBWMetricAvailable()) { memory_bw_local.push_back(std::make_shared(new CounterWidthExtender::MBLCounter(MSR[core]), 24, 1000)); if (CoreRemoteMemoryBWMetricAvailable()) { memory_bw_total.push_back(std::make_shared(new CounterWidthExtender::MBTCounter(MSR[core]), 24, 1000)); } } rmid[topology[core].socket] --; //std::cout << std::flush; // Explicitly flush after each iteration } /* Get The scaling factor by running CPUID.0xF.0x1 instruction */ L3ScalingFactor = getL3ScalingFactor(); } void PCM::initQOSevent(const uint64 event, const int32 core) { if(!isCoreOnline(core)) return; uint64 msr_qm_evtsel = 0 ; //Write 0xC8D MSR with the event id MSR[core]->read(IA32_QM_EVTSEL, &msr_qm_evtsel); //std::cout << "initQOSevent reading IA32_QM_EVTSEL 0x" << std::hex << msr_qm_evtsel << std::dec << "\n"; msr_qm_evtsel &= 0xfffffffffffffff0ULL; msr_qm_evtsel |= event & ((1ULL<<8)-1ULL); //std::cout << "initQOSevent writing IA32_QM_EVTSEL 0x" << std::hex << msr_qm_evtsel << std::dec << "\n"; MSR[core]->write(IA32_QM_EVTSEL,msr_qm_evtsel); //std::cout << std::flush; } void PCM::initCStateSupportTables() { #define PCM_PARAM_PROTECT(...) __VA_ARGS__ #define PCM_CSTATE_ARRAY(array_ , val ) \ { \ static uint64 tmp[] = val; \ PCM_COMPILE_ASSERT(sizeof(tmp) / sizeof(uint64) == (static_cast(MAX_C_STATE)+1)); \ array_ = tmp; \ break; \ } // fill package C state array switch(cpu_model) { case ATOM: case ATOM_2: case CENTERTON: case AVOTON: case BAYTRAIL: case CHERRYTRAIL: case APOLLO_LAKE: case GEMINI_LAKE: case DENVERTON: case ADL: case RPL: case SNOWRIDGE: PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x3F8, 0, 0x3F9, 0, 0x3FA, 0, 0, 0, 0 }) ); case NEHALEM_EP: case NEHALEM: case CLARKDALE: case WESTMERE_EP: case NEHALEM_EX: case WESTMERE_EX: PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0, 0, 0}) ); case SANDY_BRIDGE: case JAKETOWN: case IVY_BRIDGE: case IVYTOWN: PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x60D, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0, 0, 0}) ); case HASWELL: case HASWELL_2: case HASWELLX: case BDX_DE: case BDX: case KNL: PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x60D, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0, 0, 0}) ); case SKX: case ICX: case SPR: PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x60D, 0, 0, 0, 0x3F9, 0, 0, 0, 0}) ); case HASWELL_ULT: case BROADWELL: PCM_SKL_PATH_CASES case BROADWELL_XEON_E3: PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({0, 0, 0x60D, 0x3F8, 0, 0, 0x3F9, 0x3FA, 0x630, 0x631, 0x632}) ); default: std::cerr << "PCM error: package C-states support array is not initialized. Package C-states metrics will not be shown.\n"; PCM_CSTATE_ARRAY(pkgCStateMsr, PCM_PARAM_PROTECT({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) ); }; // fill core C state array switch(cpu_model) { case ATOM: case ATOM_2: case CENTERTON: PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) ); case NEHALEM_EP: case NEHALEM: case CLARKDALE: case WESTMERE_EP: case NEHALEM_EX: case WESTMERE_EX: PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0x3FC, 0, 0, 0x3FD, 0, 0, 0, 0}) ); case SANDY_BRIDGE: case JAKETOWN: case IVY_BRIDGE: case IVYTOWN: case HASWELL: case HASWELL_2: case HASWELL_ULT: case HASWELLX: case BDX_DE: case BDX: case BROADWELL: case BROADWELL_XEON_E3: case BAYTRAIL: case AVOTON: case CHERRYTRAIL: case APOLLO_LAKE: case GEMINI_LAKE: case DENVERTON: PCM_SKL_PATH_CASES case ADL: case RPL: case SNOWRIDGE: case ICX: case SPR: PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0x3FC, 0, 0, 0x3FD, 0x3FE, 0, 0, 0}) ); case KNL: PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0, 0, 0, 0x3FF, 0, 0, 0, 0}) ); case SKX: PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({0, 0, 0, 0, 0, 0, 0x3FD, 0, 0, 0, 0}) ); default: std::cerr << "PCM error: core C-states support array is not initialized. Core C-states metrics will not be shown.\n"; PCM_CSTATE_ARRAY(coreCStateMsr, PCM_PARAM_PROTECT({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) ); }; } #ifdef __linux__ constexpr auto perfSlotsPath = "/sys/bus/event_source/devices/cpu/events/slots"; constexpr auto perfBadSpecPath = "/sys/bus/event_source/devices/cpu/events/topdown-bad-spec"; constexpr auto perfBackEndPath = "/sys/bus/event_source/devices/cpu/events/topdown-be-bound"; constexpr auto perfFrontEndPath = "/sys/bus/event_source/devices/cpu/events/topdown-fe-bound"; constexpr auto perfRetiringPath = "/sys/bus/event_source/devices/cpu/events/topdown-retiring"; bool perfSupportsTopDown() { static int yes = -1; if (-1 == yes) { const auto slots = readSysFS(perfSlotsPath, true); const auto bad = readSysFS(perfBadSpecPath, true); const auto be = readSysFS(perfBackEndPath, true); const auto fe = readSysFS(perfFrontEndPath, true); const auto ret = readSysFS(perfRetiringPath, true); yes = (slots.size() && bad.size() && be.size() && fe.size() && ret.size()) ? 1 : 0; } return 1 == yes; } #endif const std::vector qat_evtsel_mapping = { { "sample_cnt" }, //0x0 { "pci_trans_cnt" }, //0x1 { "max_rd_lat" }, //0x2 { "rd_lat_acc_avg" }, //0x3 { "max_lat" }, //0x4 { "lat_acc_avg" }, //0x5 { "bw_in" }, //0x6 { "bw_out" }, //0x7 { "at_page_req_lat_acc_avg" }, //0x8 { "at_trans_lat_acc_avg" }, //0x9 { "at_max_tlb_used" }, //0xA { "util_cpr0" }, //0xB { "util_dcpr0" }, //0xC { "util_dcpr1" }, //0xD { "util_dcpr2" }, //0xE { "util_xlt0" }, //0xF { "util_xlt1" }, //0x10 { "util_cph0" }, //0x11 { "util_cph1" }, //0x12 { "util_cph2" }, //0x13 { "util_cph3" }, //0x14 { "util_cph4" }, //0x15 { "util_cph5" }, //0x16 { "util_cph6" }, //0x17 { "util_cph7" }, //0x18 { "util_ath0" }, //0x19 { "util_ath1" }, //0x1A { "util_ath2" }, //0x1B { "util_ath3" }, //0x1C { "util_ath4" }, //0x1D { "util_ath5" }, //0x1E { "util_ath6" }, //0x1F { "util_ath7" }, //0x20 { "util_ucs0" }, //0x21 { "util_ucs1" }, //0x22 { "util_ucs2" }, //0x23 { "util_ucs3" }, //0x24 { "util_pke0" }, //0x25 { "util_pke1" }, //0x26 { "util_pke2" }, //0x27 { "util_pke3" }, //0x28 { "util_pke4" }, //0x29 { "util_pke5" }, //0x2A { "util_pke6" }, //0x2B { "util_pke7" }, //0x2C { "util_pke8" }, //0x2D { "util_pke9" }, //0x2E { "util_pke10" }, //0x2F { "util_pke11" }, //0x30 { "util_pke12" }, //0x31 { "util_pke13" }, //0x32 { "util_pke14" }, //0x33 { "util_pke15" }, //0x34 { "util_pke16" }, //0x35 { "util_pke17" }, //0x36 { "unknown" } //0x37 }; class VirtualDummyRegister : public HWRegister { uint64 lastValue; public: VirtualDummyRegister() : lastValue(0) {} void operator = (uint64 val) override { lastValue = val; } operator uint64 () override { return lastValue; } }; class QATTelemetryVirtualGeneralConfigRegister : public HWRegister { friend class QATTelemetryVirtualCounterRegister; int domain, b, d, f; PCM::IDX_OPERATION operation; PCM::IDX_STATE state; std::unordered_map data_cache; //data cache public: QATTelemetryVirtualGeneralConfigRegister(int domain_, int b_, int d_, int f_) : domain(domain_), b(b_), d(d_), f(f_), operation(PCM::QAT_TLM_STOP), state(PCM::IDX_STATE_OFF) { } void operator = (uint64 val) override { operation = PCM::IDX_OPERATION(val); #ifdef __linux__ std::ostringstream sysfs_path(std::ostringstream::out); switch (operation) { case PCM::QAT_TLM_START: //enable state = PCM::IDX_STATE_ON; // falls through case PCM::QAT_TLM_STOP: //disable if (state == PCM::IDX_STATE_ON) { //std::cerr << "QAT telemetry operation = " << operation << ".\n"; sysfs_path << std::string("/sys/bus/pci/devices/") << std::hex << std::setw(4) << std::setfill('0') << domain << ":" << std::hex << std::setw(2) << std::setfill('0') << b << ":" << std::hex << std::setw(2) << std::setfill('0') << d << "." << std::hex << f << "/telemetry/control"; if (writeSysFS(sysfs_path.str().c_str(), (operation == PCM::QAT_TLM_START ? "1" : "0")) == false) { std::cerr << "Linux sysfs: Error on control QAT telemetry operation = " << operation << ".\n"; } } break; case PCM::QAT_TLM_REFRESH: //refresh data if (state == PCM::IDX_STATE_ON) { //std::cerr << "QAT telemetry operation = " << operation << ".\n"; sysfs_path << std::string("/sys/bus/pci/devices/") << std::hex << std::setw(4) << std::setfill('0') << domain << ":" << std::hex << std::setw(2) << std::setfill('0') << b << ":" << std::hex << std::setw(2) << std::setfill('0') << d << "." << std::hex << f << "/telemetry/device_data"; data_cache.clear(); readMapFromSysFS(sysfs_path.str().c_str(), data_cache); } break; default: break; } #endif } operator uint64 () override { return operation; } ~QATTelemetryVirtualGeneralConfigRegister() { //std::cerr << "~QATTelemetryVirtualGeneralConfigRegister.\n" << std::flush; } }; class QATTelemetryVirtualControlRegister : public HWRegister { friend class QATTelemetryVirtualCounterRegister; uint64 event; public: QATTelemetryVirtualControlRegister() : event(0x0) { } void operator = (uint64 val) override { event = extract_bits(val, 32, 59); } operator uint64 () override { return event; } }; class QATTelemetryVirtualCounterRegister : public HWRegister { std::shared_ptr gConfigReg; std::shared_ptr controlReg; int ctr_id; public: QATTelemetryVirtualCounterRegister( std::shared_ptr gConfigReg_, std::shared_ptr controlReg_, int ctr_id_) : gConfigReg(gConfigReg_), controlReg(controlReg_), ctr_id(ctr_id_) { } void operator = (uint64 /* val */) override { // no-op } operator uint64 () override { uint64 result = 0; uint32 eventsel = controlReg->event; if (eventsel < qat_evtsel_mapping.size()) { std::string key = qat_evtsel_mapping[eventsel]; if (gConfigReg->data_cache.find(key) != gConfigReg->data_cache.end()) { result = gConfigReg->data_cache.at(key); } } //std::cerr << std::hex << "QAT-CTR(0x" << ctr_id << "), key="<< key << ", val=0x" << std::hex << result << ".\n" << std::dec; return result; } }; bool PCM::discoverSystemTopology() { typedef std::map socketIdMap_type; socketIdMap_type socketIdMap; PCM_CPUID_INFO cpuid_args; // init constants for CPU topology leaf 0xB // adapted from Topology Enumeration Reference code for Intel 64 Architecture // https://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration int wasCoreReported = 0, wasThreadReported = 0; int subleaf = 0, levelType, levelShift; //uint32 coreSelectMask = 0, smtSelectMask = 0; uint32 smtMaskWidth = 0; //uint32 pkgSelectMask = (-1), pkgSelectMaskShift = 0; uint32 corePlusSMTMaskWidth = 0; uint32 coreMaskWidth = 0; { TemporalThreadAffinity aff0(0); do { pcm_cpuid(0xb, subleaf, cpuid_args); if (cpuid_args.array[1] == 0) { // if EBX ==0 then this subleaf is not valid, we can exit the loop break; } levelType = extract_bits_ui(cpuid_args.array[2], 8, 15); levelShift = extract_bits_ui(cpuid_args.array[0], 0, 4); switch (levelType) { case 1: //level type is SMT, so levelShift is the SMT_Mask_Width smtMaskWidth = levelShift; wasThreadReported = 1; break; case 2: //level type is Core, so levelShift is the CorePlusSMT_Mask_Width corePlusSMTMaskWidth = levelShift; wasCoreReported = 1; break; default: break; } subleaf++; } while (1); } if (wasThreadReported && wasCoreReported) { coreMaskWidth = corePlusSMTMaskWidth - smtMaskWidth; } else if (!wasCoreReported && wasThreadReported) { coreMaskWidth = smtMaskWidth; } else { std::cerr << "ERROR: Major problem? No leaf 0 under cpuid function 11.\n"; return false; } (void) coreMaskWidth; // to suppress warnings on MacOS (unused vars) uint32 l2CacheMaskShift = 0; #ifdef PCM_DEBUG_TOPOLOGY uint32 threadsSharingL2; #endif uint32 l2CacheMaskWidth; pcm_cpuid(0x4, 2, cpuid_args); // get ID for L2 cache l2CacheMaskWidth = 1 + extract_bits_ui(cpuid_args.array[0],14,25); // number of APIC IDs sharing L2 cache #ifdef PCM_DEBUG_TOPOLOGY threadsSharingL2 = l2CacheMaskWidth; #endif for( ; l2CacheMaskWidth > 1; l2CacheMaskWidth >>= 1) { l2CacheMaskShift++; } #ifdef PCM_DEBUG_TOPOLOGY std::cerr << "DEBUG: Number of threads sharing L2 cache = " << threadsSharingL2 << " [the most significant bit = " << l2CacheMaskShift << "]\n"; #endif #ifndef __APPLE__ auto populateEntry = [&smtMaskWidth, &coreMaskWidth, &l2CacheMaskShift](TopologyEntry & entry, const int apic_id) { entry.thread_id = smtMaskWidth ? extract_bits_ui(apic_id, 0, smtMaskWidth - 1) : 0; entry.core_id = (smtMaskWidth + coreMaskWidth) ? extract_bits_ui(apic_id, smtMaskWidth, smtMaskWidth + coreMaskWidth - 1) : 0; entry.socket = extract_bits_ui(apic_id, smtMaskWidth + coreMaskWidth, 31); entry.tile_id = extract_bits_ui(apic_id, l2CacheMaskShift, 31); }; #endif auto populateHybridEntry = [this](TopologyEntry& entry, int core) -> bool { if (hybrid == false) return true; PCM_CPUID_INFO cpuid_args; #if defined(__FreeBSD__) || defined(__DragonFly__) pcm_cpuid_bsd(0x1a, cpuid_args, core); #elif defined (_MSC_VER) || defined(__linux__) pcm_cpuid(0x1a, 0x0, cpuid_args); (void)core; #else std::cerr << "PCM Error: Hybrid processors are not supported for your OS\n"; (void)core; return false; #endif entry.native_cpu_model = extract_bits_ui(cpuid_args.reg.eax, 0, 23); entry.core_type = (TopologyEntry::CoreType) extract_bits_ui(cpuid_args.reg.eax, 24, 31); return true; }; #ifdef _MSC_VER // version for Windows 7 and later version char * slpi = new char[sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)]; DWORD len = (DWORD)sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX); BOOL res = GetLogicalProcessorInformationEx(RelationAll, (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)slpi, &len); while (res == FALSE) { delete[] slpi; if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { slpi = new char[len]; res = GetLogicalProcessorInformationEx(RelationAll, (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)slpi, &len); } else { tcerr << "Error in Windows function 'GetLogicalProcessorInformationEx': " << GetLastError() << " "; const TCHAR * strError = _com_error(GetLastError()).ErrorMessage(); if (strError) tcerr << strError; tcerr << "\n"; return false; } } char * base_slpi = slpi; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX pi = NULL; for ( ; slpi < base_slpi + len; slpi += (DWORD)pi->Size) { pi = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)slpi; if (pi->Relationship == RelationProcessorCore) { threads_per_core = (pi->Processor.Flags == LTP_PC_SMT) ? 2 : 1; // std::cout << "thr per core: " << threads_per_core << "\n"; num_cores += threads_per_core; } } // std::cout << std::flush; num_online_cores = num_cores; if (num_cores != GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)) { std::cerr << "Error in processor group size counting: " << num_cores << "!=" << GetActiveProcessorCount(ALL_PROCESSOR_GROUPS) << "\n"; std::cerr << "Make sure your binary is compiled for 64-bit: using 'x64' platform configuration.\n"; return false; } for (int i = 0; i < (int)num_cores; i++) { ThreadGroupTempAffinity affinity(i); pcm_cpuid(0xb, 0x0, cpuid_args); int apic_id = cpuid_args.array[3]; TopologyEntry entry; entry.os_id = i; populateEntry(entry, apic_id); if (populateHybridEntry(entry, i) == false) { return false; } topology.push_back(entry); socketIdMap[entry.socket] = 0; } delete[] base_slpi; #else // for Linux, Mac OS, FreeBSD and DragonFlyBSD TopologyEntry entry; #ifdef __linux__ num_cores = readMaxFromSysFS("/sys/devices/system/cpu/present"); if(num_cores == -1) { std::cerr << "Cannot read number of present cores\n"; return false; } ++num_cores; // open /proc/cpuinfo FILE * f_cpuinfo = fopen("/proc/cpuinfo", "r"); if (!f_cpuinfo) { std::cerr << "Cannot open /proc/cpuinfo file.\n"; return false; } // map with key=pkg_apic_id (not necessarily zero based or sequential) and // associated value=socket_id that should be 0 based and sequential std::map found_pkg_ids; topology.resize(num_cores); char buffer[1024]; while (0 != fgets(buffer, 1024, f_cpuinfo)) { if (strncmp(buffer, "processor", sizeof("processor") - 1) == 0) { pcm_sscanf(buffer) >> s_expect("processor\t: ") >> entry.os_id; //std::cout << "os_core_id: " << entry.os_id << "\n"; try { TemporalThreadAffinity _(entry.os_id); pcm_cpuid(0xb, 0x0, cpuid_args); int apic_id = cpuid_args.array[3]; populateEntry(entry, apic_id); if (populateHybridEntry(entry, entry.os_id) == false) { return false; } topology[entry.os_id] = entry; socketIdMap[entry.socket] = 0; ++num_online_cores; } catch (std::exception &) { std::cerr << "Marking core " << entry.os_id << " offline\n"; } } } //std::cout << std::flush; fclose(f_cpuinfo); #elif defined(__FreeBSD__) || defined(__DragonFly__) size_t size = sizeof(num_cores); if(0 != sysctlbyname("hw.ncpu", &num_cores, &size, NULL, 0)) { std::cerr << "Unable to get hw.ncpu from sysctl.\n"; return false; } num_online_cores = num_cores; if (modfind("cpuctl") == -1) { std::cerr << "cpuctl(4) not loaded.\n"; return false; } for (int i = 0; i < num_cores; i++) { pcm_cpuid_bsd(0xb, cpuid_args, i); int apic_id = cpuid_args.array[3]; entry.os_id = i; populateEntry(entry, apic_id); if (populateHybridEntry(entry, i) == false) { return false; } if (entry.socket == 0 && entry.core_id == 0) ++threads_per_core; topology.push_back(entry); socketIdMap[entry.socket] = 0; } #else // Getting processor info for Mac OS #define SAFE_SYSCTLBYNAME(message, ret_value) \ { \ size_t size; \ char *pParam; \ if(0 != sysctlbyname(message, NULL, &size, NULL, 0)) \ { \ std::cerr << "Unable to determine size of " << message << " sysctl return type.\n"; \ return false; \ } \ if(NULL == (pParam = (char *)malloc(size))) \ { \ std::cerr << "Unable to allocate memory for " << message << "\n"; \ return false; \ } \ if(0 != sysctlbyname(message, (void*)pParam, &size, NULL, 0)) \ { \ std::cerr << "Unable to get " << message << " from sysctl.\n"; \ return false; \ } \ ret_value = convertUnknownToInt(size, pParam); \ free(pParam); \ } // End SAFE_SYSCTLBYNAME // Using OSXs sysctl to get the number of CPUs right away SAFE_SYSCTLBYNAME("hw.logicalcpu", num_cores) num_online_cores = num_cores; #undef SAFE_SYSCTLBYNAME // The OSX version needs the MSR handle earlier so that it can build the CPU topology. // This topology functionality should potentially go into a different KEXT for(int i = 0; i < num_cores; i++) { MSR.push_back(std::make_shared(i)); } TopologyEntry entries[num_cores]; if (MSR[0]->buildTopology(num_cores, entries) != 0) { std::cerr << "Unable to build CPU topology" << std::endl; return false; } for(int i = 0; i < num_cores; i++){ socketIdMap[entries[i].socket] = 0; if(entries[i].os_id >= 0) { if(entries[i].core_id == 0 && entries[i].socket == 0) ++threads_per_core; if (populateHybridEntry(entries[i], i) == false) { return false; } topology.push_back(entries[i]); } } // End of OSX specific code #endif #endif //end of ifdef _MSC_VER if(num_cores == 0) { num_cores = (int32)topology.size(); } if(num_sockets == 0) { num_sockets = (int32)(std::max)(socketIdMap.size(), (size_t)1); } socketIdMap_type::iterator s = socketIdMap.begin(); for (uint32 sid = 0; s != socketIdMap.end(); ++s) { s->second = sid++; // first is apic id, second is logical socket id systemTopology->addSocket( s->first, s->second ); } for (int32 cid = 0; cid < num_cores; ++cid) { //std::cerr << "Cid: " << cid << "\n"; systemTopology->addThread( cid, topology[cid] ); } // All threads are here now so we can set the refCore for a socket for ( auto& socket : systemTopology->sockets() ) socket->setRefCore(); // use map to change apic socket id to the logical socket id for (int i = 0; (i < (int)num_cores) && (!socketIdMap.empty()); ++i) { if(isCoreOnline((int32)i)) topology[i].socket = socketIdMap[topology[i].socket]; } #if 0 std::cerr << "Number of socket ids: " << socketIdMap.size() << "\n"; std::cerr << "Topology:\nsocket os_id core_id\n"; for (int i = 0; i < num_cores; ++i) { std::cerr << topology[i].socket << " " << topology[i].os_id << " " << topology[i].core_id << "\n"; } #endif if (threads_per_core == 0) { for (int i = 0; i < (int)num_cores; ++i) { if (topology[i].socket == topology[0].socket && topology[i].core_id == topology[0].core_id) ++threads_per_core; } assert(threads_per_core != 0); } if(num_phys_cores_per_socket == 0 && num_cores == num_online_cores) num_phys_cores_per_socket = num_cores / num_sockets / threads_per_core; if(num_online_cores == 0) num_online_cores = num_cores; int32 i = 0; socketRefCore.resize(num_sockets, -1); for(i = 0; i < num_cores; ++i) { if(isCoreOnline(i)) { socketRefCore[topology[i].socket] = i; } } num_online_sockets = 0; for(i = 0; i < num_sockets; ++i) { if(isSocketOnline(i)) { ++num_online_sockets; } } FrontendBoundSlots.resize(num_cores, 0); BadSpeculationSlots.resize(num_cores, 0); BackendBoundSlots.resize(num_cores, 0); RetiringSlots.resize(num_cores, 0); AllSlotsRaw.resize(num_cores, 0); #if 0 std::cerr << "Socket reference cores:\n"; for(int32 i=0; i< num_sockets;++i) { std::cerr << "socketRefCore[" << i << "]=" << socketRefCore[i] << "\n"; } #endif return true; } void PCM::printSystemTopology() const { const bool all_cores_online_no_hybrid = (num_cores == num_online_cores && hybrid == false); if (all_cores_online_no_hybrid) { std::cerr << "Number of physical cores: " << (num_cores/threads_per_core) << "\n"; } std::cerr << "Number of logical cores: " << num_cores << "\n"; std::cerr << "Number of online logical cores: " << num_online_cores << "\n"; if (all_cores_online_no_hybrid) { std::cerr << "Threads (logical cores) per physical core: " << threads_per_core << "\n"; } else { std::cerr << "Threads (logical cores) per physical core: " << threads_per_core << " (maybe imprecise due to core offlining/hybrid CPU)\n"; std::cerr << "Offlined cores: "; for (int i = 0; i < (int)num_cores; ++i) if(isCoreOnline((int32)i) == false) std::cerr << i << " "; std::cerr << "\n"; } std::cerr << "Num sockets: " << num_sockets << "\n"; if (all_cores_online_no_hybrid) { std::cerr << "Physical cores per socket: " << num_phys_cores_per_socket << "\n"; } else { std::cerr << "Physical cores per socket: " << num_cores / num_sockets / threads_per_core << " (maybe imprecise due to core offlining/hybrid CPU)\n"; } if (hybrid == false) { std::cerr << "Last level cache slices per socket: " << getMaxNumOfCBoxes() << "\n"; } std::cerr << "Core PMU (perfmon) version: " << perfmon_version << "\n"; std::cerr << "Number of core PMU generic (programmable) counters: " << core_gen_counter_num_max << "\n"; std::cerr << "Width of generic (programmable) counters: " << core_gen_counter_width << " bits\n"; if (perfmon_version > 0) { std::cerr << "Number of core PMU fixed counters: " << core_fixed_counter_num_max << "\n"; std::cerr << "Width of fixed counters: " << core_fixed_counter_width << " bits\n"; } if (perfmon_version < 2 && vm == true) { std::cerr << "Warning: detected an unsupported virtualized environment: the hypervisor has limited the core PMU (perfmon) version to " << perfmon_version << "\n"; } } bool PCM::initMSR() { #ifndef __APPLE__ try { for (int i = 0; i < (int)num_cores; ++i) { if ( isCoreOnline( (int32)i ) ) { MSR.push_back(std::make_shared(i)); systemTopology->addMSRHandleToOSThread( MSR.back(), (uint32)i ); } else { // the core is offlined, assign an invalid MSR handle MSR.push_back(std::make_shared()); systemTopology->addMSRHandleToOSThread( MSR.back(), (uint32)i ); } } } catch (...) { // failed MSR.clear(); std::cerr << "Can not access CPUs Model Specific Registers (MSRs).\n"; #ifdef _MSC_VER std::cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program.\n"; #elif defined(__linux__) std::cerr << "execute 'modprobe msr' as root user, then execute pcm as root user.\n"; #elif defined(__FreeBSD__) || defined(__DragonFly__) std::cerr << "Ensure cpuctl module is loaded and that you have read and write\n"; std::cerr << "permissions for /dev/cpuctl* devices (the 'chown' command can help).\n"; #endif return false; } #endif return true; } bool PCM::detectNominalFrequency() { if (MSR.size()) { if (max_cpuid >= 0x16) { PCM_CPUID_INFO cpuinfo; pcm_cpuid(0x16, cpuinfo); nominal_frequency = uint64(extract_bits_ui(cpuinfo.reg.eax, 0, 15)) * 1000000ULL;; } if (!nominal_frequency) { uint64 freq = 0; MSR[socketRefCore[0]]->read(PLATFORM_INFO_ADDR, &freq); const uint64 bus_freq = ( cpu_model == SANDY_BRIDGE || cpu_model == JAKETOWN || cpu_model == IVYTOWN || cpu_model == HASWELLX || cpu_model == BDX_DE || cpu_model == BDX || cpu_model == IVY_BRIDGE || cpu_model == HASWELL || cpu_model == BROADWELL || cpu_model == AVOTON || cpu_model == APOLLO_LAKE || cpu_model == GEMINI_LAKE || cpu_model == DENVERTON || useSKLPath() || cpu_model == SNOWRIDGE || cpu_model == KNL || cpu_model == ADL || cpu_model == RPL || cpu_model == SKX || cpu_model == ICX || cpu_model == SPR ) ? (100000000ULL) : (133333333ULL); nominal_frequency = ((freq >> 8) & 255) * bus_freq; } if(!nominal_frequency) nominal_frequency = get_frequency_from_cpuid(); if(!nominal_frequency) { computeNominalFrequency(); } if(!nominal_frequency) { std::cerr << "Error: Can not detect core frequency.\n"; destroyMSR(); return false; } #ifndef PCM_SILENT std::cerr << "Nominal core frequency: " << nominal_frequency << " Hz\n"; #endif } return true; } void PCM::initEnergyMonitoring() { if(packageEnergyMetricsAvailable() && MSR.size()) { uint64 rapl_power_unit = 0; MSR[socketRefCore[0]]->read(MSR_RAPL_POWER_UNIT,&rapl_power_unit); uint64 energy_status_unit = extract_bits(rapl_power_unit,8,12); if (cpu_model == PCM::CHERRYTRAIL || cpu_model == PCM::BAYTRAIL) joulesPerEnergyUnit = double(1ULL << energy_status_unit)/1000000.; // (2)^energy_status_unit microJoules else joulesPerEnergyUnit = 1./double(1ULL<read(MSR_PKG_POWER_INFO,&package_power_info); pkgThermalSpecPower = (int32) (double(extract_bits(package_power_info, 0, 14))*wattsPerPowerUnit); pkgMinimumPower = (int32) (double(extract_bits(package_power_info, 16, 30))*wattsPerPowerUnit); pkgMaximumPower = (int32) (double(extract_bits(package_power_info, 32, 46))*wattsPerPowerUnit); #ifndef PCM_SILENT std::cerr << "Package thermal spec power: " << pkgThermalSpecPower << " Watt; "; std::cerr << "Package minimum power: " << pkgMinimumPower << " Watt; "; std::cerr << "Package maximum power: " << pkgMaximumPower << " Watt;\n"; #endif int i = 0; if(energy_status.empty()) for (i = 0; i < (int)num_sockets; ++i) energy_status.push_back( std::make_shared( new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[i]], MSR_PKG_ENERGY_STATUS), 32, 10000)); if(dramEnergyMetricsAvailable() && dram_energy_status.empty()) for (i = 0; i < (int)num_sockets; ++i) dram_energy_status.push_back( std::make_shared( new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[i]], MSR_DRAM_ENERGY_STATUS), 32, 10000)); } } static const uint32 UBOX0_DEV_IDS[] = { 0x3451, 0x3251 }; std::vector > socket2UBOX0bus; void initSocket2Bus(std::vector > & socket2bus, uint32 device, uint32 function, const uint32 DEV_IDS[], uint32 devIdsSize); void initSocket2Ubox0Bus() { initSocket2Bus(socket2UBOX0bus, SERVER_UBOX0_REGISTER_DEV_ADDR, SERVER_UBOX0_REGISTER_FUNC_ADDR, UBOX0_DEV_IDS, (uint32)sizeof(UBOX0_DEV_IDS) / sizeof(UBOX0_DEV_IDS[0])); } bool initRootBusMap(std::map &rootbus_map) { bool mapped = false; static const uint32 MSM_DEV_IDS[] = { SPR_MSM_DEV_ID }; std::vector > socket2MSMbus; initSocket2Bus(socket2MSMbus, SPR_MSM_DEV_ADDR, SPR_MSM_FUNC_ADDR, MSM_DEV_IDS, (uint32)sizeof(MSM_DEV_IDS) / sizeof(MSM_DEV_IDS[0])); for (auto & s2bus : socket2MSMbus) { uint32 cpuBusValid = 0x0; int cpuBusPackageId; std::vector cpuBusNo; if (get_cpu_bus(s2bus.first, s2bus.second, SPR_MSM_DEV_ADDR, SPR_MSM_FUNC_ADDR, cpuBusValid, cpuBusNo, cpuBusPackageId) == false) return false; for (int cpuBusId = 0; cpuBusId < SPR_MSM_CPUBUSNO_MAX; ++cpuBusId) { if (!((cpuBusValid >> cpuBusId) & 0x1)) { //std::cout << "CPU bus " << cpuBusId << " is disabled on package " << cpuBusPackageId << std::endl; continue; } int rootBus = (cpuBusNo[(int)(cpuBusId / 4)] >> ((cpuBusId % 4) * 8)) & 0xff; rootbus_map[((s2bus.first << 8) | rootBus)] = cpuBusPackageId; //std::cout << "Mapped CPU bus #" << std::dec << cpuBusId << std::hex << " (domain=0x" << s2bus.first << " bus=0x" << rootBus << ") to " << std::dec << "package" << cpuBusPackageId << std::endl; } mapped = true; } return mapped; } #define SPR_IDX_ACCEL_COUNTER_MAX_NUM (8) #define SPR_QAT_ACCEL_COUNTER_MAX_NUM (16) struct idx_accel_dev_info { uint64 mem_bar; uint32 numa_node; uint32 socket_id; uint32 domain; uint32 bus; uint32 dev; uint32 func; }; bool getIDXDevBAR(std::vector > & socket2bus, uint32 dev, uint32 func, std::map &bus2socket, std::vector &idx_devs) { uint64 memBar = 0x0; uint32 pciCmd = 0x0, pmCsr= 0x0; uint32 numaNode = 0xff; struct idx_accel_dev_info idx_dev; for (auto & s2bus : socket2bus) { memBar = 0x0; pciCmd = 0x0; PciHandleType IDXHandle(s2bus.first, s2bus.second, dev, func); IDXHandle.read64(SPR_IDX_ACCEL_BAR0_OFFSET, &memBar); IDXHandle.read32(SPR_IDX_ACCEL_PCICMD_OFFSET, &pciCmd); IDXHandle.read32(SPR_IDX_ACCEL_PMCSR_OFFSET, &pmCsr); if (memBar == 0x0 || (pciCmd & 0x02) == 0x0) //Check BAR0 is valid or NOT. { std::cerr << "Warning: IDX - BAR0 of B:0x" << std::hex << s2bus.second << ",D:0x" << std::hex << dev << ",F:0x" << std::hex << func << " is invalid(memBar=0x" << std::hex << memBar << ", pciCmd=0x" << std::hex << pciCmd <<"), skipped." << std::dec << std::endl; continue; } if ((pmCsr & 0x03) == 0x3) //Check power state { std::cout << "Warning: IDX - Power state of B:0x" << std::hex << s2bus.second << ",D:0x" << std::hex << dev << ",F:0x" << std::hex << func \ << " is off, skipped." << std::endl; continue; } numaNode = 0xff; #ifdef __linux__ std::ostringstream devNumaNodePath(std::ostringstream::out); devNumaNodePath << std::string("/sys/bus/pci/devices/") << std::hex << std::setw(4) << std::setfill('0') << s2bus.first << ":" << std::hex << std::setw(2) << std::setfill('0') << s2bus.second << ":" << std::hex << std::setw(2) << std::setfill('0') << dev << "." << std::hex << func << "/numa_node"; const std::string devNumaNodeStr = readSysFS(devNumaNodePath.str().c_str(), true); if (devNumaNodeStr.size()) { numaNode = std::atoi(devNumaNodeStr.c_str()); if (numaNode == (std::numeric_limits::max)()) { numaNode = 0xff; //translate to special value for numa disable case. } } //std::cout << "IDX DEBUG: numa node file path=" << devNumaNodePath.str().c_str() << ", value=" << numaNode << std::endl; #endif idx_dev.mem_bar = memBar; idx_dev.numa_node = numaNode; idx_dev.socket_id = 0xff; idx_dev.domain = s2bus.first; idx_dev.bus = s2bus.second; idx_dev.dev = dev; idx_dev.func = func; if (bus2socket.find(((s2bus.first << 8 ) | s2bus.second)) != bus2socket.end()) { idx_dev.socket_id = bus2socket.at(((s2bus.first << 8 ) | s2bus.second)); } idx_devs.push_back(idx_dev); } return true; } void PCM::initUncoreObjects() { if (hasPCICFGUncore() && MSR.size()) { int i = 0; bool failed = false; try { for (i = 0; i < (int)num_sockets; ++i) { serverUncorePMUs.push_back(std::make_shared(i, this)); } } catch (std::runtime_error & e) { std::cerr << e.what() << "\n"; failed = true; } catch (...) { failed = true; } if (failed) { serverUncorePMUs.clear(); std::cerr << "Can not access server uncore PCI configuration space. Access to uncore counters (memory and QPI bandwidth) is disabled.\n"; #ifdef _MSC_VER std::cerr << "You must have signed msr.sys driver in your current directory and have administrator rights to run this program.\n"; #else //std::cerr << "you must have read and write permissions for /proc/bus/pci/7f/10.* and /proc/bus/pci/ff/10.* devices (the 'chown' command can help).\n"; //std::cerr << "you must have read and write permissions for /dev/mem device (the 'chown' command can help).\n"; //std::cerr << "you must have read permission for /sys/firmware/acpi/tables/MCFG device (the 'chmod' command can help).\n"; std::cerr << "You must be root to access server uncore counters in PCM.\n"; #endif } } else if(hasClientMCCounters() && MSR.size()) { // initialize memory bandwidth counting try { switch (cpu_model) { case TGL: case ADL: // TGLClientBW works fine for ADL case RPL: // TGLClientBW works fine for ADL clientBW = std::make_shared(); break; /* Disabled since ADLClientBW requires 2x multiplier for BW on top case ADL: case RPL: clientBW = std::make_shared(); break; */ default: clientBW = std::make_shared(); } clientImcReads = std::make_shared( new CounterWidthExtender::ClientImcReadsCounter(clientBW), 32, 10000); clientImcWrites = std::make_shared( new CounterWidthExtender::ClientImcWritesCounter(clientBW), 32, 10000); clientGtRequests = std::make_shared( new CounterWidthExtender::ClientGtRequestsCounter(clientBW), 32, 10000); clientIaRequests = std::make_shared( new CounterWidthExtender::ClientIaRequestsCounter(clientBW), 32, 10000); clientIoRequests = std::make_shared( new CounterWidthExtender::ClientIoRequestsCounter(clientBW), 32, 10000); } catch(...) { std::cerr << "Can not read memory controller counter information from PCI configuration space. Access to memory bandwidth counters is not possible.\n"; #ifdef _MSC_VER // TODO: add message here #endif #ifdef __linux__ std::cerr << "You must be root to access these SandyBridge/IvyBridge/Haswell counters in PCM. \n"; #endif } } if (cpu_model == ICX || cpu_model == SNOWRIDGE || cpu_model == SPR) { bool failed = false; try { initSocket2Ubox0Bus(); } catch (std::exception & e) { std::cerr << e.what() << "\n"; failed = true; } catch (...) { failed = true; } if (failed) { std::cerr << "Can not read PCI configuration space bus mapping. Access to uncore counters is disabled.\n"; } } if (cpu_model == ICX || cpu_model == SNOWRIDGE) { for (size_t s = 0; s < (size_t)num_sockets && s < socket2UBOX0bus.size() && s < serverUncorePMUs.size(); ++s) { serverBW.push_back(std::make_shared(serverUncorePMUs[s]->getNumMC(), socket2UBOX0bus[s].first, socket2UBOX0bus[s].second)); // std::cout << " Added serverBW object serverUncorePMUs[s]->getNumMC() = " << serverUncorePMUs[s]->getNumMC() << std::endl; } if (socket2UBOX0bus.size() != (size_t)num_sockets) { std::cerr << "PCM warning: found " << socket2UBOX0bus.size() << " uboxes. Expected " << num_sockets << std::endl; } } if (useLinuxPerfForUncore()) { initUncorePMUsPerf(); } else { initUncorePMUsDirect(); } } void PCM::globalFreezeUncoreCounters() { globalFreezeUncoreCountersInternal(1ULL); } void PCM::globalUnfreezeUncoreCounters() { globalFreezeUncoreCountersInternal(0ULL); } // 1 : freeze // 0 : unfreeze void PCM::globalFreezeUncoreCountersInternal(const unsigned long long int freeze) { for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto& handle = MSR[socketRefCore[s]]; switch (cpu_model) { case SPR: handle->write(SPR_MSR_UNCORE_PMON_GLOBAL_CTL, freeze); break; case SKX: case ICX: handle->write(MSR_UNCORE_PMON_GLOBAL_CTL, (1ULL - freeze) << 61ULL); break; case HASWELLX: case BDX: handle->write(MSR_UNCORE_PMON_GLOBAL_CTL, (1ULL - freeze) << 29ULL); break; case IVYTOWN: handle->write(IVT_MSR_UNCORE_PMON_GLOBAL_CTL, (1ULL - freeze) << 29ULL); break; } } } void PCM::initUncorePMUsDirect() { for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto & handle = MSR[socketRefCore[s]]; // unfreeze uncore PMUs globalUnfreezeUncoreCounters(); if (IVYTOWN == cpu_model || JAKETOWN == cpu_model) { uboxPMUs.push_back( UncorePMU( std::shared_ptr(), std::make_shared(handle, JKTIVT_UBOX_MSR_PMON_CTL0_ADDR), std::make_shared(handle, JKTIVT_UBOX_MSR_PMON_CTL1_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, JKTIVT_UBOX_MSR_PMON_CTR0_ADDR), std::make_shared(handle, JKTIVT_UBOX_MSR_PMON_CTR1_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, JKTIVT_UCLK_FIXED_CTL_ADDR), std::make_shared(handle, JKTIVT_UCLK_FIXED_CTR_ADDR) ) ); } else if (SPR == cpu_model) { uboxPMUs.push_back( UncorePMU( std::make_shared(handle, SPR_UBOX_MSR_PMON_BOX_CTL_ADDR), std::make_shared(handle, SPR_UBOX_MSR_PMON_CTL0_ADDR), std::make_shared(handle, SPR_UBOX_MSR_PMON_CTL1_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, SPR_UBOX_MSR_PMON_CTR0_ADDR), std::make_shared(handle, SPR_UBOX_MSR_PMON_CTR1_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, SPR_UCLK_FIXED_CTL_ADDR), std::make_shared(handle, SPR_UCLK_FIXED_CTR_ADDR) ) ); } else if (isServerCPU() && hasPCICFGUncore()) { uboxPMUs.push_back( UncorePMU( std::shared_ptr(), std::make_shared(handle, UBOX_MSR_PMON_CTL0_ADDR), std::make_shared(handle, UBOX_MSR_PMON_CTL1_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, UBOX_MSR_PMON_CTR0_ADDR), std::make_shared(handle, UBOX_MSR_PMON_CTR1_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, UCLK_FIXED_CTL_ADDR), std::make_shared(handle, UCLK_FIXED_CTR_ADDR) ) ); } auto addPMUsFromDiscovery = [this, &handle, &s](std::vector & out, const unsigned int pmuType, const int filter0 = -1) { if (uncorePMUDiscovery.get()) { for (size_t box = 0; box < uncorePMUDiscovery->getNumBoxes(pmuType, s); ++box) { if (uncorePMUDiscovery->getBoxAccessType(pmuType, s, box) == UncorePMUDiscovery::accessTypeEnum::MSR && uncorePMUDiscovery->getBoxNumRegs(pmuType, s, box) >= 4) { out.push_back( UncorePMU( std::make_shared(handle, uncorePMUDiscovery->getBoxCtlAddr(pmuType, s, box)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtlAddr(pmuType, s, box, 0)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtlAddr(pmuType, s, box, 1)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtlAddr(pmuType, s, box, 2)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtlAddr(pmuType, s, box, 3)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtrAddr(pmuType, s, box, 0)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtrAddr(pmuType, s, box, 1)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtrAddr(pmuType, s, box, 2)), std::make_shared(handle, uncorePMUDiscovery->getBoxCtrAddr(pmuType, s, box, 3)), std::shared_ptr(), std::shared_ptr(), (filter0 < 0) ? std::shared_ptr() : std::make_shared(handle, uncorePMUDiscovery->getBoxCtlAddr(pmuType, s, box) + filter0) // filters not supported by discovery ) ); } } } }; switch (cpu_model) { case IVYTOWN: case JAKETOWN: pcuPMUs.push_back( UncorePMU( std::make_shared(handle, JKTIVT_PCU_MSR_PMON_BOX_CTL_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTL0_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTL1_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTL2_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTL3_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTR0_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTR1_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTR2_ADDR), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_CTR3_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, JKTIVT_PCU_MSR_PMON_BOX_FILTER_ADDR) ) ); break; case BDX_DE: case BDX: case KNL: case HASWELLX: case SKX: case ICX: pcuPMUs.push_back( UncorePMU( std::make_shared(handle, HSX_PCU_MSR_PMON_BOX_CTL_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTL0_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTL1_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTL2_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTL3_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTR0_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTR1_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTR2_ADDR), std::make_shared(handle, HSX_PCU_MSR_PMON_CTR3_ADDR), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, HSX_PCU_MSR_PMON_BOX_FILTER_ADDR) ) ); break; case SPR: addPMUsFromDiscovery(pcuPMUs, SPR_PCU_BOX_TYPE, 0xE); if (pcuPMUs.empty()) { std::cerr << "ERROR: PCU PMU not found\n"; } break; } // add MDF PMUs switch (cpu_model) { case SPR: mdfPMUs.resize(num_sockets); addPMUsFromDiscovery(mdfPMUs[s], SPR_MDF_BOX_TYPE); if (mdfPMUs[s].empty()) { std::cerr << "ERROR: MDF PMU not found\n"; } break; } } // init IIO addresses if (getCPUModel() == PCM::SKX) { iioPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto & handle = MSR[socketRefCore[s]]; for (int unit = 0; unit < SKX_IIO_STACK_COUNT; ++unit) { iioPMUs[s][unit] = UncorePMU( std::make_shared(handle, SKX_IIO_CBDMA_UNIT_CTL + SKX_IIO_PM_REG_STEP * unit), std::make_shared(handle, SKX_IIO_CBDMA_CTL0 + SKX_IIO_PM_REG_STEP * unit + 0), std::make_shared(handle, SKX_IIO_CBDMA_CTL0 + SKX_IIO_PM_REG_STEP * unit + 1), std::make_shared(handle, SKX_IIO_CBDMA_CTL0 + SKX_IIO_PM_REG_STEP * unit + 2), std::make_shared(handle, SKX_IIO_CBDMA_CTL0 + SKX_IIO_PM_REG_STEP * unit + 3), std::make_shared(handle, SKX_IIO_CBDMA_CTR0 + SKX_IIO_PM_REG_STEP * unit + 0), std::make_shared(handle, SKX_IIO_CBDMA_CTR0 + SKX_IIO_PM_REG_STEP * unit + 1), std::make_shared(handle, SKX_IIO_CBDMA_CTR0 + SKX_IIO_PM_REG_STEP * unit + 2), std::make_shared(handle, SKX_IIO_CBDMA_CTR0 + SKX_IIO_PM_REG_STEP * unit + 3) ); } } } else if (getCPUModel() == PCM::ICX) { iioPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto & handle = MSR[socketRefCore[s]]; for (int unit = 0; unit < ICX_IIO_STACK_COUNT; ++unit) { iioPMUs[s][unit] = UncorePMU( std::make_shared(handle, ICX_IIO_UNIT_CTL[unit]), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTL_REG_OFFSET + 0), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTL_REG_OFFSET + 1), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTL_REG_OFFSET + 2), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTL_REG_OFFSET + 3), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTR_REG_OFFSET + 0), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTR_REG_OFFSET + 1), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTR_REG_OFFSET + 2), std::make_shared(handle, ICX_IIO_UNIT_CTL[unit] + ICX_IIO_CTR_REG_OFFSET + 3) ); } } } else if (getCPUModel() == PCM::SNOWRIDGE) { iioPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto & handle = MSR[socketRefCore[s]]; for (int unit = 0; unit < SNR_IIO_STACK_COUNT; ++unit) { iioPMUs[s][unit] = UncorePMU( std::make_shared(handle, SNR_IIO_CBDMA_UNIT_CTL + SNR_IIO_PM_REG_STEP * unit), std::make_shared(handle, SNR_IIO_CBDMA_CTL0 + SNR_IIO_PM_REG_STEP * unit + 0), std::make_shared(handle, SNR_IIO_CBDMA_CTL0 + SNR_IIO_PM_REG_STEP * unit + 1), std::make_shared(handle, SNR_IIO_CBDMA_CTL0 + SNR_IIO_PM_REG_STEP * unit + 2), std::make_shared(handle, SNR_IIO_CBDMA_CTL0 + SNR_IIO_PM_REG_STEP * unit + 3), std::make_shared(handle, SNR_IIO_CBDMA_CTR0 + SNR_IIO_PM_REG_STEP * unit + 0), std::make_shared(handle, SNR_IIO_CBDMA_CTR0 + SNR_IIO_PM_REG_STEP * unit + 1), std::make_shared(handle, SNR_IIO_CBDMA_CTR0 + SNR_IIO_PM_REG_STEP * unit + 2), std::make_shared(handle, SNR_IIO_CBDMA_CTR0 + SNR_IIO_PM_REG_STEP * unit + 3) ); } } } if (getCPUModel() == PCM::SPR) { iioPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto & handle = MSR[socketRefCore[s]]; for (int unit = 0; unit < SPR_M2IOSF_NUM; ++unit) { iioPMUs[s][unit] = UncorePMU( std::make_shared(handle, SPR_M2IOSF_IIO_UNIT_CTL + SPR_M2IOSF_REG_STEP * unit), std::make_shared(handle, SPR_M2IOSF_IIO_CTL0 + SPR_M2IOSF_REG_STEP * unit + 0), std::make_shared(handle, SPR_M2IOSF_IIO_CTL0 + SPR_M2IOSF_REG_STEP * unit + 1), std::make_shared(handle, SPR_M2IOSF_IIO_CTL0 + SPR_M2IOSF_REG_STEP * unit + 2), std::make_shared(handle, SPR_M2IOSF_IIO_CTL0 + SPR_M2IOSF_REG_STEP * unit + 3), std::make_shared(handle, SPR_M2IOSF_IIO_CTR0 + SPR_M2IOSF_REG_STEP * unit + 0), std::make_shared(handle, SPR_M2IOSF_IIO_CTR0 + SPR_M2IOSF_REG_STEP * unit + 1), std::make_shared(handle, SPR_M2IOSF_IIO_CTR0 + SPR_M2IOSF_REG_STEP * unit + 2), std::make_shared(handle, SPR_M2IOSF_IIO_CTR0 + SPR_M2IOSF_REG_STEP * unit + 3) ); } } } //init the IDX accelerator auto createIDXPMU = [](const size_t addr, const size_t mapSize, const size_t numaNode, const size_t socketId) -> IDX_PMU { const auto alignedAddr = addr & ~4095ULL; auto handle = std::make_shared(alignedAddr, mapSize, false); auto pmon_offset = (handle->read64(SPR_IDX_ACCEL_PMON_BASE_OFFSET) & SPR_IDX_ACCEL_PMON_BASE_MASK)*SPR_IDX_ACCEL_PMON_BASE_RATIO; const auto n_regs = SPR_IDX_ACCEL_COUNTER_MAX_NUM; std::vector > CounterControlRegs, CounterValueRegs, CounterFilterWQRegs, CounterFilterENGRegs; std::vector > CounterFilterTCRegs, CounterFilterPGSZRegs, CounterFilterXFERSZRegs; for (size_t r = 0; r < n_regs; ++r) { CounterControlRegs.push_back(std::make_shared(handle, (SPR_IDX_PMON_CTL_OFFSET(r) + pmon_offset))); CounterValueRegs.push_back(std::make_shared(handle, (SPR_IDX_PMON_CTR_OFFSET(r) + pmon_offset))); CounterFilterWQRegs.push_back(std::make_shared(handle, (SPR_IDX_PMON_FILTER_WQ_OFFSET(r) + pmon_offset))); CounterFilterENGRegs.push_back(std::make_shared(handle, (SPR_IDX_PMON_FILTER_ENG_OFFSET(r) + pmon_offset))); CounterFilterTCRegs.push_back(std::make_shared(handle, (SPR_IDX_PMON_FILTER_TC_OFFSET(r) + pmon_offset))); CounterFilterPGSZRegs.push_back(std::make_shared(handle, (SPR_IDX_PMON_FILTER_PGSZ_OFFSET(r) + pmon_offset))); CounterFilterXFERSZRegs.push_back(std::make_shared(handle, (SPR_IDX_PMON_FILTER_XFERSZ_OFFSET(r) + pmon_offset))); } return IDX_PMU( false, numaNode, socketId, std::make_shared(handle, SPR_IDX_PMON_RESET_CTL_OFFSET + pmon_offset), std::make_shared(handle, SPR_IDX_PMON_FREEZE_CTL_OFFSET + pmon_offset), std::make_shared(), CounterControlRegs, CounterValueRegs, CounterFilterWQRegs, CounterFilterENGRegs, CounterFilterTCRegs, CounterFilterPGSZRegs, CounterFilterXFERSZRegs ); }; //init the QAT accelerator auto createQATPMU = [](const size_t numaNode, const size_t socketId, const size_t domain, const size_t bus, const size_t dev, const size_t func) -> IDX_PMU { const auto n_regs = SPR_QAT_ACCEL_COUNTER_MAX_NUM; auto GlobalConfigReg= std::make_shared(domain, bus, dev, func); std::vector > CounterControlRegs, CounterValueRegs, CounterFilterWQRegs, CounterFilterENGRegs; std::vector > CounterFilterTCRegs, CounterFilterPGSZRegs, CounterFilterXFERSZRegs; for (size_t r = 0; r < n_regs; ++r) { auto CounterControlReg= std::make_shared(); CounterControlRegs.push_back(CounterControlReg); CounterValueRegs.push_back(std::make_shared(GlobalConfigReg, CounterControlReg, r)); CounterFilterWQRegs.push_back(std::make_shared()); //dummy CounterFilterENGRegs.push_back(std::make_shared()); //dummy CounterFilterTCRegs.push_back(std::make_shared()); //dummy CounterFilterPGSZRegs.push_back(std::make_shared()); //dummy CounterFilterXFERSZRegs.push_back(std::make_shared()); //dummy } return IDX_PMU( false, numaNode, socketId, std::make_shared(), std::make_shared(), GlobalConfigReg, CounterControlRegs, CounterValueRegs, CounterFilterWQRegs, CounterFilterENGRegs, CounterFilterTCRegs, CounterFilterPGSZRegs, CounterFilterXFERSZRegs ); }; if (supportIDXAccelDev() == true) { static const uint32 IAA_DEV_IDS[] = { 0x0CFE }; static const uint32 DSA_DEV_IDS[] = { 0x0B25 }; static const uint32 QAT_DEV_IDS[] = { 0x4940, 0x4942 }; std::vector > socket2IAAbus; std::vector > socket2DSAbus; std::vector > socket2QATbus; std::map rootbusMap; //Enumurate IDX devices by PCIe bus scan initSocket2Bus(socket2IAAbus, SPR_IDX_IAA_REGISTER_DEV_ADDR, SPR_IDX_IAA_REGISTER_FUNC_ADDR, IAA_DEV_IDS, (uint32)sizeof(IAA_DEV_IDS) / sizeof(IAA_DEV_IDS[0])); initSocket2Bus(socket2DSAbus, SPR_IDX_DSA_REGISTER_DEV_ADDR, SPR_IDX_DSA_REGISTER_FUNC_ADDR, DSA_DEV_IDS, (uint32)sizeof(DSA_DEV_IDS) / sizeof(DSA_DEV_IDS[0])); initSocket2Bus(socket2QATbus, SPR_IDX_QAT_REGISTER_DEV_ADDR, SPR_IDX_QAT_REGISTER_FUNC_ADDR, QAT_DEV_IDS, (uint32)sizeof(QAT_DEV_IDS) / sizeof(QAT_DEV_IDS[0])); #ifndef PCM_SILENT std::cerr << "Info: IDX - Detected " << socket2IAAbus.size() << " IAA devices, " << socket2DSAbus.size() << " DSA devices, " << socket2QATbus.size() << " QAT devices. \n"; #endif initRootBusMap(rootbusMap); idxPMUs.resize(IDX_MAX); idxPMUs[IDX_IAA].clear(); if (socket2IAAbus.size()) { std::vector devInfos; getIDXDevBAR(socket2IAAbus, SPR_IDX_IAA_REGISTER_DEV_ADDR, SPR_IDX_IAA_REGISTER_FUNC_ADDR, rootbusMap, devInfos); for (auto & devInfo : devInfos) { idxPMUs[IDX_IAA].push_back(createIDXPMU(devInfo.mem_bar, SPR_IDX_ACCEL_BAR0_SIZE, devInfo.numa_node, devInfo.socket_id)); } } idxPMUs[IDX_DSA].clear(); if (socket2DSAbus.size()) { std::vector devInfos; getIDXDevBAR(socket2DSAbus, SPR_IDX_DSA_REGISTER_DEV_ADDR, SPR_IDX_DSA_REGISTER_FUNC_ADDR, rootbusMap, devInfos); for (auto & devInfo : devInfos) { idxPMUs[IDX_DSA].push_back(createIDXPMU(devInfo.mem_bar, SPR_IDX_ACCEL_BAR0_SIZE, devInfo.numa_node, devInfo.socket_id)); } } idxPMUs[IDX_QAT].clear(); #ifdef __linux__ if (socket2QATbus.size()) { std::vector devInfos; getIDXDevBAR(socket2QATbus, SPR_IDX_QAT_REGISTER_DEV_ADDR, SPR_IDX_QAT_REGISTER_FUNC_ADDR, rootbusMap, devInfos); for (auto & devInfo : devInfos) { std::ostringstream qat_TLMCTL_sysfs_path(std::ostringstream::out); qat_TLMCTL_sysfs_path << std::string("/sys/bus/pci/devices/") << std::hex << std::setw(4) << std::setfill('0') << devInfo.domain << ":" << std::hex << std::setw(2) << std::setfill('0') << devInfo.bus << ":" << std::hex << std::setw(2) << std::setfill('0') << devInfo.dev << "." << std::hex << devInfo.func << "/telemetry/control"; const std::string qatTLMCTLStr = readSysFS(qat_TLMCTL_sysfs_path.str().c_str(), true); if (!qatTLMCTLStr.size()) //check TLM feature available or NOT. { std::cout << "Warning: IDX - QAT telemetry feature of B:0x" << std::hex << devInfo.bus << ",D:0x" << devInfo.dev << ",F:0x" << devInfo.func \ << " is NOT available, skipped." << std::dec << std::endl; continue; } idxPMUs[IDX_QAT].push_back(createQATPMU(devInfo.numa_node, devInfo.socket_id, devInfo.domain , devInfo.bus, devInfo.dev , devInfo.func)); } } #endif } // init IRP PMU int irpStacks = 0; size_t IRP_CTL_REG_OFFSET = 0; size_t IRP_CTR_REG_OFFSET = 0; const uint32* IRP_UNIT_CTL = nullptr; switch (getCPUModel()) { case SKX: irpStacks = SKX_IIO_STACK_COUNT; IRP_CTL_REG_OFFSET = SKX_IRP_CTL_REG_OFFSET; IRP_CTR_REG_OFFSET = SKX_IRP_CTR_REG_OFFSET; IRP_UNIT_CTL = SKX_IRP_UNIT_CTL; break; case ICX: irpStacks = ICX_IIO_STACK_COUNT; IRP_CTL_REG_OFFSET = ICX_IRP_CTL_REG_OFFSET; IRP_CTR_REG_OFFSET = ICX_IRP_CTR_REG_OFFSET; IRP_UNIT_CTL = ICX_IRP_UNIT_CTL; break; case SNOWRIDGE: irpStacks = SNR_IIO_STACK_COUNT; IRP_CTL_REG_OFFSET = SNR_IRP_CTL_REG_OFFSET; IRP_CTR_REG_OFFSET = SNR_IRP_CTR_REG_OFFSET; IRP_UNIT_CTL = SNR_IRP_UNIT_CTL; break; case SPR: irpStacks = SPR_M2IOSF_NUM; IRP_CTL_REG_OFFSET = SPR_IRP_CTL_REG_OFFSET; IRP_CTR_REG_OFFSET = SPR_IRP_CTR_REG_OFFSET; IRP_UNIT_CTL = SPR_IRP_UNIT_CTL; break; } if (IRP_UNIT_CTL) { irpPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto& handle = MSR[socketRefCore[s]]; for (int unit = 0; unit < irpStacks; ++unit) { irpPMUs[s][unit] = UncorePMU( std::make_shared(handle, IRP_UNIT_CTL[unit]), std::make_shared(handle, IRP_UNIT_CTL[unit] + IRP_CTL_REG_OFFSET + 0), std::make_shared(handle, IRP_UNIT_CTL[unit] + IRP_CTL_REG_OFFSET + 1), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, IRP_UNIT_CTL[unit] + IRP_CTR_REG_OFFSET + 0), std::make_shared(handle, IRP_UNIT_CTL[unit] + IRP_CTR_REG_OFFSET + 1), std::shared_ptr(), std::shared_ptr() ); } } } if (hasPCICFGUncore() && MSR.size()) { cboPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { auto & handle = MSR[socketRefCore[s]]; for (uint32 cbo = 0; cbo < getMaxNumOfCBoxes(); ++cbo) { assert(CX_MSR_PMON_BOX_CTL(cbo)); const auto filter1MSR = CX_MSR_PMON_BOX_FILTER1(cbo); std::shared_ptr filter1MSRHandle = filter1MSR ? std::make_shared(handle, filter1MSR) : std::shared_ptr(); cboPMUs[s].push_back( UncorePMU( std::make_shared(handle, CX_MSR_PMON_BOX_CTL(cbo)), std::make_shared(handle, CX_MSR_PMON_CTLY(cbo, 0)), std::make_shared(handle, CX_MSR_PMON_CTLY(cbo, 1)), std::make_shared(handle, CX_MSR_PMON_CTLY(cbo, 2)), std::make_shared(handle, CX_MSR_PMON_CTLY(cbo, 3)), std::make_shared( std::make_shared(new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[s]], CX_MSR_PMON_CTRY(cbo, 0)), 48, 5555)), std::make_shared( std::make_shared(new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[s]], CX_MSR_PMON_CTRY(cbo, 1)), 48, 5555)), std::make_shared( std::make_shared(new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[s]], CX_MSR_PMON_CTRY(cbo, 2)), 48, 5555)), std::make_shared( std::make_shared(new CounterWidthExtender::MsrHandleCounter(MSR[socketRefCore[s]], CX_MSR_PMON_CTRY(cbo, 3)), 48, 5555)), std::shared_ptr(), std::shared_ptr(), std::make_shared(handle, CX_MSR_PMON_BOX_FILTER(cbo)), filter1MSRHandle ) ); } } } if (1) { cxlPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { if (uncorePMUDiscovery.get()) { auto createCXLPMU = [this](const uint32 s, const unsigned BoxType, const size_t pos) -> UncorePMU { std::vector > CounterControlRegs, CounterValueRegs; const auto n_regs = uncorePMUDiscovery->getBoxNumRegs(BoxType, s, pos); const auto unitControlAddr = uncorePMUDiscovery->getBoxCtlAddr(BoxType, s, pos); const auto unitControlAddrAligned = unitControlAddr & ~4095ULL; auto handle = std::make_shared(unitControlAddrAligned, CXL_PMON_SIZE, false); for (size_t r = 0; r < n_regs; ++r) { CounterControlRegs.push_back(std::make_shared(handle, uncorePMUDiscovery->getBoxCtlAddr(BoxType, s, pos, r) - unitControlAddrAligned)); CounterValueRegs.push_back(std::make_shared(handle, uncorePMUDiscovery->getBoxCtrAddr(BoxType, s, pos, r) - unitControlAddrAligned)); } return UncorePMU(std::make_shared(handle, unitControlAddr - unitControlAddrAligned), CounterControlRegs, CounterValueRegs); }; if (getCPUModel() == PCM::SPR) { const auto n_units = (std::min)(uncorePMUDiscovery->getNumBoxes(SPR_CXLCM_BOX_TYPE, s), uncorePMUDiscovery->getNumBoxes(SPR_CXLDP_BOX_TYPE, s)); for (size_t pos = 0; pos < n_units; ++pos) { cxlPMUs[s].push_back(std::make_pair(createCXLPMU(s, SPR_CXLCM_BOX_TYPE, pos), createCXLPMU(s, SPR_CXLDP_BOX_TYPE, pos))); } } } } } } #ifdef PCM_USE_PERF std::vector enumeratePerfPMUs(const std::string & type, int max_id); void populatePerfPMUs(unsigned socket_, const std::vector & ids, std::vector & pmus, bool fixed, bool filter0 = false, bool filter1 = false); std::vector > enumerateIDXPerfPMUs(const std::string & type, int max_id); void populateIDXPerfPMUs(unsigned socket_, const std::vector > & ids, std::vector & pmus); #endif void PCM::initUncorePMUsPerf() { #ifdef PCM_USE_PERF iioPMUs.resize(num_sockets); irpPMUs.resize(num_sockets); cboPMUs.resize(num_sockets); mdfPMUs.resize(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { populatePerfPMUs(s, enumeratePerfPMUs("pcu", 100), pcuPMUs, false, true); populatePerfPMUs(s, enumeratePerfPMUs("ubox", 100), uboxPMUs, true); populatePerfPMUs(s, enumeratePerfPMUs("cbox", 100), cboPMUs[s], false, true, true); populatePerfPMUs(s, enumeratePerfPMUs("cha", 200), cboPMUs[s], false, true, true); populatePerfPMUs(s, enumeratePerfPMUs("mdf", 200), mdfPMUs[s], false, true, true); auto populateMapPMUs = [&s](const std::string& type, std::vector > & out) { std::vector PMUVector; populatePerfPMUs(s, enumeratePerfPMUs(type, 100), PMUVector, false); for (size_t i = 0; i < PMUVector.size(); ++i) { out[s][i] = PMUVector[i]; } }; populateMapPMUs("iio", iioPMUs); populateMapPMUs("irp", irpPMUs); } if (supportIDXAccelDev() == true) { idxPMUs.resize(IDX_MAX); idxPMUs[IDX_IAA].clear(); idxPMUs[IDX_DSA].clear(); idxPMUs[IDX_QAT].clear(); //QAT NOT support perf driver mode. populateIDXPerfPMUs(0, enumerateIDXPerfPMUs("iax", 100), idxPMUs[IDX_IAA]); populateIDXPerfPMUs(0, enumerateIDXPerfPMUs("dsa", 100), idxPMUs[IDX_DSA]); #ifndef PCM_SILENT std::cerr << "Info: IDX - Detected " << idxPMUs[IDX_IAA].size() << " IAA devices, " << idxPMUs[IDX_DSA].size() << " DSA devices.\n"; std::cerr << "Warning: IDX - QAT device NOT support perf driver mode.\n"; #endif } #endif } #ifdef __linux__ const char * keepNMIWatchdogEnabledEnvStr = "PCM_KEEP_NMI_WATCHDOG"; bool keepNMIWatchdogEnabled() { static int keep = -1; if (keep < 0) { keep = (safe_getenv(keepNMIWatchdogEnabledEnvStr) == std::string("1")) ? 1 : 0; } return keep == 1; } #define PCM_NMI_WATCHDOG_PATH "/proc/sys/kernel/nmi_watchdog" bool isNMIWatchdogEnabled(const bool silent) { const auto watchdog = readSysFS(PCM_NMI_WATCHDOG_PATH, silent); if (watchdog.length() == 0) { return false; } return (std::atoi(watchdog.c_str()) == 1); } void disableNMIWatchdog(const bool silent) { if (!silent) { std::cerr << " Disabling NMI watchdog since it consumes one hw-PMU counter. To keep NMI watchdog set environment variable " << keepNMIWatchdogEnabledEnvStr << "=1 (this reduces the core metrics set)\n"; } writeSysFS(PCM_NMI_WATCHDOG_PATH, "0"); } void enableNMIWatchdog(const bool silent) { if (!silent) std::cerr << " Re-enabling NMI watchdog.\n"; writeSysFS(PCM_NMI_WATCHDOG_PATH, "1"); } #endif class CoreTaskQueue { std::queue > wQueue; std::mutex m; std::condition_variable condVar; std::thread worker; CoreTaskQueue() = delete; CoreTaskQueue(CoreTaskQueue &) = delete; CoreTaskQueue & operator = (CoreTaskQueue &) = delete; public: CoreTaskQueue(int32 core) : worker([=]() { try { TemporalThreadAffinity tempThreadAffinity(core, false); std::unique_lock lock(m); while (1) { while (wQueue.empty()) { condVar.wait(lock); } while (!wQueue.empty()) { wQueue.front()(); wQueue.pop(); } } } catch (const std::exception & e) { std::cerr << "PCM Error. Exception in CoreTaskQueue worker function: " << e.what() << "\n"; } }) {} void push(std::packaged_task & task) { std::unique_lock lock(m); wQueue.push(std::move(task)); condVar.notify_one(); } }; std::ofstream* PCM::outfile = nullptr; // output file stream std::streambuf* PCM::backup_ofile = nullptr; // backup of original output = cout std::streambuf* PCM::backup_ofile_cerr = nullptr; // backup of original output = cerr #ifdef __linux__ void increaseULimit() { rlimit lim{}; if (getrlimit(RLIMIT_NOFILE, &lim) == 0) { const rlim_t recommendedLimit = 1000000; // std::cout << "file open limit: " << lim.rlim_cur << "," << lim.rlim_max << "\n"; if (lim.rlim_cur < recommendedLimit || lim.rlim_max < recommendedLimit) { lim.rlim_cur = lim.rlim_max = recommendedLimit; if (setrlimit(RLIMIT_NOFILE, &lim) != 0) { std::cerr << "PCM Info: setrlimit for file limit " << recommendedLimit << " failed with error " << strerror(errno) << "\n"; } } } else { std::cerr << "PCM Info: getrlimit for file limit failed with error " << strerror(errno) << "\n"; } } #endif PCM::PCM() : cpu_family(-1), cpu_model(-1), cpu_stepping(-1), cpu_microcode_level(-1), max_cpuid(0), threads_per_core(0), num_cores(0), num_sockets(0), num_phys_cores_per_socket(0), num_online_cores(0), num_online_sockets(0), core_gen_counter_num_max(0), core_gen_counter_num_used(0), // 0 means no core gen counters used core_gen_counter_width(0), core_fixed_counter_num_max(0), core_fixed_counter_num_used(0), core_fixed_counter_width(0), uncore_gen_counter_num_max(8), uncore_gen_counter_num_used(0), uncore_gen_counter_width(48), uncore_fixed_counter_num_max(1), uncore_fixed_counter_num_used(0), uncore_fixed_counter_width(48), perfmon_version(0), perfmon_config_anythread(1), nominal_frequency(0), max_qpi_speed(0), L3ScalingFactor(0), pkgThermalSpecPower(-1), pkgMinimumPower(-1), pkgMaximumPower(-1), systemTopology(new SystemRoot(this)), joulesPerEnergyUnit(0), #ifdef __linux__ resctrl(*this), #endif useResctrl(false), disable_JKT_workaround(false), blocked(false), coreCStateMsr(NULL), pkgCStateMsr(NULL), L2CacheHitRatioAvailable(false), L3CacheHitRatioAvailable(false), L3CacheMissesAvailable(false), L2CacheMissesAvailable(false), L2CacheHitsAvailable(false), L3CacheHitsNoSnoopAvailable(false), L3CacheHitsSnoopAvailable(false), L3CacheHitsAvailable(false), forceRTMAbortMode(false), mode(INVALID_MODE), canUsePerf(false), run_state(1), needToRestoreNMIWatchdog(false) { #ifdef __linux__ increaseULimit(); #endif #ifdef _MSC_VER // WARNING: This driver code (msr.sys) is only for testing purposes, not for production use Driver drv(Driver::msrLocalPath()); // drv.stop(); // restart driver (usually not needed) if (!drv.start()) { tcerr << "Cannot access CPU counters\n"; tcerr << "You must have a signed driver at " << drv.driverPath() << " and have administrator rights to run this program\n"; return; } #endif if(!detectModel()) return; if(!checkModel()) return; initCStateSupportTables(); if(!discoverSystemTopology()) return; if(!initMSR()) return; readCoreCounterConfig(true); #ifndef PCM_SILENT printSystemTopology(); #endif if(!detectNominalFrequency()) return; showSpecControlMSRs(); #ifndef PCM_DEBUG_TOPOLOGY if (safe_getenv("PCM_PRINT_TOPOLOGY") == "1") #endif { printDetailedSystemTopology(); } initEnergyMonitoring(); #ifndef PCM_SILENT std::cerr << "\n"; #endif uncorePMUDiscovery = std::make_shared(); initUncoreObjects(); initRDT(); readCPUMicrocodeLevel(); #ifdef PCM_USE_PERF canUsePerf = true; perfEventHandle.resize(num_cores, std::vector(PERF_MAX_COUNTERS, -1)); #endif for (int32 i = 0; i < num_cores; ++i) { coreTaskQueues.push_back(std::make_shared(i)); } #ifndef PCM_SILENT std::cerr << "\n"; #endif } void PCM::printDetailedSystemTopology() { // produce debug output similar to Intel MPI cpuinfo if (true) { std::cerr << "\n===== Processor topology =====\n"; std::cerr << "OS_Processor Thread_Id Core_Id Tile_Id Package_Id Core_Type Native_CPU_Model\n"; std::map > os_id_by_core, os_id_by_tile, core_id_by_socket; size_t counter = 0; for (auto it = topology.begin(); it != topology.end(); ++it) { std::cerr << std::left << std::setfill(' ') << std::setw(16) << ((it->os_id >= 0) ? it->os_id : counter) << std::setw(16) << it->thread_id << std::setw(16) << it->core_id << std::setw(16) << it->tile_id << std::setw(16) << it->socket << std::setw(16) << it->getCoreTypeStr() << std::setw(16) << it->native_cpu_model << "\n"; if (std::find(core_id_by_socket[it->socket].begin(), core_id_by_socket[it->socket].end(), it->core_id) == core_id_by_socket[it->socket].end()) core_id_by_socket[it->socket].push_back(it->core_id); // add socket offset to distinguish cores and tiles from different sockets os_id_by_core[(it->socket << 15) + it->core_id].push_back(it->os_id); os_id_by_tile[(it->socket << 15) + it->tile_id].push_back(it->os_id); ++counter; } std::cerr << "===== Placement on packages =====\n"; std::cerr << "Package Id. Core Id. Processors\n"; for (auto pkg = core_id_by_socket.begin(); pkg != core_id_by_socket.end(); ++pkg) { auto core_id = pkg->second.begin(); std::cerr << std::left << std::setfill(' ') << std::setw(15) << pkg->first << *core_id; for (++core_id; core_id != pkg->second.end(); ++core_id) { std::cerr << "," << *core_id; } std::cerr << "\n"; } std::cerr << "\n===== Core/Tile sharing =====\n"; std::cerr << "Level Processors\nCore "; for (auto core = os_id_by_core.begin(); core != os_id_by_core.end(); ++core) { auto os_id = core->second.begin(); std::cerr << "(" << *os_id; for (++os_id; os_id != core->second.end(); ++os_id) { std::cerr << "," << *os_id; } std::cerr << ")"; } std::cerr << "\nTile / L2$ "; for (auto core = os_id_by_tile.begin(); core != os_id_by_tile.end(); ++core) { auto os_id = core->second.begin(); std::cerr << "(" << *os_id; for (++os_id; os_id != core->second.end(); ++os_id) { std::cerr << "," << *os_id; } std::cerr << ")"; } std::cerr << "\n"; std::cerr << "\n"; } } void PCM::enableJKTWorkaround(bool enable) { if(disable_JKT_workaround) return; std::cerr << "Using PCM on your system might have a performance impact as per http://software.intel.com/en-us/articles/performance-impact-when-sampling-certain-llc-events-on-snb-ep-with-vtune\n"; std::cerr << "You can avoid the performance impact by using the option --noJKTWA, however the cache metrics might be wrong then.\n"; if(MSR.size()) { for(int32 i = 0; i < num_cores; ++i) { uint64 val64 = 0; MSR[i]->read(0x39C, &val64); if(enable) val64 |= 1ULL; else val64 &= (~1ULL); MSR[i]->write(0x39C, val64); } } for (size_t i = 0; i < (size_t)serverUncorePMUs.size(); ++i) { if(serverUncorePMUs[i].get()) serverUncorePMUs[i]->enableJKTWorkaround(enable); } } void PCM::showSpecControlMSRs() { PCM_CPUID_INFO cpuinfo; pcm_cpuid(7, 0, cpuinfo); if (MSR.size()) { if ((cpuinfo.reg.edx & (1 << 26)) || (cpuinfo.reg.edx & (1 << 27))) { uint64 val64 = 0; MSR[0]->read(MSR_IA32_SPEC_CTRL, &val64); std::cerr << "IBRS enabled in the kernel : " << ((val64 & 1) ? "yes" : "no") << "\n"; std::cerr << "STIBP enabled in the kernel : " << ((val64 & 2) ? "yes" : "no") << "\n"; } if (cpuinfo.reg.edx & (1 << 29)) { uint64 val64 = 0; MSR[0]->read(MSR_IA32_ARCH_CAPABILITIES, &val64); std::cerr << "The processor is not susceptible to Rogue Data Cache Load: " << ((val64 & 1) ? "yes" : "no") << "\n"; std::cerr << "The processor supports enhanced IBRS : " << ((val64 & 2) ? "yes" : "no") << "\n"; } } } bool PCM::isCoreOnline(int32 os_core_id) const { return (topology[os_core_id].os_id != -1) && (topology[os_core_id].core_id != -1) && (topology[os_core_id].socket != -1); } bool PCM::isSocketOnline(int32 socket_id) const { return socketRefCore[socket_id] != -1; } bool PCM::isCPUModelSupported(const int model_) { return ( model_ == NEHALEM_EP || model_ == NEHALEM_EX || model_ == WESTMERE_EP || model_ == WESTMERE_EX || isAtom(model_) || model_ == SNOWRIDGE || model_ == CLARKDALE || model_ == SANDY_BRIDGE || model_ == JAKETOWN || model_ == IVY_BRIDGE || model_ == HASWELL || model_ == IVYTOWN || model_ == HASWELLX || model_ == BDX_DE || model_ == BDX || model_ == BROADWELL || model_ == KNL || model_ == SKL || model_ == SKL_UY || model_ == KBL || model_ == KBL_1 || model_ == CML || model_ == ICL || model_ == RKL || model_ == TGL || model_ == ADL || model_ == RPL || model_ == SKX || model_ == ICX || model_ == SPR ); } bool PCM::checkModel() { switch (cpu_model) { case NEHALEM: cpu_model = NEHALEM_EP; break; case ATOM_2: cpu_model = ATOM; break; case HASWELL_ULT: case HASWELL_2: cpu_model = HASWELL; break; case BROADWELL_XEON_E3: cpu_model = BROADWELL; break; case ICX_D: cpu_model = ICX; break; case CML_1: cpu_model = CML; break; case ICL_1: cpu_model = ICL; break; case TGL_1: cpu_model = TGL; break; case ADL_1: cpu_model = ADL; break; case RPL_1: case RPL_2: case RPL_3: cpu_model = RPL; break; } if(!isCPUModelSupported((int)cpu_model)) { std::cerr << getUnsupportedMessage() << " CPU model number: " << cpu_model << " Brand: \"" << getCPUBrandString().c_str() << "\"\n"; /* FOR TESTING PURPOSES ONLY */ #ifdef PCM_TEST_FALLBACK_TO_ATOM std::cerr << "Fall back to ATOM functionality.\n"; cpu_model = ATOM; return true; #endif return false; } return true; } void PCM::destroyMSR() { MSR.clear(); } PCM::~PCM() { if (instance) { destroyMSR(); instance = NULL; delete systemTopology; } } bool PCM::good() { return !MSR.empty(); } #ifdef PCM_USE_PERF perf_event_attr PCM_init_perf_event_attr(bool group = true) { perf_event_attr e; bzero(&e,sizeof(perf_event_attr)); e.type = -1; // must be set up later e.size = sizeof(e); e.config = -1; // must be set up later e.sample_period = 0; e.sample_type = 0; e.read_format = group ? PERF_FORMAT_GROUP : 0; /* PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID | PERF_FORMAT_GROUP ; */ e.disabled = 0; e.inherit = 0; e.pinned = 0; e.exclusive = 0; e.exclude_user = 0; e.exclude_kernel = 0; e.exclude_hv = 0; e.exclude_idle = 0; e.mmap = 0; e.comm = 0; e.freq = 0; e.inherit_stat = 0; e.enable_on_exec = 0; e.task = 0; e.watermark = 0; e.wakeup_events = 0; return e; } #endif PCM::ErrorCode PCM::program(const PCM::ProgramMode mode_, const void * parameter_, const bool silent, const int pid) { #ifdef __linux__ if (isNMIWatchdogEnabled(silent) && (keepNMIWatchdogEnabled() == false)) { disableNMIWatchdog(silent); needToRestoreNMIWatchdog = true; } #endif if (MSR.empty()) return PCM::MSRAccessDenied; ExtendedCustomCoreEventDescription * pExtDesc = (ExtendedCustomCoreEventDescription *)parameter_; #ifdef PCM_USE_PERF closePerfHandles(silent); if (!silent) std::cerr << "Trying to use Linux perf events...\n"; const char * no_perf_env = std::getenv("PCM_NO_PERF"); if (no_perf_env != NULL && std::string(no_perf_env) == std::string("1")) { canUsePerf = false; if (!silent) std::cerr << "Usage of Linux perf events is disabled through PCM_NO_PERF environment variable. Using direct PMU programming...\n"; } /* if(num_online_cores < num_cores) { canUsePerf = false; std::cerr << "PCM does not support using Linux perf API on systems with offlined cores. Falling-back to direct PMU programming.\n"; } */ else if(PERF_COUNT_HW_MAX <= PCM_PERF_COUNT_HW_REF_CPU_CYCLES) { canUsePerf = false; if (!silent) std::cerr << "Can not use Linux perf because your Linux kernel does not support PERF_COUNT_HW_REF_CPU_CYCLES event. Falling-back to direct PMU programming.\n"; } else if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->fixedCfg && (pExtDesc->fixedCfg->value & 0x444)) { canUsePerf = false; if (!silent) { std::cerr << "Can not use Linux perf because \"any_thread\" fixed counter configuration requested (0x" << std::hex << pExtDesc->fixedCfg->value << std::dec << ") =\n" << *(pExtDesc->fixedCfg) << "\nFalling-back to direct PMU programming.\n\n"; } } else if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && (pExtDesc->OffcoreResponseMsrValue[0] || pExtDesc->OffcoreResponseMsrValue[1])) { const std::string offcore_rsp_format = readSysFS("/sys/bus/event_source/devices/cpu/format/offcore_rsp"); if (offcore_rsp_format != "config1:0-63\n") { canUsePerf = false; if (!silent) std::cerr << "Can not use Linux perf because OffcoreResponse usage is not supported. Falling-back to direct PMU programming.\n"; } } if (isHWTMAL1Supported() == true && perfSupportsTopDown() == false && pid == -1) { canUsePerf = false; if (!silent) std::cerr << "Installed Linux kernel perf does not support hardware top-down level-1 counters. Using direct PMU programming instead.\n"; } if (canUsePerf && (cpu_model == ADL || cpu_model == RPL)) { canUsePerf = false; if (!silent) std::cerr << "Linux kernel perf rejects an architectural event on your platform. Using direct PMU programming instead.\n"; } if (canUsePerf == false && noMSRMode()) { std::cerr << "ERROR: can not use perf driver and no-MSR mode is enabled\n" ; return PCM::UnknownError; } #endif if (programmed_core_pmu == false) { if((canUsePerf == false) && PMUinUse()) { return PCM::PMUBusy; } } mode = mode_; // copy custom event descriptions if (mode == CUSTOM_CORE_EVENTS) { if (!parameter_) { std::cerr << "PCM Internal Error: data structure for custom event not initialized\n"; return PCM::UnknownError; } CustomCoreEventDescription * pDesc = (CustomCoreEventDescription *)parameter_; coreEventDesc[0] = pDesc[0]; coreEventDesc[1] = pDesc[1]; if (isAtom() == false && cpu_model != KNL) { coreEventDesc[2] = pDesc[2]; core_gen_counter_num_used = 3; if (core_gen_counter_num_max > 3) { coreEventDesc[3] = pDesc[3]; core_gen_counter_num_used = 4; } } else core_gen_counter_num_used = 2; } else if (mode != EXT_CUSTOM_CORE_EVENTS) { auto LLCArchEventInit = [](CustomCoreEventDescription * evt) { evt[0].event_number = ARCH_LLC_MISS_EVTNR; evt[0].umask_value = ARCH_LLC_MISS_UMASK; evt[1].event_number = ARCH_LLC_REFERENCE_EVTNR; evt[1].umask_value = ARCH_LLC_REFERENCE_UMASK; }; if (isAtom() || cpu_model == KNL) { LLCArchEventInit(coreEventDesc); L2CacheHitRatioAvailable = true; L2CacheMissesAvailable = true; L2CacheHitsAvailable = true; core_gen_counter_num_used = 2; } else if (memoryEventErrata()) { LLCArchEventInit(coreEventDesc); L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 2; if (HASWELLX == cpu_model || HASWELL == cpu_model) { coreEventDesc[BasicCounterState::HSXL2MissPos].event_number = HSX_L2_RQSTS_MISS_EVTNR; coreEventDesc[BasicCounterState::HSXL2MissPos].umask_value = HSX_L2_RQSTS_MISS_UMASK; coreEventDesc[BasicCounterState::HSXL2RefPos].event_number = HSX_L2_RQSTS_REFERENCES_EVTNR; coreEventDesc[BasicCounterState::HSXL2RefPos].umask_value = HSX_L2_RQSTS_REFERENCES_UMASK; L2CacheHitRatioAvailable = true; L2CacheHitsAvailable = true; core_gen_counter_num_used = 4; } } else switch ( cpu_model ) { case ADL: case RPL: LLCArchEventInit(hybridAtomEventDesc); hybridAtomEventDesc[2].event_number = SKL_MEM_LOAD_RETIRED_L2_MISS_EVTNR; hybridAtomEventDesc[2].umask_value = SKL_MEM_LOAD_RETIRED_L2_MISS_UMASK; hybridAtomEventDesc[3].event_number = SKL_MEM_LOAD_RETIRED_L2_HIT_EVTNR; hybridAtomEventDesc[3].umask_value = SKL_MEM_LOAD_RETIRED_L2_HIT_UMASK; LLCArchEventInit(coreEventDesc); coreEventDesc[2].event_number = SKL_MEM_LOAD_RETIRED_L2_MISS_EVTNR; coreEventDesc[2].umask_value = SKL_MEM_LOAD_RETIRED_L2_MISS_UMASK; coreEventDesc[3].event_number = SKL_MEM_LOAD_RETIRED_L2_HIT_EVTNR; coreEventDesc[3].umask_value = SKL_MEM_LOAD_RETIRED_L2_HIT_UMASK; L2CacheHitRatioAvailable = true; L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L2CacheHitsAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 4; break; case SNOWRIDGE: LLCArchEventInit(coreEventDesc); coreEventDesc[2].event_number = SKL_MEM_LOAD_RETIRED_L2_MISS_EVTNR; coreEventDesc[2].umask_value = SKL_MEM_LOAD_RETIRED_L2_MISS_UMASK; coreEventDesc[3].event_number = SKL_MEM_LOAD_RETIRED_L2_HIT_EVTNR; coreEventDesc[3].umask_value = SKL_MEM_LOAD_RETIRED_L2_HIT_UMASK; L2CacheHitRatioAvailable = true; L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L2CacheHitsAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 4; break; PCM_SKL_PATH_CASES case SKX: case ICX: case SPR: assert(useSkylakeEvents()); coreEventDesc[0].event_number = SKL_MEM_LOAD_RETIRED_L3_MISS_EVTNR; coreEventDesc[0].umask_value = SKL_MEM_LOAD_RETIRED_L3_MISS_UMASK; coreEventDesc[1].event_number = SKL_MEM_LOAD_RETIRED_L3_HIT_EVTNR; coreEventDesc[1].umask_value = SKL_MEM_LOAD_RETIRED_L3_HIT_UMASK; coreEventDesc[2].event_number = SKL_MEM_LOAD_RETIRED_L2_MISS_EVTNR; coreEventDesc[2].umask_value = SKL_MEM_LOAD_RETIRED_L2_MISS_UMASK; coreEventDesc[3].event_number = SKL_MEM_LOAD_RETIRED_L2_HIT_EVTNR; coreEventDesc[3].umask_value = SKL_MEM_LOAD_RETIRED_L2_HIT_UMASK; if (core_gen_counter_num_max == 2) { L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 2; break; } else if (core_gen_counter_num_max == 3) { L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 3; break; } L2CacheHitRatioAvailable = true; L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L2CacheHitsAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 4; break; case SANDY_BRIDGE: case JAKETOWN: case IVYTOWN: case IVY_BRIDGE: case HASWELL: case HASWELLX: case BROADWELL: case BDX_DE: case BDX: coreEventDesc[0].event_number = ARCH_LLC_MISS_EVTNR; coreEventDesc[0].umask_value = ARCH_LLC_MISS_UMASK; coreEventDesc[1].event_number = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_EVTNR; coreEventDesc[1].umask_value = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_UMASK; coreEventDesc[2].event_number = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_EVTNR; coreEventDesc[2].umask_value = MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_UMASK; coreEventDesc[3].event_number = MEM_LOAD_UOPS_RETIRED_L2_HIT_EVTNR; coreEventDesc[3].umask_value = MEM_LOAD_UOPS_RETIRED_L2_HIT_UMASK; if (core_gen_counter_num_max == 3) { L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L3CacheHitsNoSnoopAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 3; break; } L2CacheHitRatioAvailable = true; L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L2CacheHitsAvailable = true; L3CacheHitsNoSnoopAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 4; break; case NEHALEM_EP: case WESTMERE_EP: case CLARKDALE: coreEventDesc[0].event_number = MEM_LOAD_RETIRED_L3_MISS_EVTNR; coreEventDesc[0].umask_value = MEM_LOAD_RETIRED_L3_MISS_UMASK; coreEventDesc[1].event_number = MEM_LOAD_RETIRED_L3_UNSHAREDHIT_EVTNR; coreEventDesc[1].umask_value = MEM_LOAD_RETIRED_L3_UNSHAREDHIT_UMASK; coreEventDesc[2].event_number = MEM_LOAD_RETIRED_L2_HITM_EVTNR; coreEventDesc[2].umask_value = MEM_LOAD_RETIRED_L2_HITM_UMASK; coreEventDesc[3].event_number = MEM_LOAD_RETIRED_L2_HIT_EVTNR; coreEventDesc[3].umask_value = MEM_LOAD_RETIRED_L2_HIT_UMASK; L2CacheHitRatioAvailable = true; L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L2CacheHitsAvailable = true; L3CacheHitsNoSnoopAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 4; break; default: assert(!useSkylakeEvents()); coreEventDesc[0].event_number = ARCH_LLC_MISS_EVTNR; coreEventDesc[0].umask_value = ARCH_LLC_MISS_UMASK; coreEventDesc[1].event_number = MEM_LOAD_RETIRED_L3_UNSHAREDHIT_EVTNR; coreEventDesc[1].umask_value = MEM_LOAD_RETIRED_L3_UNSHAREDHIT_UMASK; coreEventDesc[2].event_number = MEM_LOAD_RETIRED_L2_HITM_EVTNR; coreEventDesc[2].umask_value = MEM_LOAD_RETIRED_L2_HITM_UMASK; coreEventDesc[3].event_number = MEM_LOAD_RETIRED_L2_HIT_EVTNR; coreEventDesc[3].umask_value = MEM_LOAD_RETIRED_L2_HIT_UMASK; L2CacheHitRatioAvailable = true; L3CacheHitRatioAvailable = true; L3CacheMissesAvailable = true; L2CacheMissesAvailable = true; L2CacheHitsAvailable = true; L3CacheHitsNoSnoopAvailable = true; L3CacheHitsSnoopAvailable = true; L3CacheHitsAvailable = true; core_gen_counter_num_used = 4; } } core_fixed_counter_num_used = 3; if(EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && (pExtDesc->gpCounterCfg || pExtDesc->gpCounterHybridAtomCfg)) { core_gen_counter_num_used = pExtDesc->nGPCounters; } if(cpu_model == JAKETOWN) { bool enableWA = false; for(uint32 i = 0; i< core_gen_counter_num_used; ++i) { if(coreEventDesc[i].event_number == MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_EVTNR) enableWA = true; } enableJKTWorkaround(enableWA); // this has a performance penalty on memory access } if (core_gen_counter_num_used > core_gen_counter_num_max) { std::cerr << "PCM ERROR: Trying to program " << core_gen_counter_num_used << " general purpose counters with only " << core_gen_counter_num_max << " available\n"; return PCM::UnknownError; } if (core_fixed_counter_num_used > core_fixed_counter_num_max) { std::cerr << "PCM ERROR: Trying to program " << core_fixed_counter_num_used << " fixed counters with only " << core_fixed_counter_num_max << " available\n"; return PCM::UnknownError; } if (pid != -1 && canUsePerf == false) { std::cerr << "PCM ERROR: pid monitoring is only supported with Linux perf_event driver\n"; return PCM::UnknownError; } #ifdef __linux__ if (isNMIWatchdogEnabled(silent) && (canUsePerf == false)) { std::cerr << "PCM ERROR: Unsupported mode. NMI watchdog is enabled and Linux perf_event driver is not used\n"; return PCM::UnknownError; } #endif std::vector tids{}; #ifdef PCM_USE_PERF if (pid != -1) { const auto strDir = std::string("/proc/") + std::to_string(pid) + "/task/"; DIR * tidDir = opendir(strDir.c_str()); if (tidDir) { struct dirent * entry{nullptr}; while ((entry = readdir(tidDir)) != nullptr) { assert(entry->d_name); const auto tid = atoi(entry->d_name); if (tid) { tids.push_back(tid); // std::cerr << "Detected task " << tids.back() << "\n"; } } closedir(tidDir); } else { std::cerr << "ERROR: Can't open " << strDir << "\n"; return PCM::UnknownError; } } if (tids.empty() == false) { if (isHWTMAL1Supported()) { if (!silent) std::cerr << "INFO: TMA L1 metrics are not supported in PID collection mode\n"; } if (!silent) std::cerr << "INFO: collecting core metrics for " << tids.size() << " threads in process " << pid << "\n"; PerfEventHandleContainer _1(num_cores, std::vector(PERF_MAX_COUNTERS, -1)); perfEventTaskHandle.resize(tids.size(), _1); } #endif lastProgrammedCustomCounters.clear(); lastProgrammedCustomCounters.resize(num_cores); core_global_ctrl_value = 0ULL; std::vector > asyncCoreResults; std::vector programmingStatuses(num_cores, PCM::Success); for (int i = 0; i < (int)num_cores; ++i) { if (isCoreOnline(i) == false) continue; std::packaged_task task([this, i, mode_, pExtDesc, &programmingStatuses, &tids]() -> void { TemporalThreadAffinity tempThreadAffinity(i, false); // speedup trick for Linux programmingStatuses[i] = programCoreCounters(i, mode_, pExtDesc, lastProgrammedCustomCounters[i], tids); }); asyncCoreResults.push_back(task.get_future()); coreTaskQueues[i]->push(task); } for (auto& ar : asyncCoreResults) ar.wait(); for (const auto& status : programmingStatuses) { if (status != PCM::Success) { return status; } } programmed_core_pmu = true; if (canUsePerf && !silent) { std::cerr << "Successfully programmed on-core PMU using Linux perf\n"; } if (EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->defaultUncoreProgramming == false) { return PCM::Success; } if (hasPCICFGUncore()) // program uncore counters { std::vector> qpi_speeds; for (size_t i = 0; i < (size_t)serverUncorePMUs.size(); ++i) { serverUncorePMUs[i]->program(); qpi_speeds.push_back(std::async(std::launch::async, &ServerUncorePMUs::computeQPISpeed, serverUncorePMUs[i].get(), socketRefCore[i], cpu_model)); } for (size_t i = 0; i < (size_t)serverUncorePMUs.size(); ++i) { max_qpi_speed = (std::max)(qpi_speeds[i].get(), max_qpi_speed); } programCbo(); } // program uncore counters on old CPU arch else if (cpu_model == NEHALEM_EP || cpu_model == WESTMERE_EP || cpu_model == CLARKDALE) { for (int i = 0; i < (int)num_cores; ++i) { if (isCoreOnline(i) == false) continue; TemporalThreadAffinity tempThreadAffinity(i, false); // speedup trick for Linux programNehalemEPUncore(i); } } else if (hasBecktonUncore()) { for (int i = 0; i < (int)num_cores; ++i) { if (isCoreOnline(i) == false) continue; TemporalThreadAffinity tempThreadAffinity(i, false); // speedup trick for Linux programBecktonUncore(i); } } if (!silent) reportQPISpeed(); return PCM::Success; } void PCM::checkError(const PCM::ErrorCode code) { switch (code) { case PCM::Success: break; case PCM::MSRAccessDenied: std::cerr << "Access to Intel(r) Performance Counter Monitor has denied (no MSR or PCI CFG space access).\n"; exit(EXIT_FAILURE); case PCM::PMUBusy: std::cerr << "Access to Intel(r) Performance Counter Monitor has denied (Performance Monitoring Unit is occupied by other application)\n"; std::cerr << "Try to stop the application that uses PMU, or reset PMU configuration from PCM application itself\n"; std::cerr << "You can try to reset PMU configuration now. Try to reset? (y/n)\n"; char yn; std::cin >> yn; if ('y' == yn) { resetPMU(); std::cerr << "PMU configuration has been reset. Try to rerun the program again.\n"; } exit(EXIT_FAILURE); default: std::cerr << "Access to Intel(r) Performance Counter Monitor has denied (Unknown error).\n"; exit(EXIT_FAILURE); } } std::mutex printErrorMutex; PCM::ErrorCode PCM::programCoreCounters(const int i /* core */, const PCM::ProgramMode mode_, const ExtendedCustomCoreEventDescription * pExtDesc, std::vector & result, const std::vector & tids) { (void) tids; // to silence uused param warning on non Linux OS // program core counters result.clear(); FixedEventControlRegister ctrl_reg; auto initFixedCtrl = [&](const bool & enableCtr3) { if (EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->fixedCfg) { ctrl_reg = *(pExtDesc->fixedCfg); } else { ctrl_reg.value = 0; ctrl_reg.fields.os0 = 1; ctrl_reg.fields.usr0 = 1; ctrl_reg.fields.os1 = 1; ctrl_reg.fields.usr1 = 1; ctrl_reg.fields.os2 = 1; ctrl_reg.fields.usr2 = 1; if (enableCtr3 && isFixedCounterSupported(3)) { ctrl_reg.fields.os3 = 1; ctrl_reg.fields.usr3 = 1; } } }; #ifdef PCM_USE_PERF int leader_counter = -1; auto programPerfEvent = [this, &leader_counter, &i, &tids](perf_event_attr e, const int eventPos, const std::string & eventName) -> bool { auto programPerfEventHelper = [&i]( PerfEventHandleContainer & perfEventHandle, perf_event_attr & e, const int eventPos, const std::string & eventName, const int leader_counter, const int tid) -> bool { // if (i == 0) std::cerr << "DEBUG: programming event "<< std::hex << e.config << std::dec << "\n"; if ((perfEventHandle[i][eventPos] = syscall(SYS_perf_event_open, &e, tid, i /* core id */, leader_counter /* group leader */, 0)) <= 0) { std::lock_guard _(printErrorMutex); std::cerr << "Linux Perf: Error when programming " << eventName << ", error: " << strerror(errno) << " with config 0x" << std::hex << e.config << " config1 0x" << e.config1 << std::dec << " for tid " << tid << " leader " << leader_counter << "\n"; if (24 == errno) { std::cerr << PCM_ULIMIT_RECOMMENDATION; } else { std::cerr << "try running with environment variable PCM_NO_PERF=1\n"; } return false; } return true; }; if (tids.empty() == false) { e.inherit = 1; e.exclude_kernel = 1; e.exclude_hv = 1; e.read_format = 0; // 'inherit' does not work for combinations of read format (e.g. PERF_FORMAT_GROUP) auto handleIt = perfEventTaskHandle.begin(); for (const auto & tid: tids) { if (handleIt == perfEventTaskHandle.end()) { break; } if (programPerfEventHelper(*handleIt, e, eventPos, eventName, -1, tid) == false) { return false; } ++handleIt; } return true; } return programPerfEventHelper(perfEventHandle, e, eventPos, eventName, leader_counter, -1); }; if (canUsePerf) { initFixedCtrl(false); perf_event_attr e = PCM_init_perf_event_attr(); e.type = PERF_TYPE_HARDWARE; e.config = PERF_COUNT_HW_INSTRUCTIONS; e.exclude_kernel = 1 - ctrl_reg.fields.os0; e.exclude_hv = e.exclude_kernel; e.exclude_user = 1 - ctrl_reg.fields.usr0; if (programPerfEvent(e, PERF_INST_RETIRED_POS, "INST_RETIRED") == false) { return PCM::UnknownError; } leader_counter = perfEventHandle[i][PERF_INST_RETIRED_POS]; e.config = PERF_COUNT_HW_CPU_CYCLES; e.exclude_kernel = 1 - ctrl_reg.fields.os1; e.exclude_hv = e.exclude_kernel; e.exclude_user = 1 - ctrl_reg.fields.usr1; if (programPerfEvent(e, PERF_CPU_CLK_UNHALTED_THREAD_POS, "CPU_CLK_UNHALTED_THREAD") == false) { return PCM::UnknownError; } e.config = PCM_PERF_COUNT_HW_REF_CPU_CYCLES; e.exclude_kernel = 1 - ctrl_reg.fields.os2; e.exclude_hv = e.exclude_kernel; e.exclude_user = 1 - ctrl_reg.fields.usr2; if (programPerfEvent(e, PERF_CPU_CLK_UNHALTED_REF_POS, "CPU_CLK_UNHALTED_REF") == false) { return PCM::UnknownError; } } else #endif { // disable counters while programming MSR[i]->write(IA32_CR_PERF_GLOBAL_CTRL, 0); MSR[i]->read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); initFixedCtrl(true); MSR[i]->write(INST_RETIRED_ADDR, 0); MSR[i]->write(CPU_CLK_UNHALTED_THREAD_ADDR, 0); MSR[i]->write(CPU_CLK_UNHALTED_REF_ADDR, 0); MSR[i]->write(IA32_CR_FIXED_CTR_CTRL, ctrl_reg.value); } if (EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc) { if (pExtDesc->OffcoreResponseMsrValue[0]) // still need to do also if perf API is used due to a bug in perf MSR[i]->write(MSR_OFFCORE_RSP0, pExtDesc->OffcoreResponseMsrValue[0]); if (pExtDesc->OffcoreResponseMsrValue[1]) MSR[i]->write(MSR_OFFCORE_RSP1, pExtDesc->OffcoreResponseMsrValue[1]); if (pExtDesc->LoadLatencyMsrValue != ExtendedCustomCoreEventDescription::invalidMsrValue()) { MSR[i]->write(MSR_LOAD_LATENCY, pExtDesc->LoadLatencyMsrValue); } if (pExtDesc->FrontendMsrValue != ExtendedCustomCoreEventDescription::invalidMsrValue()) { MSR[i]->write(MSR_FRONTEND, pExtDesc->FrontendMsrValue); } } auto setEvent = [] (EventSelectRegister & reg, const uint64 event, const uint64 umask) { reg.fields.event_select = event; reg.fields.umask = umask; reg.fields.usr = 1; reg.fields.os = 1; reg.fields.edge = 0; reg.fields.pin_control = 0; reg.fields.apic_int = 0; reg.fields.any_thread = 0; reg.fields.enable = 1; reg.fields.invert = 0; reg.fields.cmask = 0; reg.fields.in_tx = 0; reg.fields.in_txcp = 0; }; EventSelectRegister event_select_reg; uint64 PEBSEnable = 0ULL; for (uint32 j = 0; j < core_gen_counter_num_used; ++j) { if (hybrid == false || (hybrid == true && topology[i].core_type == TopologyEntry::Core)) { if (EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->gpCounterCfg) { event_select_reg = pExtDesc->gpCounterCfg[j]; event_select_reg.fields.enable = 1; } else { MSR[i]->read(IA32_PERFEVTSEL0_ADDR + j, &event_select_reg.value); // read-only also safe for perf setEvent(event_select_reg, coreEventDesc[j].event_number, coreEventDesc[j].umask_value); } } else if (hybrid == true && topology[i].core_type == TopologyEntry::Atom) { if (EXT_CUSTOM_CORE_EVENTS == mode_ && pExtDesc && pExtDesc->gpCounterHybridAtomCfg) { event_select_reg = pExtDesc->gpCounterHybridAtomCfg[j]; event_select_reg.fields.enable = 1; } else { MSR[i]->read(IA32_PERFEVTSEL0_ADDR + j, &event_select_reg.value); // read-only also safe for perf setEvent(event_select_reg, hybridAtomEventDesc[j].event_number, hybridAtomEventDesc[j].umask_value); } } result.push_back(event_select_reg); if (pExtDesc != nullptr && event_select_reg.fields.event_select == LOAD_LATENCY_EVTNR && event_select_reg.fields.umask == LOAD_LATENCY_UMASK) { PEBSEnable |= (1ULL << j); } #ifdef PCM_USE_PERF if (canUsePerf) { perf_event_attr e = PCM_init_perf_event_attr(); e.type = PERF_TYPE_RAW; e.config = (1ULL << 63ULL) + event_select_reg.value; if (pExtDesc != nullptr) { if (event_select_reg.fields.event_select == getOCREventNr(0, i).first && event_select_reg.fields.umask == getOCREventNr(0, i).second) e.config1 = pExtDesc->OffcoreResponseMsrValue[0]; if (event_select_reg.fields.event_select == getOCREventNr(1, i).first && event_select_reg.fields.umask == getOCREventNr(1, i).second) e.config1 = pExtDesc->OffcoreResponseMsrValue[1]; if (event_select_reg.fields.event_select == LOAD_LATENCY_EVTNR && event_select_reg.fields.umask == LOAD_LATENCY_UMASK) { e.config1 = pExtDesc->LoadLatencyMsrValue; } if (event_select_reg.fields.event_select == FRONTEND_EVTNR && event_select_reg.fields.umask == FRONTEND_UMASK) { e.config1 = pExtDesc->FrontendMsrValue; } } if (programPerfEvent(e, PERF_GEN_EVENT_0_POS + j, std::string("generic event #") + std::to_string(j) + std::string(" on core #") + std::to_string(i)) == false) { return PCM::UnknownError; } } else #endif { MSR[i]->write(IA32_PMC0 + j, 0); MSR[i]->write(IA32_PERFEVTSEL0_ADDR + j, event_select_reg.value); } } if (!canUsePerf) { // start counting, enable all (4 programmable + 3 fixed) counters uint64 value = (1ULL << 0) + (1ULL << 1) + (1ULL << 2) + (1ULL << 3) + (1ULL << 32) + (1ULL << 33) + (1ULL << 34); if (isFixedCounterSupported(3)) { value |= (1ULL << 35); MSR[i]->write(TOPDOWN_SLOTS_ADDR, 0); } if (isHWTMAL1Supported()) { value |= (1ULL << 48); MSR[i]->write(PERF_METRICS_ADDR, 0); } if (isAtom() || cpu_model == KNL) // KNL and Atom have 3 fixed + only 2 programmable counters value = (1ULL << 0) + (1ULL << 1) + (1ULL << 32) + (1ULL << 33) + (1ULL << 34); for (uint32 j = 0; j < core_gen_counter_num_used; ++j) { value |= (1ULL << j); // enable all custom counters (if > 4) } if (core_global_ctrl_value) { assert(core_global_ctrl_value == value); } else { core_global_ctrl_value = value; } MSR[i]->write(IA32_PERF_GLOBAL_OVF_CTRL, value); MSR[i]->write(IA32_CR_PERF_GLOBAL_CTRL, value); } #ifdef PCM_USE_PERF else { if (isFixedCounterSupported(3) && isHWTMAL1Supported() && perfSupportsTopDown()) { const auto topDownEvents = { std::make_pair(perfSlotsPath, PERF_TOPDOWN_SLOTS_POS), std::make_pair(perfBadSpecPath, PERF_TOPDOWN_BADSPEC_POS), std::make_pair(perfBackEndPath, PERF_TOPDOWN_BACKEND_POS), std::make_pair(perfFrontEndPath, PERF_TOPDOWN_FRONTEND_POS), std::make_pair(perfRetiringPath, PERF_TOPDOWN_RETIRING_POS)}; int readPos = core_fixed_counter_num_used + core_gen_counter_num_used; leader_counter = -1; for (const auto & event : topDownEvents) { uint64 eventSel = 0, umask = 0; const auto eventDesc = readSysFS(event.first); const auto tokens = split(eventDesc, ','); for (const auto & token : tokens) { if (match(token, "event=", &eventSel)) { // found and matched event, wrote value to 'eventSel' } else if (match(token, "umask=", &umask)) { // found and matched umask, wrote value to 'umask' } else { std::lock_guard _(printErrorMutex); std::cerr << "ERROR: unknown token " << token << " in event description \"" << eventDesc << "\" from " << event.first << "\n"; return PCM::UnknownError; } } EventSelectRegister reg; reg.fields.event_select = eventSel; reg.fields.umask = umask; perf_event_attr e = PCM_init_perf_event_attr(); e.type = PERF_TYPE_RAW; e.config = reg.value; // std::cerr << "Programming perf event " << std::hex << e.config << "\n" << std::dec; if (programPerfEvent(e, event.second, std::string("event ") + event.first + " " + eventDesc) == false) { return PCM::UnknownError; } leader_counter = perfEventHandle[i][PERF_TOPDOWN_SLOTS_POS]; perfTopDownPos[event.second] = readPos++; } } } #endif if (PEBSEnable) { cleanupPEBS = true; MSR[i]->write(IA32_PEBS_ENABLE_ADDR, PEBSEnable); } return PCM::Success; } void PCM::reportQPISpeed() const { if (!max_qpi_speed) return; if (hasPCICFGUncore()) { for (size_t i = 0; i < (size_t)serverUncorePMUs.size(); ++i) { std::cerr << "Socket " << i << "\n"; if(serverUncorePMUs[i].get()) serverUncorePMUs[i]->reportQPISpeed(); } } else { std::cerr << "Max " << xPI() << " speed: " << max_qpi_speed / (1e9) << " GBytes/second (" << max_qpi_speed / (1e9*getBytesPerLinkTransfer()) << " GT/second)\n"; } } void PCM::programNehalemEPUncore(int32 core) { #define CPUCNT_INIT_THE_REST_OF_EVTCNT \ unc_event_select_reg.fields.occ_ctr_rst = 1; \ unc_event_select_reg.fields.edge = 0; \ unc_event_select_reg.fields.enable_pmi = 0; \ unc_event_select_reg.fields.enable = 1; \ unc_event_select_reg.fields.invert = 0; \ unc_event_select_reg.fields.cmask = 0; uncore_gen_counter_num_used = 8; UncoreEventSelectRegister unc_event_select_reg; MSR[core]->read(MSR_UNCORE_PERFEVTSEL0_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QMC_WRITES_FULL_ANY_EVTNR; unc_event_select_reg.fields.umask = UNC_QMC_WRITES_FULL_ANY_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL0_ADDR, unc_event_select_reg.value); MSR[core]->read(MSR_UNCORE_PERFEVTSEL1_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QMC_NORMAL_READS_ANY_EVTNR; unc_event_select_reg.fields.umask = UNC_QMC_NORMAL_READS_ANY_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL1_ADDR, unc_event_select_reg.value); MSR[core]->read(MSR_UNCORE_PERFEVTSEL2_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_IOH_READS_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL2_ADDR, unc_event_select_reg.value); MSR[core]->read(MSR_UNCORE_PERFEVTSEL3_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_IOH_WRITES_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL3_ADDR, unc_event_select_reg.value); MSR[core]->read(MSR_UNCORE_PERFEVTSEL4_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_REMOTE_READS_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL4_ADDR, unc_event_select_reg.value); MSR[core]->read(MSR_UNCORE_PERFEVTSEL5_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_REMOTE_WRITES_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL5_ADDR, unc_event_select_reg.value); MSR[core]->read(MSR_UNCORE_PERFEVTSEL6_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_LOCAL_READS_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL6_ADDR, unc_event_select_reg.value); MSR[core]->read(MSR_UNCORE_PERFEVTSEL7_ADDR, &unc_event_select_reg.value); unc_event_select_reg.fields.event_select = UNC_QHL_REQUESTS_EVTNR; unc_event_select_reg.fields.umask = UNC_QHL_REQUESTS_LOCAL_WRITES_UMASK; CPUCNT_INIT_THE_REST_OF_EVTCNT MSR[core]->write(MSR_UNCORE_PERFEVTSEL7_ADDR, unc_event_select_reg.value); #undef CPUCNT_INIT_THE_REST_OF_EVTCNT // start uncore counting uint64 value = 255 + (1ULL << 32); // enable all counters MSR[core]->write(MSR_UNCORE_PERF_GLOBAL_CTRL_ADDR, value); // synchronise counters MSR[core]->write(MSR_UNCORE_PMC0, 0); MSR[core]->write(MSR_UNCORE_PMC1, 0); MSR[core]->write(MSR_UNCORE_PMC2, 0); MSR[core]->write(MSR_UNCORE_PMC3, 0); MSR[core]->write(MSR_UNCORE_PMC4, 0); MSR[core]->write(MSR_UNCORE_PMC5, 0); MSR[core]->write(MSR_UNCORE_PMC6, 0); MSR[core]->write(MSR_UNCORE_PMC7, 0); } void PCM::programBecktonUncore(int32 core) { // program Beckton uncore if (core == socketRefCore[0]) computeQPISpeedBeckton((int)core); uint64 value = 1 << 29ULL; // reset all counters MSR[core]->write(U_MSR_PMON_GLOBAL_CTL, value); BecktonUncorePMUZDPCTLFVCRegister FVCreg; FVCreg.value = 0; if (cpu_model == NEHALEM_EX) { FVCreg.fields.bcmd = 0; // rd_bcmd FVCreg.fields.resp = 0; // ack_resp FVCreg.fields.evnt0 = 5; // bcmd_match FVCreg.fields.evnt1 = 6; // resp_match FVCreg.fields.pbox_init_err = 0; } else { FVCreg.fields_wsm.bcmd = 0; // rd_bcmd FVCreg.fields_wsm.resp = 0; // ack_resp FVCreg.fields_wsm.evnt0 = 5; // bcmd_match FVCreg.fields_wsm.evnt1 = 6; // resp_match FVCreg.fields_wsm.pbox_init_err = 0; } MSR[core]->write(MB0_MSR_PMU_ZDP_CTL_FVC, FVCreg.value); MSR[core]->write(MB1_MSR_PMU_ZDP_CTL_FVC, FVCreg.value); BecktonUncorePMUCNTCTLRegister CNTCTLreg; CNTCTLreg.value = 0; CNTCTLreg.fields.en = 1; CNTCTLreg.fields.pmi_en = 0; CNTCTLreg.fields.count_mode = 0; CNTCTLreg.fields.storage_mode = 0; CNTCTLreg.fields.wrap_mode = 1; CNTCTLreg.fields.flag_mode = 0; CNTCTLreg.fields.inc_sel = 0x0d; // FVC_EV0 MSR[core]->write(MB0_MSR_PMU_CNT_CTL_0, CNTCTLreg.value); MSR[core]->write(MB1_MSR_PMU_CNT_CTL_0, CNTCTLreg.value); CNTCTLreg.fields.inc_sel = 0x0e; // FVC_EV1 MSR[core]->write(MB0_MSR_PMU_CNT_CTL_1, CNTCTLreg.value); MSR[core]->write(MB1_MSR_PMU_CNT_CTL_1, CNTCTLreg.value); value = 1 + ((0x0C) << 1ULL); // enable bit + (event select IMT_INSERTS_WR) MSR[core]->write(BB0_MSR_PERF_CNT_CTL_1, value); MSR[core]->write(BB1_MSR_PERF_CNT_CTL_1, value); MSR[core]->write(MB0_MSR_PERF_GLOBAL_CTL, 3); // enable two counters MSR[core]->write(MB1_MSR_PERF_GLOBAL_CTL, 3); // enable two counters MSR[core]->write(BB0_MSR_PERF_GLOBAL_CTL, 2); // enable second counter MSR[core]->write(BB1_MSR_PERF_GLOBAL_CTL, 2); // enable second counter // program R-Box to monitor QPI traffic // enable counting on all counters on the left side (port 0-3) MSR[core]->write(R_MSR_PMON_GLOBAL_CTL_7_0, 255); // ... on the right side (port 4-7) MSR[core]->write(R_MSR_PMON_GLOBAL_CTL_15_8, 255); // pick the event value = (1 << 7ULL) + (1 << 6ULL) + (1 << 2ULL); // count any (incoming) data responses MSR[core]->write(R_MSR_PORT0_IPERF_CFG0, value); MSR[core]->write(R_MSR_PORT1_IPERF_CFG0, value); MSR[core]->write(R_MSR_PORT4_IPERF_CFG0, value); MSR[core]->write(R_MSR_PORT5_IPERF_CFG0, value); // pick the event value = (1ULL << 30ULL); // count null idle flits sent MSR[core]->write(R_MSR_PORT0_IPERF_CFG1, value); MSR[core]->write(R_MSR_PORT1_IPERF_CFG1, value); MSR[core]->write(R_MSR_PORT4_IPERF_CFG1, value); MSR[core]->write(R_MSR_PORT5_IPERF_CFG1, value); // choose counter 0 to monitor R_MSR_PORT0_IPERF_CFG0 MSR[core]->write(R_MSR_PMON_CTL0, 1 + 2 * (0)); // choose counter 1 to monitor R_MSR_PORT1_IPERF_CFG0 MSR[core]->write(R_MSR_PMON_CTL1, 1 + 2 * (6)); // choose counter 8 to monitor R_MSR_PORT4_IPERF_CFG0 MSR[core]->write(R_MSR_PMON_CTL8, 1 + 2 * (0)); // choose counter 9 to monitor R_MSR_PORT5_IPERF_CFG0 MSR[core]->write(R_MSR_PMON_CTL9, 1 + 2 * (6)); // choose counter 2 to monitor R_MSR_PORT0_IPERF_CFG1 MSR[core]->write(R_MSR_PMON_CTL2, 1 + 2 * (1)); // choose counter 3 to monitor R_MSR_PORT1_IPERF_CFG1 MSR[core]->write(R_MSR_PMON_CTL3, 1 + 2 * (7)); // choose counter 10 to monitor R_MSR_PORT4_IPERF_CFG1 MSR[core]->write(R_MSR_PMON_CTL10, 1 + 2 * (1)); // choose counter 11 to monitor R_MSR_PORT5_IPERF_CFG1 MSR[core]->write(R_MSR_PMON_CTL11, 1 + 2 * (7)); // enable uncore TSC counter (fixed one) MSR[core]->write(W_MSR_PMON_GLOBAL_CTL, 1ULL << 31ULL); MSR[core]->write(W_MSR_PMON_FIXED_CTR_CTL, 1ULL); value = (1 << 28ULL) + 1; // enable all counters MSR[core]->write(U_MSR_PMON_GLOBAL_CTL, value); } uint64 RDTSC(); void PCM::computeNominalFrequency() { const int ref_core = 0; const uint64 before = getInvariantTSC_Fast(ref_core); MySleepMs(100); const uint64 after = getInvariantTSC_Fast(ref_core); nominal_frequency = 10ULL*(after-before); std::cerr << "WARNING: Core nominal frequency has to be estimated\n"; } std::string PCM::getCPUBrandString() { char buffer[sizeof(int)*4*3+1]; PCM_CPUID_INFO * info = (PCM_CPUID_INFO *) buffer; pcm_cpuid(0x80000002, *info); ++info; pcm_cpuid(0x80000003, *info); ++info; pcm_cpuid(0x80000004, *info); buffer[sizeof(int)*4*3] = 0; std::string result(buffer); while(result[0]==' ') result.erase(0,1); std::string::size_type i; while((i = result.find(" ")) != std::string::npos) result.replace(i,2," "); // remove duplicate spaces return result; } std::string PCM::getCPUFamilyModelString() { char buffer[sizeof(int)*4*3+6]; std::fill(buffer, buffer + sizeof(buffer), 0); #ifdef _MSC_VER sprintf_s(buffer,sizeof(buffer),"GenuineIntel-%d-%2X-%X",this->cpu_family,this->cpu_model,this->cpu_stepping); #else snprintf(buffer,sizeof(buffer),"GenuineIntel-%d-%2X-%X",this->cpu_family,this->cpu_model,this->cpu_stepping); #endif std::string result(buffer); return result; } void PCM::enableForceRTMAbortMode(const bool silent) { // std::cout << "enableForceRTMAbortMode(): forceRTMAbortMode=" << forceRTMAbortMode << "\n"; if (!forceRTMAbortMode) { if (isForceRTMAbortModeAvailable() && (core_gen_counter_num_max < 4)) { for (auto& m : MSR) { const auto res = m->write(MSR_TSX_FORCE_ABORT, 1); if (res != sizeof(uint64)) { std::cerr << "Warning: writing 1 to MSR_TSX_FORCE_ABORT failed with error " << res << " on core " << m->getCoreId() << "\n"; } } readCoreCounterConfig(true); // re-read core_gen_counter_num_max from CPUID if (!silent) std::cerr << "The number of custom counters is now " << core_gen_counter_num_max << "\n"; if (core_gen_counter_num_max < 4) { std::cerr << "PCM Warning: the number of custom counters did not increase (" << core_gen_counter_num_max << ")\n"; } forceRTMAbortMode = true; } } } bool PCM::isForceRTMAbortModeEnabled() const { return forceRTMAbortMode; } void PCM::disableForceRTMAbortMode(const bool silent) { // std::cout << "disableForceRTMAbortMode(): forceRTMAbortMode=" << forceRTMAbortMode << "\n"; if (forceRTMAbortMode) { for (auto& m : MSR) { const auto res = m->write(MSR_TSX_FORCE_ABORT, 0); if (res != sizeof(uint64)) { std::cerr << "Warning: writing 0 to MSR_TSX_FORCE_ABORT failed with error " << res << " on core " << m->getCoreId() << "\n"; } } readCoreCounterConfig(true); // re-read core_gen_counter_num_max from CPUID if (!silent) std::cerr << "The number of custom counters is now " << core_gen_counter_num_max << "\n"; if (core_gen_counter_num_max != 3) { std::cerr << "PCM Warning: the number of custom counters is not 3 (" << core_gen_counter_num_max << ")\n"; } forceRTMAbortMode = false; } } bool PCM::isForceRTMAbortModeAvailable() const { PCM_CPUID_INFO info; pcm_cpuid(7, 0, info); // leaf 7, subleaf 0 return (info.reg.edx & (0x1 << 13)) ? true : false; } uint64 get_frequency_from_cpuid() // from Pat Fay (Intel) { double speed=0; std::string brand = PCM::getCPUBrandString(); if (brand.length() > std::string::size_type(0)) { std::string::size_type unitsg = brand.find("GHz"); if(unitsg != std::string::npos) { std::string::size_type atsign = brand.rfind(' ', unitsg); if(atsign != std::string::npos) { std::istringstream(brand.substr(atsign)) >> speed; speed *= 1000; } } else { std::string::size_type unitsg = brand.find("MHz"); if(unitsg != std::string::npos) { std::string::size_type atsign = brand.rfind(' ', unitsg); if(atsign != std::string::npos) { std::istringstream(brand.substr(atsign)) >> speed; } } } } return (uint64)(speed * 1000. * 1000.); } std::string PCM::getSupportedUarchCodenames() const { std::ostringstream ostr; for(int32 i=0; i < static_cast(PCM::END_OF_MODEL_LIST) ; ++i) if(isCPUModelSupported((int)i)) ostr << getUArchCodename(i) << ", "; return std::string(ostr.str().substr(0, ostr.str().length() - 2)); } std::string PCM::getUnsupportedMessage() const { std::ostringstream ostr; ostr << "Error: unsupported processor. Only Intel(R) processors are supported (Atom(R) and microarchitecture codename " << getSupportedUarchCodenames() << ")."; return std::string(ostr.str()); } void PCM::computeQPISpeedBeckton(int core_nr) { uint64 startFlits = 0; // reset all counters MSR[core_nr]->write(U_MSR_PMON_GLOBAL_CTL, 1 << 29ULL); // enable counting on all counters on the left side (port 0-3) MSR[core_nr]->write(R_MSR_PMON_GLOBAL_CTL_7_0, 255); // disable on the right side (port 4-7) MSR[core_nr]->write(R_MSR_PMON_GLOBAL_CTL_15_8, 0); // count flits sent MSR[core_nr]->write(R_MSR_PORT0_IPERF_CFG0, 1ULL << 31ULL); // choose counter 0 to monitor R_MSR_PORT0_IPERF_CFG0 MSR[core_nr]->write(R_MSR_PMON_CTL0, 1 + 2 * (0)); // enable all counters MSR[core_nr]->write(U_MSR_PMON_GLOBAL_CTL, (1 << 28ULL) + 1); MSR[core_nr]->read(R_MSR_PMON_CTR0, &startFlits); const uint64 timerGranularity = 1000000ULL; // mks uint64 startTSC = getTickCount(timerGranularity, (uint32) core_nr); uint64 endTSC; do { endTSC = getTickCount(timerGranularity, (uint32) core_nr); } while (endTSC - startTSC < 200000ULL); // spin for 200 ms uint64 endFlits = 0; MSR[core_nr]->read(R_MSR_PMON_CTR0, &endFlits); max_qpi_speed = (endFlits - startFlits) * 8ULL * timerGranularity / (endTSC - startTSC); } uint32 PCM::checkCustomCoreProgramming(std::shared_ptr msr) { const auto core = msr->getCoreId(); if (size_t(core) >= lastProgrammedCustomCounters.size() || canUsePerf) { // checking 'canUsePerf'because corruption detection currently works // only if perf is not used, see https://github.com/opcm/pcm/issues/106 return 0; } uint32 corruptedCountersMask = 0; for (size_t ctr = 0; ctr < lastProgrammedCustomCounters[core].size(); ++ctr) { EventSelectRegister current; if (msr->read(IA32_PERFEVTSEL0_ADDR + ctr, ¤t.value) != sizeof(current.value)) { std::cerr << "PCM Error: can not read MSR 0x" << std::hex << (IA32_PERFEVTSEL0_ADDR + ctr) << " on core " << std::dec << core << "\n"; continue; } if (canUsePerf) { current.fields.apic_int = 0; // perf sets this bit } if (current.value != lastProgrammedCustomCounters[core][ctr].value) { std::cerr << "PCM Error: someone has corrupted custom counter " << ctr << " on core " << core << " expected value " << lastProgrammedCustomCounters[core][ctr].value << " value read " << current.value << "\n"; corruptedCountersMask |= (1<= 4) { MSR[i]->read(MSR_PERF_GLOBAL_INUSE, &value); for (uint32 j = 0; j < core_gen_counter_num_max; ++j) { if (value & (1ULL << j)) { std::cerr << "WARNING: Custom counter " << j << " is in use. MSR_PERF_GLOBAL_INUSE on core " << i << ": 0x" << std::hex << value << std::dec << "\n"; /* Testing MSR_PERF_GLOBAL_INUSE mechanism for a moment. At a later point in time will report BUSY. return true; */ } } } MSR[i]->read(IA32_CR_PERF_GLOBAL_CTRL, &value); // std::cout << "Core " << i << " IA32_CR_PERF_GLOBAL_CTRL is " << std::hex << value << std::dec << "\n"; EventSelectRegister event_select_reg; event_select_reg.value = 0xFFFFFFFFFFFFFFFF; for (uint32 j = 0; j < core_gen_counter_num_max; ++j) { const auto count = MSR[i]->read(IA32_PERFEVTSEL0_ADDR + j, &event_select_reg.value); if (count && (event_select_reg.fields.event_select != 0 || event_select_reg.fields.apic_int != 0)) { std::cerr << "WARNING: Core " << i <<" IA32_PERFEVTSEL" << j << "_ADDR is not zeroed " << event_select_reg.value << "\n"; if (needToRestoreNMIWatchdog == true && event_select_reg.fields.event_select == 0x3C && event_select_reg.fields.umask == 0) { // NMI watchdog did not clear its event, ignore it continue; } return true; } } FixedEventControlRegister ctrl_reg; ctrl_reg.value = 0xffffffffffffffff; const auto count = MSR[i]->read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); // Check if someone has installed pmi handler on counter overflow. // If so, that agent might potentially need to change counter value // for the "sample after"-mode messing up PCM measurements if (count && (ctrl_reg.fields.enable_pmi0 || ctrl_reg.fields.enable_pmi1 || ctrl_reg.fields.enable_pmi2)) { std::cerr << "WARNING: Core " << i << " fixed ctrl:" << ctrl_reg.value << "\n"; if (needToRestoreNMIWatchdog == false) // if NMI watchdog did not clear the fields, ignore it { return true; } } #if 0 // either os=0,usr=0 (not running) or os=1,usr=1 (fits PCM modus) are ok, other combinations are not if(ctrl_reg.fields.os0 != ctrl_reg.fields.usr0 || ctrl_reg.fields.os1 != ctrl_reg.fields.usr1 || ctrl_reg.fields.os2 != ctrl_reg.fields.usr2) { std::cerr << "WARNING: Core " << i << " fixed ctrl:" << ctrl_reg.value << "\n"; return true; } #endif } #ifdef _MSC_VER // try to check if PMU is reserved using MSR driver auto hDriver = openMSRDriver(); if (hDriver != INVALID_HANDLE_VALUE) { DWORD reslength = 0; uint64 result = 0; BOOL status = DeviceIoControl(hDriver, IO_CTL_PMU_ALLOC_SUPPORT, NULL, 0, &result, sizeof(uint64), &reslength, NULL); if (status == TRUE && reslength == sizeof(uint64) && result == 1) { status = DeviceIoControl(hDriver, IO_CTL_PMU_ALLOC, NULL, 0, &result, sizeof(uint64), &reslength, NULL); if (status == FALSE) { std::cerr << "PMU can not be allocated with msr.sys driver. Error code is " << ((reslength == sizeof(uint64)) ? std::to_string(result) : "unknown") << " \n"; CloseHandle(hDriver); return true; } else { // std::cerr << "Successfully allocated PMU through msr.sys" << " \n"; } } CloseHandle(hDriver); } #endif //std::cout << std::flush return false; } const char * PCM::getUArchCodename(const int32 cpu_model_param) const { auto cpu_model_ = cpu_model_param; if(cpu_model_ < 0) cpu_model_ = this->cpu_model ; switch(cpu_model_) { case CENTERTON: return "Centerton"; case BAYTRAIL: return "Baytrail"; case AVOTON: return "Avoton"; case CHERRYTRAIL: return "Cherrytrail"; case APOLLO_LAKE: return "Apollo Lake"; case GEMINI_LAKE: return "Gemini Lake"; case DENVERTON: return "Denverton"; case SNOWRIDGE: return "Snowridge"; case NEHALEM_EP: case NEHALEM: return "Nehalem/Nehalem-EP"; case ATOM: return "Atom(tm)"; case CLARKDALE: return "Westmere/Clarkdale"; case WESTMERE_EP: return "Westmere-EP"; case NEHALEM_EX: return "Nehalem-EX"; case WESTMERE_EX: return "Westmere-EX"; case SANDY_BRIDGE: return "Sandy Bridge"; case JAKETOWN: return "Sandy Bridge-EP/Jaketown"; case IVYTOWN: return "Ivy Bridge-EP/EN/EX/Ivytown"; case HASWELLX: return "Haswell-EP/EN/EX"; case BDX_DE: return "Broadwell-DE"; case BDX: return "Broadwell-EP/EX"; case KNL: return "Knights Landing"; case IVY_BRIDGE: return "Ivy Bridge"; case HASWELL: return "Haswell"; case BROADWELL: return "Broadwell"; case SKL: return "Skylake"; case SKL_UY: return "Skylake U/Y"; case KBL: return "Kabylake"; case KBL_1: return "Kabylake/Whiskey Lake"; case CML: return "Comet Lake"; case ICL: return "Icelake"; case RKL: return "Rocket Lake"; case TGL: return "Tiger Lake"; case ADL: return "Alder Lake"; case RPL: return "Raptor Lake"; case SKX: if (cpu_model_param >= 0) { // query for specified cpu_model_param, stepping not provided return "Skylake-SP, Cascade Lake-SP"; } if (isCLX()) { return "Cascade Lake-SP"; } if (isCPX()) { return "Cooper Lake"; } return "Skylake-SP"; case ICX: return "Icelake-SP"; case SPR: return "Sapphire Rapids-SP"; } return "unknown"; } #ifdef PCM_USE_PERF void PCM::closePerfHandles(const bool silent) { if (canUsePerf) { auto cleanOne = [this](PerfEventHandleContainer & cont) { for (int i = 0; i < num_cores; ++i) { for(int c = 0; c < PERF_MAX_COUNTERS; ++c) { auto & h = cont[i][c]; if (h != -1) ::close(h); h = -1; } } }; cleanOne(perfEventHandle); for (auto & cont : perfEventTaskHandle) { cleanOne(cont); } perfEventTaskHandle.clear(); if (!silent) std::cerr << " Closed perf event handles\n"; } } #endif void PCM::cleanupPMU(const bool silent) { programmed_core_pmu = false; #ifdef PCM_USE_PERF closePerfHandles(silent); if (canUsePerf) { return; } #endif // follow the "Performance Monitoring Unit Sharing Guide" by P. Irelan and Sh. Kuo for (int i = 0; i < (int)num_cores; ++i) { // disable generic counters and continue free running counting for fixed counters MSR[i]->write(IA32_CR_PERF_GLOBAL_CTRL, (1ULL << 32) + (1ULL << 33) + (1ULL << 34)); for (uint32 j = 0; j < core_gen_counter_num_max; ++j) { MSR[i]->write(IA32_PERFEVTSEL0_ADDR + j, 0); } if (cleanupPEBS) { MSR[i]->write(IA32_PEBS_ENABLE_ADDR, 0ULL); } } cleanupPEBS = false; if(cpu_model == JAKETOWN) enableJKTWorkaround(false); #ifndef PCM_SILENT if (!silent) std::cerr << " Zeroed PMU registers\n"; #endif } #ifdef PCM_SILENT #pragma GCC diagnostic ignored "-Wunused-parameter" #endif void PCM::cleanupUncorePMUs(const bool silent) { for (auto & sPMUs : iioPMUs) { for (auto & pmu : sPMUs) { pmu.second.cleanup(); } } for (auto & sPMUs : idxPMUs) { for (auto & pmu : sPMUs) { pmu.cleanup(); } } for (auto& sPMUs : irpPMUs) { for (auto& pmu : sPMUs) { pmu.second.cleanup(); } } for (auto & sCBOPMUs : cboPMUs) { for (auto & pmu : sCBOPMUs) { pmu.cleanup(); } } for (auto& sMDFPMUs : mdfPMUs) { for (auto& pmu : sMDFPMUs) { pmu.cleanup(); } } for (auto & pmu : pcuPMUs) { pmu.cleanup(); } for (auto& sPMUs : cxlPMUs) { for (auto& pmus : sPMUs) { pmus.first.cleanup(); pmus.second.cleanup(); } } for (auto & uncore : serverUncorePMUs) { uncore->cleanupPMUs(); } #ifndef PCM_SILENT if (!silent) std::cerr << " Zeroed uncore PMU registers\n"; #endif } void PCM::resetPMU() { for (int i = 0; i < (int)MSR.size(); ++i) { // disable all counters MSR[i]->write(IA32_CR_PERF_GLOBAL_CTRL, 0); for (uint32 j = 0; j < core_gen_counter_num_max; ++j) { MSR[i]->write(IA32_PERFEVTSEL0_ADDR + j, 0); } FixedEventControlRegister ctrl_reg; ctrl_reg.value = 0xffffffffffffffff; MSR[i]->read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); if ((ctrl_reg.fields.os0 || ctrl_reg.fields.usr0 || ctrl_reg.fields.enable_pmi0 || ctrl_reg.fields.os1 || ctrl_reg.fields.usr1 || ctrl_reg.fields.enable_pmi1 || ctrl_reg.fields.os2 || ctrl_reg.fields.usr2 || ctrl_reg.fields.enable_pmi2) != 0) MSR[i]->write(IA32_CR_FIXED_CTR_CTRL, 0); } #ifndef PCM_SILENT std::cerr << " Zeroed PMU registers\n"; #endif } void PCM::cleanupRDT(const bool silent) { if(!(QOSMetricAvailable() && L3QOSMetricAvailable())) { return; } #ifdef __linux__ if (useResctrl) { resctrl.cleanup(); return; } #endif for(int32 core = 0; core < num_cores; core ++ ) { if(!isCoreOnline(core)) continue; uint64 msr_pqr_assoc = 0 ; uint64 msr_qm_evtsel = 0; int32 rmid = 0; int32 event = 0; //Read 0xC8F MSR for each core MSR[core]->read(IA32_PQR_ASSOC, &msr_pqr_assoc); msr_pqr_assoc &= 0xffffffff00000000ULL; //Write 0xC8F MSR with RMID 0 MSR[core]->write(IA32_PQR_ASSOC,msr_pqr_assoc); msr_qm_evtsel = rmid & ((1ULL<<10)-1ULL) ; msr_qm_evtsel <<= 32 ; msr_qm_evtsel |= event & ((1ULL<<8)-1ULL); //Write Event Id as 0 and RMID 0 to the MSR for each core MSR[core]->write(IA32_QM_EVTSEL,msr_qm_evtsel); } if (!silent) std::cerr << " Freeing up all RMIDs\n"; } void PCM::setOutput(const std::string filename, const bool cerrToo) { const auto pos = filename.find_last_of("/"); if (pos != std::string::npos) { const std::string dir_name = filename.substr(0, pos); struct stat info; if (stat(dir_name.c_str(), &info) != 0) { std::cerr << "Output directory: " << dir_name << " doesn't exist\n"; exit(EXIT_FAILURE); } } outfile = new std::ofstream(filename.c_str()); backup_ofile = std::cout.rdbuf(); std::cout.rdbuf(outfile->rdbuf()); if (cerrToo) { backup_ofile_cerr = std::cerr.rdbuf(); std::cerr.rdbuf(outfile->rdbuf()); } } void PCM::restoreOutput() { // restore cout back to what it was originally if(backup_ofile) std::cout.rdbuf(backup_ofile); if (backup_ofile_cerr) std::cerr.rdbuf(backup_ofile_cerr); // close output file if(outfile) outfile->close(); } void PCM::cleanup(const bool silent) { if (MSR.empty()) return; if (!silent) std::cerr << "Cleaning up\n"; cleanupPMU(silent); disableForceRTMAbortMode(silent); cleanupUncorePMUs(silent); cleanupRDT(silent); #ifdef __linux__ if (needToRestoreNMIWatchdog) { enableNMIWatchdog(silent); needToRestoreNMIWatchdog = false; } #endif #ifdef _MSC_VER // free PMU using MSR driver auto hDriver = openMSRDriver(); if (hDriver != INVALID_HANDLE_VALUE) { DWORD reslength = 0; uint64 result = 0; BOOL status = DeviceIoControl(hDriver, IO_CTL_PMU_ALLOC_SUPPORT, NULL, 0, &result, sizeof(uint64), &reslength, NULL); if (status == TRUE && reslength == sizeof(uint64) && result == 1) { status = DeviceIoControl(hDriver, IO_CTL_PMU_FREE, NULL, 0, &result, sizeof(uint64), &reslength, NULL); if (status == FALSE) { std::cerr << "PMU can not be freed with msr.sys driver. Error code is " << ((reslength == sizeof(uint64)) ? std::to_string(result) : "unknown") << " \n"; } } CloseHandle(hDriver); } #endif } // hle is only available when cpuid has this: // HLE: CPUID.07H.EBX.HLE [bit 4] = 1 bool PCM::supportsHLE() const { PCM_CPUID_INFO info; pcm_cpuid(7, 0, info); // leaf 7, subleaf 0 return (info.reg.ebx & (0x1 << 4)) ? true : false; } // rtm is only available when cpuid has this: // RTM: CPUID.07H.EBX.RTM [bit 11] = 1 bool PCM::supportsRTM() const { PCM_CPUID_INFO info; pcm_cpuid(7, 0, info); // leaf 7, subleaf 0 return (info.reg.ebx & (0x1 << 11)) ? true : false; } bool PCM::supportsRDTSCP() const { static int supports = -1; if (supports < 0) { PCM_CPUID_INFO info; pcm_cpuid(0x80000001, info); supports = (info.reg.edx & (0x1 << 27)) ? 1 : 0; } return 1 == supports; } #ifdef __APPLE__ int convertUnknownToInt(size_t size, char* value) { if(sizeof(int) == size) { return *(int*)value; } else if(sizeof(long) == size) { return *(long *)value; } else if(sizeof(long long) == size) { return *(long long *)value; } else { // In this case, we don't know what it is so we guess int return *(int *)value; } } #endif uint64 PCM::getTickCount(uint64 multiplier, uint32 core) { return (multiplier * getInvariantTSC_Fast(core)) / getNominalFrequency(); } uint64 PCM::getInvariantTSC_Fast(uint32 core) { if (supportsRDTSCP()) { TemporalThreadAffinity aff(core); return RDTSCP(); } else if (core < MSR.size()) { uint64 cInvariantTSC = 0; MSR[core]->read(IA32_TIME_STAMP_COUNTER, &cInvariantTSC); if (cInvariantTSC) return cInvariantTSC; } std::cerr << "ERROR: cannot read time stamp counter\n"; return 0ULL; } SystemCounterState getSystemCounterState() { PCM * inst = PCM::getInstance(); SystemCounterState result; if (inst) result = inst->getSystemCounterState(); return result; } SocketCounterState getSocketCounterState(uint32 socket) { PCM * inst = PCM::getInstance(); SocketCounterState result; if (inst) result = inst->getSocketCounterState(socket); return result; } CoreCounterState getCoreCounterState(uint32 core) { PCM * inst = PCM::getInstance(); CoreCounterState result; if (inst) result = inst->getCoreCounterState(core); return result; } #ifdef PCM_USE_PERF void PCM::readPerfData(uint32 core, std::vector & outData) { if (perfEventTaskHandle.empty() == false) { std::fill(outData.begin(), outData.end(), 0); for (const auto & handleArray : perfEventTaskHandle) { for (size_t ctr = 0; ctr < PERF_MAX_COUNTERS; ++ctr) { const int fd = handleArray[core][ctr]; if (fd != -1) { uint64 result{0ULL}; const int status = ::read(fd, &result, sizeof(result)); if (status != sizeof(result)) { std::cerr << "PCM Error: failed to read from Linux perf handle " << fd << "\n"; } else { outData[ctr] += result; } } } } return; } auto readPerfDataHelper = [this](const uint32 core, std::vector& outData, const uint32 leader, const uint32 num_counters) { if (perfEventHandle[core][leader] < 0) { std::fill(outData.begin(), outData.end(), 0); return; } uint64 data[1 + PERF_MAX_COUNTERS]; const int32 bytes2read = sizeof(uint64) * (1 + num_counters); assert(num_counters <= PERF_MAX_COUNTERS); int result = ::read(perfEventHandle[core][leader], data, bytes2read); // data layout: nr counters; counter 0, counter 1, counter 2,... if (result != bytes2read) { std::cerr << "Error while reading perf data. Result is " << result << "\n"; std::cerr << "Check if you run other competing Linux perf clients.\n"; } else if (data[0] != num_counters) { std::cerr << "Number of counters read from perf is wrong. Elements read: " << data[0] << "\n"; } else { /* if (core == 0) { std::unique_lock _(instanceCreationMutex); std::cerr << "DEBUG: perf raw: " << std::dec; for (uint32 p=0; p < (1 + num_counters) ; ++p) std::cerr << data[p] << " "; std::cerr << "\n"; } */ // copy all counters, they start from position 1 in data std::copy((data + 1), (data + 1) + data[0], outData.begin()); } }; readPerfDataHelper(core, outData, PERF_GROUP_LEADER_COUNTER, core_fixed_counter_num_used + core_gen_counter_num_used); if (isHWTMAL1Supported() && perfSupportsTopDown()) { std::vector outTopDownData(outData.size(), 0); readPerfDataHelper(core, outTopDownData, PERF_TOPDOWN_GROUP_LEADER_COUNTER, PERF_TOPDOWN_COUNTERS); std::copy(outTopDownData.begin(), outTopDownData.begin() + PERF_TOPDOWN_COUNTERS, outData.begin() + core_fixed_counter_num_used + core_gen_counter_num_used); } } #endif void BasicCounterState::readAndAggregateTSC(std::shared_ptr msr) { uint64 cInvariantTSC = 0; PCM * m = PCM::getInstance(); const auto cpu_model = m->getCPUModel(); if (m->isAtom() == false || cpu_model == PCM::AVOTON) { cInvariantTSC = m->getInvariantTSC_Fast(msr->getCoreId()); MSRValues[IA32_TIME_STAMP_COUNTER] = cInvariantTSC; } else { #ifdef _MSC_VER cInvariantTSC = ((static_cast(GetTickCount()/1000ULL)))*m->getNominalFrequency(); #else struct timeval tp; gettimeofday(&tp, NULL); cInvariantTSC = (double(tp.tv_sec) + tp.tv_usec / 1000000.)*m->getNominalFrequency(); #endif } InvariantTSC += cInvariantTSC; } void BasicCounterState::readAndAggregate(std::shared_ptr msr) { uint64 cInstRetiredAny = 0, cCpuClkUnhaltedThread = 0, cCpuClkUnhaltedRef = 0; uint64 cL3Occupancy = 0; uint64 cCustomEvents[PERF_MAX_CUSTOM_COUNTERS] = {0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL, 0ULL }; uint64 cCStateResidency[PCM::MAX_C_STATE + 1]; std::fill(cCStateResidency, cCStateResidency + PCM::MAX_C_STATE + 1, 0); uint64 thermStatus = 0; uint64 cSMICount = 0; uint64 cFrontendBoundSlots = 0; uint64 cBadSpeculationSlots = 0; uint64 cBackendBoundSlots = 0; uint64 cRetiringSlots = 0; uint64 cAllSlotsRaw = 0; const int32 core_id = msr->getCoreId(); TemporalThreadAffinity tempThreadAffinity(core_id); // speedup trick for Linux PCM * m = PCM::getInstance(); assert(m); const auto core_global_ctrl_value = m->core_global_ctrl_value; const bool freezeUnfreeze = m->canUsePerf == false && core_global_ctrl_value != 0ULL; if (freezeUnfreeze) { msr->write(IA32_CR_PERF_GLOBAL_CTRL, 0ULL); // freeze } const int32 core_gen_counter_num_max = m->getMaxCustomCoreEvents(); uint64 overflows = 0; const auto corruptedCountersMask = m->checkCustomCoreProgramming(msr); // reading core PMU counters #ifdef PCM_USE_PERF if(m->canUsePerf) { std::vector perfData(PERF_MAX_COUNTERS, 0ULL); m->readPerfData(msr->getCoreId(), perfData); cInstRetiredAny = perfData[PCM::PERF_INST_RETIRED_POS]; cCpuClkUnhaltedThread = perfData[PCM::PERF_CPU_CLK_UNHALTED_THREAD_POS]; cCpuClkUnhaltedRef = perfData[PCM::PERF_CPU_CLK_UNHALTED_REF_POS]; for (int i = 0; i < core_gen_counter_num_max; ++i) { cCustomEvents[i] = perfData[PCM::PERF_GEN_EVENT_0_POS + i]; } if (m->isHWTMAL1Supported() && perfSupportsTopDown()) { cFrontendBoundSlots = perfData[m->perfTopDownPos[PCM::PERF_TOPDOWN_FRONTEND_POS]]; cBadSpeculationSlots = perfData[m->perfTopDownPos[PCM::PERF_TOPDOWN_BADSPEC_POS]]; cBackendBoundSlots = perfData[m->perfTopDownPos[PCM::PERF_TOPDOWN_BACKEND_POS]]; cRetiringSlots = perfData[m->perfTopDownPos[PCM::PERF_TOPDOWN_RETIRING_POS]]; cAllSlotsRaw = perfData[m->perfTopDownPos[PCM::PERF_TOPDOWN_SLOTS_POS]]; // if (core_id == 0) std::cout << "DEBUG: All: "<< cAllSlotsRaw << " FE: " << cFrontendBoundSlots << " BAD-SP: " << cBadSpeculationSlots << " BE: " << cBackendBoundSlots << " RET: " << cRetiringSlots << std::endl; } } else #endif { { msr->read(IA32_PERF_GLOBAL_STATUS, &overflows); // read overflows // std::cerr << "Debug " << core_id << " IA32_PERF_GLOBAL_STATUS: " << overflows << std::endl; msr->read(INST_RETIRED_ADDR, &cInstRetiredAny); msr->read(CPU_CLK_UNHALTED_THREAD_ADDR, &cCpuClkUnhaltedThread); msr->read(CPU_CLK_UNHALTED_REF_ADDR, &cCpuClkUnhaltedRef); for (int i = 0; i < core_gen_counter_num_max; ++i) { msr->read(IA32_PMC0 + i, &cCustomEvents[i]); } } msr->write(IA32_PERF_GLOBAL_OVF_CTRL, overflows); // clear overflows if (m->isHWTMAL1Supported()) { uint64 perfMetrics = 0, slots = 0; msr->lock(); msr->read(PERF_METRICS_ADDR, &perfMetrics); msr->read(TOPDOWN_SLOTS_ADDR, &slots); msr->write(PERF_METRICS_ADDR, 0); msr->write(TOPDOWN_SLOTS_ADDR, 0); cFrontendBoundSlots = extract_bits(perfMetrics, 16, 23); cBadSpeculationSlots = extract_bits(perfMetrics, 8, 15); cBackendBoundSlots = extract_bits(perfMetrics, 24, 31); cRetiringSlots = extract_bits(perfMetrics, 0, 7); const double total = double(cFrontendBoundSlots + cBadSpeculationSlots + cBackendBoundSlots + cRetiringSlots); if (total != 0) { cFrontendBoundSlots = m->FrontendBoundSlots[core_id] += uint64((double(cFrontendBoundSlots) / total) * double(slots)); cBadSpeculationSlots = m->BadSpeculationSlots[core_id] += uint64((double(cBadSpeculationSlots) / total) * double(slots)); cBackendBoundSlots = m->BackendBoundSlots[core_id] += uint64((double(cBackendBoundSlots) / total) * double(slots)); cRetiringSlots = m->RetiringSlots[core_id] += uint64((double(cRetiringSlots) / total) * double(slots)); } cAllSlotsRaw = m->AllSlotsRaw[core_id] += slots; // std::cout << "DEBUG: "<< slots << " " << cFrontendBoundSlots << " " << cBadSpeculationSlots << " " << cBackendBoundSlots << " " << cRetiringSlots << std::endl; msr->unlock(); } } for (int i = 0; i < core_gen_counter_num_max; ++i) { if (corruptedCountersMask & (1<getCoreId() << " " << cInstRetiredAny << " \n"; if (m->L3CacheOccupancyMetricAvailable() && m->useResctrl == false) { msr->lock(); uint64 event = 1; m->initQOSevent(event, core_id); msr->read(IA32_QM_CTR, &cL3Occupancy); //std::cout << "readAndAggregate reading IA32_QM_CTR " << std::dec << cL3Occupancy << std::dec << "\n"; msr->unlock(); } m->readAndAggregateMemoryBWCounters(static_cast(core_id), *this); readAndAggregateTSC(msr); // reading core C state counters for (int i = 0; i <= (int)(PCM::MAX_C_STATE); ++i) { if (m->coreCStateMsr && m->coreCStateMsr[i]) { const auto index = m->coreCStateMsr[i]; msr->read(index, &(cCStateResidency[i])); MSRValues[index] = cCStateResidency[i]; } } // reading temperature msr->read(MSR_IA32_THERM_STATUS, &thermStatus); MSRValues[MSR_IA32_THERM_STATUS] = thermStatus; msr->read(MSR_SMI_COUNT, &cSMICount); MSRValues[MSR_SMI_COUNT] = cSMICount; InstRetiredAny += checked_uint64(m->extractCoreFixedCounterValue(cInstRetiredAny), extract_bits(overflows, 32, 32)); CpuClkUnhaltedThread += checked_uint64(m->extractCoreFixedCounterValue(cCpuClkUnhaltedThread), extract_bits(overflows, 33, 33)); CpuClkUnhaltedRef += checked_uint64(m->extractCoreFixedCounterValue(cCpuClkUnhaltedRef), extract_bits(overflows, 34, 34)); for (int i = 0; i < core_gen_counter_num_max; ++i) { Event[i] += checked_uint64(m->extractCoreGenCounterValue(cCustomEvents[i]), extract_bits(overflows, i, i)); } #ifdef __linux__ if (m->useResctrl) { L3Occupancy = m->resctrl.getL3OCC(core_id) / 1024; } else #endif { //std::cout << "Scaling Factor " << m->L3ScalingFactor; cL3Occupancy = m->extractQOSMonitoring(cL3Occupancy); L3Occupancy = (cL3Occupancy==PCM_INVALID_QOS_MONITORING_DATA)? PCM_INVALID_QOS_MONITORING_DATA : (uint64)((double)(cL3Occupancy * m->L3ScalingFactor) / 1024.0); } for(int i=0; i <= int(PCM::MAX_C_STATE);++i) CStateResidency[i] += cCStateResidency[i]; ThermalHeadroom = extractThermalHeadroom(thermStatus); SMICount += cSMICount; FrontendBoundSlots += cFrontendBoundSlots; BadSpeculationSlots += cBadSpeculationSlots; BackendBoundSlots += cBackendBoundSlots; RetiringSlots += cRetiringSlots; AllSlotsRaw += cAllSlotsRaw; if (freezeUnfreeze) { msr->write(IA32_CR_PERF_GLOBAL_CTRL, core_global_ctrl_value); // unfreeze } } PCM::ErrorCode PCM::programServerUncoreLatencyMetrics(bool enable_pmm) { uint32 DDRConfig[4] = {0,0,0,0}; if (enable_pmm == false) { //DDR is false if (ICX == cpu_model || SPR == cpu_model) { DDRConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x80) + MC_CH_PCI_PMON_CTL_UMASK(1); // DRAM RPQ occupancy DDRConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x10) + MC_CH_PCI_PMON_CTL_UMASK(1); // DRAM RPQ Insert DDRConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x81) + MC_CH_PCI_PMON_CTL_UMASK(0); // DRAM WPQ Occupancy DDRConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(0x20) + MC_CH_PCI_PMON_CTL_UMASK(0); // DRAM WPQ Insert } else { DDRConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x80) + MC_CH_PCI_PMON_CTL_UMASK(0); // DRAM RPQ occupancy DDRConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x10) + MC_CH_PCI_PMON_CTL_UMASK(0); // DRAM RPQ Insert DDRConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x81) + MC_CH_PCI_PMON_CTL_UMASK(0); // DRAM WPQ Occupancy DDRConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(0x20) + MC_CH_PCI_PMON_CTL_UMASK(0); // DRAM WPQ Insert } } else { DDRConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0xe0) + MC_CH_PCI_PMON_CTL_UMASK(1); // PMM RDQ occupancy DDRConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0xe3) + MC_CH_PCI_PMON_CTL_UMASK(0); // PMM RDQ Insert DDRConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0xe4) + MC_CH_PCI_PMON_CTL_UMASK(1); // PMM WPQ Occupancy DDRConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(0xe7) + MC_CH_PCI_PMON_CTL_UMASK(0); // PMM WPQ Insert } if (DDRLatencyMetricsAvailable()) { for (size_t i = 0; i < (size_t)serverUncorePMUs.size(); ++i) { serverUncorePMUs[i]->programIMC(DDRConfig); } } return PCM::Success; } PCM::ErrorCode PCM::programServerUncoreMemoryMetrics(const ServerUncoreMemoryMetrics & metrics, int rankA, int rankB) { if (MSR.empty() || serverUncorePMUs.empty()) return PCM::MSRAccessDenied; for (int i = 0; (i < (int)serverUncorePMUs.size()) && MSR.size(); ++i) { serverUncorePMUs[i]->programServerUncoreMemoryMetrics(metrics, rankA, rankB); } programCXLCM(); return PCM::Success; } PCM::ErrorCode PCM::programServerUncorePowerMetrics(int mc_profile, int pcu_profile, int * freq_bands) { if(MSR.empty() || serverUncorePMUs.empty()) return PCM::MSRAccessDenied; uint32 PCUCntConf[4] = {0,0,0,0}; switch (cpu_model) { case SPR: PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(1); // clock ticks break; default: PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0); // clock ticks } switch(pcu_profile) { case 0: PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0xB); // FREQ_BAND0_CYCLES PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0xC); // FREQ_BAND1_CYCLES PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0xD); // FREQ_BAND2_CYCLES break; case 1: switch (cpu_model) { case SPR: PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x35); // POWER_STATE_OCCUPANCY.C0 PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x36); // POWER_STATE_OCCUPANCY.C3 PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x37); // POWER_STATE_OCCUPANCY.C6 break; default: PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x80) + PCU_MSR_PMON_CTL_OCC_SEL(1); // POWER_STATE_OCCUPANCY.C0 using CLOCKTICKS + 8th-bit PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x80) + PCU_MSR_PMON_CTL_OCC_SEL(2); // POWER_STATE_OCCUPANCY.C3 using CLOCKTICKS + 8th-bit PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x80) + PCU_MSR_PMON_CTL_OCC_SEL(3); // POWER_STATE_OCCUPANCY.C6 using CLOCKTICKS + 8th-bit } break; case 2: PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x09); // PROCHOT_INTERNAL_CYCLES PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x0A); // PROCHOT_EXTERNAL_CYCLES PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x04); // Thermal frequency limit cycles: FREQ_MAX_LIMIT_THERMAL_CYCLES break; case 3: PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x04); // Thermal frequency limit cycles: FREQ_MAX_LIMIT_THERMAL_CYCLES PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x05); // Power frequency limit cycles: FREQ_MAX_POWER_CYCLES PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x07); // Clipped frequency limit cycles: FREQ_MAX_CURRENT_CYCLES (not supported on SKX,ICX,SNOWRIDGE,SPR) break; case 4: // not supported on SKX, ICX, SNOWRIDGE, SPR PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x06); // OS frequency limit cycles: FREQ_MAX_OS_CYCLES PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x05); // Power frequency limit cycles: FREQ_MAX_POWER_CYCLES PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x07); // Clipped frequency limit cycles: FREQ_MAX_CURRENT_CYCLES (not supported on SKX and ICX and SNOWRIDGE) break; case 5: if(JAKETOWN == cpu_model) { PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0) + PCU_MSR_PMON_CTL_EXTRA_SEL + PCU_MSR_PMON_CTL_EDGE_DET ; // number of frequency transitions PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0) + PCU_MSR_PMON_CTL_EXTRA_SEL ; // cycles spent changing frequency } else if (IVYTOWN == cpu_model ) { PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x60) + PCU_MSR_PMON_CTL_EDGE_DET ; // number of frequency transitions PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x60) ; // cycles spent changing frequency: FREQ_TRANS_CYCLES } else if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model || ICX == cpu_model || SNOWRIDGE == cpu_model || SPR == cpu_model) { PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x74) + PCU_MSR_PMON_CTL_EDGE_DET ; // number of frequency transitions PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x74) ; // cycles spent changing frequency: FREQ_TRANS_CYCLES if(HASWELLX == cpu_model) { PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x79) + PCU_MSR_PMON_CTL_EDGE_DET ; // number of UFS transitions PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x79) ; // UFS transition cycles } } else { std::cerr << "ERROR: no frequency transition events defined for CPU model " << cpu_model << "\n"; } break; case 6: if (IVYTOWN == cpu_model ) { PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x2B) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC2 transitions PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x2D) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC6 transitions } else if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model || ICX == cpu_model || SNOWRIDGE == cpu_model || SPR == cpu_model) { PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x4E) ; // PC1e residenicies (not supported on SKX,ICX,SNOWRIDGE,SPR) PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x4E) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC1 transitions (not supported on SKX,ICX,SNOWRIDGE,SPR) PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x2B) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC2 transitions PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x2D) + PCU_MSR_PMON_CTL_EDGE_DET ; // PC6 transitions } else { std::cerr << "ERROR: no package C-state transition events defined for CPU model " << cpu_model << "\n"; } break; case 7: if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model) { PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x7E) ; // UFS_TRANSITIONS_PERF_P_LIMIT PCUCntConf[1] = PCU_MSR_PMON_CTL_EVENT(0x7D) ; // UFS_TRANSITIONS_IO_P_LIMIT PCUCntConf[2] = PCU_MSR_PMON_CTL_EVENT(0x7A) ; // UFS_TRANSITIONS_UP_RING_TRAFFIC PCUCntConf[3] = PCU_MSR_PMON_CTL_EVENT(0x7B) ; // UFS_TRANSITIONS_UP_STALL_CYCLES } else { std::cerr << "ERROR: no UFS transition events defined for CPU model " << cpu_model << "\n"; } break; case 8: if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model) { PCUCntConf[0] = PCU_MSR_PMON_CTL_EVENT(0x7C) ; // UFS_TRANSITIONS_DOWN } else { std::cerr << "ERROR: no UFS transition events defined for CPU model " << cpu_model << "\n"; } break; default: std::cerr << "ERROR: unsupported PCU profile " << pcu_profile << "\n"; } for (auto& u : serverUncorePMUs) { u->program_power_metrics(mc_profile); } uint64 filter = 0; if (freq_bands == NULL) { filter = PCU_MSR_PMON_BOX_FILTER_BAND_0(10) + // 1000 MHz PCU_MSR_PMON_BOX_FILTER_BAND_1(20) + // 2000 MHz PCU_MSR_PMON_BOX_FILTER_BAND_2(30); // 3000 MHz } else { filter = PCU_MSR_PMON_BOX_FILTER_BAND_0(freq_bands[0]) + PCU_MSR_PMON_BOX_FILTER_BAND_1(freq_bands[1]) + PCU_MSR_PMON_BOX_FILTER_BAND_2(freq_bands[2]); } programPCU(PCUCntConf, filter); return PCM::Success; } void PCM::programPCU(uint32* PCUCntConf, const uint64 filter) { for (int i = 0; (i < (int)serverUncorePMUs.size()) && MSR.size(); ++i) { if (i >= (int)pcuPMUs.size()) { continue; } uint32 refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux pcuPMUs[i].initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); if (pcuPMUs[i].filter[0].get()) { *pcuPMUs[i].filter[0] = filter; } program(pcuPMUs[i], &PCUCntConf[0], &PCUCntConf[4], UNC_PMON_UNIT_CTL_FRZ_EN); } } PCM::ErrorCode PCM::program(const RawPMUConfigs& curPMUConfigs_, const bool silent, const int pid) { if (MSR.empty()) return PCM::MSRAccessDenied; threadMSRConfig = RawPMUConfig{}; packageMSRConfig = RawPMUConfig{}; pcicfgConfig = RawPMUConfig{}; RawPMUConfigs curPMUConfigs = curPMUConfigs_; constexpr auto globalRegPos = 0ULL; PCM::ExtendedCustomCoreEventDescription conf; auto updateRegs = [this, &conf](const RawPMUConfig& corePMUConfig, EventSelectRegister* regs) -> bool { if (corePMUConfig.programmable.size() > (size_t)getMaxCustomCoreEvents()) { std::cerr << "ERROR: trying to program " << corePMUConfig.programmable.size() << " core PMU counters, which exceeds the max num possible (" << getMaxCustomCoreEvents() << ").\n"; for (const auto& e : corePMUConfig.programmable) { std::cerr << " Event: " << e.second << "\n"; } return false; } size_t c = 0; for (; c < corePMUConfig.programmable.size() && c < (size_t)getMaxCustomCoreEvents() && c < PERF_MAX_CUSTOM_COUNTERS; ++c) { regs[c].value = corePMUConfig.programmable[c].first[0]; } conf.nGPCounters = (std::max)((uint32)c, conf.nGPCounters); return true; }; FixedEventControlRegister fixedReg; auto setOtherConf = [&conf, &fixedReg, &globalRegPos](const RawPMUConfig& corePMUConfig) { if ((size_t)globalRegPos < corePMUConfig.programmable.size()) { conf.OffcoreResponseMsrValue[0] = corePMUConfig.programmable[globalRegPos].first[OCR0Pos]; conf.OffcoreResponseMsrValue[1] = corePMUConfig.programmable[globalRegPos].first[OCR1Pos]; conf.LoadLatencyMsrValue = corePMUConfig.programmable[globalRegPos].first[LoadLatencyPos]; conf.FrontendMsrValue = corePMUConfig.programmable[globalRegPos].first[FrontendPos]; } if (corePMUConfig.fixed.empty()) { conf.fixedCfg = NULL; // default } else { fixedReg.value = 0; for (const auto& cfg : corePMUConfig.fixed) { fixedReg.value |= uint64(cfg.first[0]); } conf.fixedCfg = &fixedReg; } }; EventSelectRegister regs[PERF_MAX_CUSTOM_COUNTERS]; EventSelectRegister atomRegs[PERF_MAX_CUSTOM_COUNTERS]; conf.OffcoreResponseMsrValue[0] = 0; conf.OffcoreResponseMsrValue[1] = 0; if (curPMUConfigs.count("core") > 0) { // need to program core PMU first const auto & corePMUConfig = curPMUConfigs["core"]; if (updateRegs(corePMUConfig, regs) == false) { return PCM::UnknownError; } conf.gpCounterCfg = regs; setOtherConf(corePMUConfig); conf.defaultUncoreProgramming = false; curPMUConfigs.erase("core"); if (curPMUConfigs.count("atom")) { const auto & atomPMUConfig = curPMUConfigs["atom"]; if (updateRegs(atomPMUConfig, atomRegs) == false) { return PCM::UnknownError; } conf.gpCounterHybridAtomCfg = atomRegs; curPMUConfigs.erase("atom"); } const auto status = program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf, silent, pid); if (status != PCM::Success) { return status; } } else if (curPMUConfigs.count("atom") > 0) // no core, only atom { const auto& atomPMUConfig = curPMUConfigs["atom"]; if (updateRegs(atomPMUConfig, atomRegs) == false) { return PCM::UnknownError; } conf.gpCounterHybridAtomCfg = atomRegs; setOtherConf(atomPMUConfig); conf.defaultUncoreProgramming = false; curPMUConfigs.erase("atom"); const auto status = program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf, silent, pid); if (status != PCM::Success) { return status; } } for (auto& pmuConfig : curPMUConfigs) { const auto & type = pmuConfig.first; const auto & events = pmuConfig.second; if (events.programmable.empty() && events.fixed.empty()) { continue; } if (events.programmable.size() > ServerUncoreCounterState::maxCounters) { std::cerr << "ERROR: trying to program " << events.programmable.size() << " core PMU counters, which exceeds the max num possible (" << ServerUncoreCounterState::maxCounters << ")."; return PCM::UnknownError; } uint32 events32[ServerUncoreCounterState::maxCounters] = { 0,0,0,0,0,0,0,0 }; uint64 events64[ServerUncoreCounterState::maxCounters] = { 0,0,0,0,0,0,0,0 }; for (size_t c = 0; c < events.programmable.size() && c < ServerUncoreCounterState::maxCounters; ++c) { events32[c] = (uint32)events.programmable[c].first[0]; events64[c] = events.programmable[c].first[0]; } if (type == "m3upi") { for (auto& uncore : serverUncorePMUs) { uncore->programM3UPI(events32); } } else if (type == "xpi" || type == "upi" || type == "qpi") { for (auto& uncore : serverUncorePMUs) { uncore->programXPI(events32); } } else if (type == "imc") { for (auto& uncore : serverUncorePMUs) { uncore->programIMC(events32); } } else if (type == "m2m") { for (auto& uncore : serverUncorePMUs) { uncore->programM2M(events64); } } else if (type == "pcu") { uint64 filter = 0; if (globalRegPos < events.programmable.size()) { filter = events.programmable[globalRegPos].first[1]; } programPCU(events32, filter); } else if (type == "ubox") { programUBOX(events64); } else if (type == "cbo" || type == "cha") { uint64 filter0 = 0, filter1 = 0; if (globalRegPos < events.programmable.size()) { filter0 = events.programmable[globalRegPos].first[1]; filter1 = events.programmable[globalRegPos].first[2]; } programCboRaw(events64, filter0, filter1); } else if (type == "mdf") { programMDF(events64); } else if (type == "irp") { programIRPCounters(events64); } else if (type == "iio") { programIIOCounters(events64); } else if (type == "package_msr") { packageMSRConfig = pmuConfig.second; } else if (type == "thread_msr") { threadMSRConfig = pmuConfig.second; } else if (type == "pcicfg") { pcicfgConfig = pmuConfig.second; auto addLocations = [this](const std::vector& configs) { for (const auto& c : configs) { if (PCICFGRegisterLocations.find(c.first) == PCICFGRegisterLocations.end()) { // add locations std::vector locations; const auto deviceID = c.first[PCICFGEventPosition::deviceID]; forAllIntelDevices([&locations, &deviceID, &c](const uint32 group, const uint32 bus, const uint32 device, const uint32 function, const uint32 device_id) { if (deviceID == device_id && PciHandleType::exists(group, bus, device, function)) { // PciHandleType shared ptr, offset locations.push_back(PCICFGRegisterEncoding{ std::make_shared(group, bus, device, function), (uint32)c.first[PCICFGEventPosition::offset] }); } }); PCICFGRegisterLocations[c.first] = locations; } } }; addLocations(pcicfgConfig.programmable); addLocations(pcicfgConfig.fixed); } else if (type == "cxlcm") { programCXLCM(events64); } else if (type == "cxldp") { programCXLDP(events64); } else { std::cerr << "ERROR: unrecognized PMU type \"" << type << "\" when trying to program PMUs.\n"; return PCM::UnknownError; } } return PCM::Success; } void PCM::freezeServerUncoreCounters() { for (int i = 0; (i < (int)serverUncorePMUs.size()) && MSR.size(); ++i) { serverUncorePMUs[i]->freezeCounters(); const auto refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux pcuPMUs[i].freeze(UNC_PMON_UNIT_CTL_FRZ_EN); if (IIOEventsAvailable()) { for (auto & pmu : iioPMUs[i]) { pmu.second.freeze(UNC_PMON_UNIT_CTL_RSV); } } if (size_t(i) < irpPMUs.size()) { for (auto& pmu : irpPMUs[i]) { pmu.second.freeze(UNC_PMON_UNIT_CTL_RSV); } } if (size_t(i) < cboPMUs.size()) { for (auto& pmu : cboPMUs[i]) { pmu.freeze(UNC_PMON_UNIT_CTL_FRZ_EN); } } if (size_t(i) < mdfPMUs.size()) { for (auto& pmu : mdfPMUs[i]) { pmu.freeze(UNC_PMON_UNIT_CTL_FRZ_EN); } } } for (auto& sPMUs : cxlPMUs) { for (auto& pmus : sPMUs) { pmus.first.freeze(UNC_PMON_UNIT_CTL_FRZ_EN); pmus.second.freeze(UNC_PMON_UNIT_CTL_FRZ_EN); } } } void PCM::unfreezeServerUncoreCounters() { for (int i = 0; (i < (int)serverUncorePMUs.size()) && MSR.size(); ++i) { serverUncorePMUs[i]->unfreezeCounters(); const auto refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux pcuPMUs[i].unfreeze(UNC_PMON_UNIT_CTL_FRZ_EN); if (IIOEventsAvailable()) { for (auto & pmu : iioPMUs[i]) { pmu.second.unfreeze(UNC_PMON_UNIT_CTL_RSV); } } if (size_t(i) < irpPMUs.size()) { for (auto& pmu : irpPMUs[i]) { pmu.second.unfreeze(UNC_PMON_UNIT_CTL_RSV); } } if (size_t(i) < cboPMUs.size()) { for (auto& pmu : cboPMUs[i]) { pmu.unfreeze(UNC_PMON_UNIT_CTL_FRZ_EN); } } if (size_t(i) < mdfPMUs.size()) { for (auto& pmu : mdfPMUs[i]) { pmu.unfreeze(UNC_PMON_UNIT_CTL_FRZ_EN); } } } for (auto& sPMUs : cxlPMUs) { for (auto& pmus : sPMUs) { pmus.first.unfreeze(UNC_PMON_UNIT_CTL_FRZ_EN); pmus.second.unfreeze(UNC_PMON_UNIT_CTL_FRZ_EN); } } } void UncoreCounterState::readAndAggregate(std::shared_ptr msr) { const auto coreID = msr->getCoreId(); TemporalThreadAffinity tempThreadAffinity(coreID); // speedup trick for Linux auto pcm = PCM::getInstance(); pcm->readAndAggregatePackageCStateResidencies(msr, *this); } SystemCounterState PCM::getSystemCounterState() { SystemCounterState result; if (MSR.size()) { // read core and uncore counter state for (int32 core = 0; core < num_cores; ++core) if ( isCoreOnline( core ) ) result.readAndAggregate(MSR[core]); for (uint32 s = 0; s < (uint32)num_sockets; s++) { if ( isSocketOnline( s ) ) { readAndAggregateUncoreMCCounters(s, result); readAndAggregateEnergyCounters(s, result); } } readAndAggregateCXLCMCounters(result); readQPICounters(result); result.ThermalHeadroom = static_cast(PCM_INVALID_THERMAL_HEADROOM); // not available for system } return result; } template void PCM::readAndAggregateMemoryBWCounters(const uint32 core, CounterStateType & result) { #ifdef __linux__ if (useResctrl) { if (CoreLocalMemoryBWMetricAvailable()) { result.MemoryBWLocal += resctrl.getMBL(core) / (1024*1024); } if (CoreRemoteMemoryBWMetricAvailable()) { result.MemoryBWTotal += resctrl.getMBT(core) / (1024*1024); } return; } #endif uint64 cMemoryBWLocal = 0; uint64 cMemoryBWTotal = 0; if(core < memory_bw_local.size()) { cMemoryBWLocal = memory_bw_local[core]->read(); cMemoryBWLocal = extractQOSMonitoring(cMemoryBWLocal); //std::cout << "Read MemoryBWLocal " << cMemoryBWLocal << "\n"; if(cMemoryBWLocal==PCM_INVALID_QOS_MONITORING_DATA) result.MemoryBWLocal = PCM_INVALID_QOS_MONITORING_DATA; // do not accumulate invalid reading else result.MemoryBWLocal += (uint64)((double)(cMemoryBWLocal * L3ScalingFactor) / (1024.0 * 1024.0)); } if(core < memory_bw_total.size()) { cMemoryBWTotal = memory_bw_total[core]->read(); cMemoryBWTotal = extractQOSMonitoring(cMemoryBWTotal); //std::cout << "Read MemoryBWTotal " << cMemoryBWTotal << "\n"; if(cMemoryBWTotal==PCM_INVALID_QOS_MONITORING_DATA) result.MemoryBWTotal = PCM_INVALID_QOS_MONITORING_DATA; // do not accumulate invalid reading else result.MemoryBWTotal += (uint64)((double)(cMemoryBWTotal * L3ScalingFactor) / (1024.0 * 1024.0)); } //std::cout << std::flush; } template void PCM::readAndAggregateCXLCMCounters( CounterStateType & result) { for (size_t socket = 0; socket < getNumSockets(); ++socket) { uint64 CXLWriteMem = 0; uint64 CXLWriteCache = 0; for (size_t p = 0; p < getNumCXLPorts(socket); ++p) { CXLWriteMem += *cxlPMUs[socket][p].first.counterValue[0]; CXLWriteCache += *cxlPMUs[socket][p].first.counterValue[1]; } result.CXLWriteMem[socket] = CXLWriteMem; result.CXLWriteCache[socket] = CXLWriteCache; } } template void PCM::readAndAggregateUncoreMCCounters(const uint32 socket, CounterStateType & result) { if (LLCReadMissLatencyMetricsAvailable()) { result.TOROccupancyIAMiss += getCBOCounterState(socket, EventPosition::TOR_OCCUPANCY); result.TORInsertsIAMiss += getCBOCounterState(socket, EventPosition::TOR_INSERTS); } if (LLCReadMissLatencyMetricsAvailable() || uncoreFrequencyMetricAvailable()) { result.UncClocks += getUncoreClocks(socket); } const bool ReadMCStatsFromServerBW = (socket < serverBW.size()); if (ReadMCStatsFromServerBW) { result.UncMCNormalReads += serverBW[socket]->getImcReads(); result.UncMCFullWrites += serverBW[socket]->getImcWrites(); if (PMMTrafficMetricsAvailable()) { result.UncPMMReads += serverBW[socket]->getPMMReads(); result.UncPMMWrites += serverBW[socket]->getPMMWrites(); } } if (hasPCICFGUncore()) { if (serverUncorePMUs.size() && serverUncorePMUs[socket].get()) { serverUncorePMUs[socket]->freezeCounters(); if (ReadMCStatsFromServerBW == false) { result.UncMCNormalReads += serverUncorePMUs[socket]->getImcReads(); result.UncMCFullWrites += serverUncorePMUs[socket]->getImcWrites(); } if (localMemoryRequestRatioMetricAvailable()) { if (hasCHA()) { result.UncHARequests += getCBOCounterState(socket, EventPosition::REQUESTS_ALL); result.UncHALocalRequests += getCBOCounterState(socket, EventPosition::REQUESTS_LOCAL); } else { result.UncHARequests += serverUncorePMUs[socket]->getHARequests(); result.UncHALocalRequests += serverUncorePMUs[socket]->getHALocalRequests(); } } if (PMMTrafficMetricsAvailable() && (ReadMCStatsFromServerBW == false)) { result.UncPMMReads += serverUncorePMUs[socket]->getPMMReads(); result.UncPMMWrites += serverUncorePMUs[socket]->getPMMWrites(); } if (HBMmemoryTrafficMetricsAvailable()) { result.UncEDCNormalReads += serverUncorePMUs[socket]->getEdcReads(); result.UncEDCFullWrites += serverUncorePMUs[socket]->getEdcWrites(); } serverUncorePMUs[socket]->unfreezeCounters(); } } else if(clientBW.get() && socket == 0) { result.UncMCNormalReads += clientImcReads->read(); result.UncMCFullWrites += clientImcWrites->read(); result.UncMCGTRequests += clientGtRequests->read(); result.UncMCIARequests += clientIaRequests->read(); result.UncMCIORequests += clientIoRequests->read(); } else { std::shared_ptr msr = MSR[socketRefCore[socket]]; TemporalThreadAffinity tempThreadAffinity(socketRefCore[socket]); // speedup trick for Linux switch (cpu_model) { case PCM::WESTMERE_EP: case PCM::NEHALEM_EP: { uint64 cUncMCFullWrites = 0; uint64 cUncMCNormalReads = 0; msr->read(MSR_UNCORE_PMC0, &cUncMCFullWrites); msr->read(MSR_UNCORE_PMC1, &cUncMCNormalReads); result.UncMCFullWrites += extractUncoreGenCounterValue(cUncMCFullWrites); result.UncMCNormalReads += extractUncoreGenCounterValue(cUncMCNormalReads); } break; case PCM::NEHALEM_EX: case PCM::WESTMERE_EX: { uint64 cUncMCNormalReads = 0; msr->read(MB0_MSR_PMU_CNT_0, &cUncMCNormalReads); result.UncMCNormalReads += extractUncoreGenCounterValue(cUncMCNormalReads); msr->read(MB1_MSR_PMU_CNT_0, &cUncMCNormalReads); result.UncMCNormalReads += extractUncoreGenCounterValue(cUncMCNormalReads); uint64 cUncMCFullWrites = 0; // really good approximation of msr->read(BB0_MSR_PERF_CNT_1, &cUncMCFullWrites); result.UncMCFullWrites += extractUncoreGenCounterValue(cUncMCFullWrites); msr->read(BB1_MSR_PERF_CNT_1, &cUncMCFullWrites); result.UncMCFullWrites += extractUncoreGenCounterValue(cUncMCFullWrites); } break; default:; } } } template void PCM::readAndAggregateEnergyCounters(const uint32 socket, CounterStateType & result) { if(socket < (uint32)energy_status.size()) result.PackageEnergyStatus += energy_status[socket]->read(); if (socket < (uint32)dram_energy_status.size()) result.DRAMEnergyStatus += dram_energy_status[socket]->read(); } template void PCM::readMSRs(std::shared_ptr msr, const PCM::RawPMUConfig& msrConfig, CounterStateType& result) { auto read = [&msr, &result](const RawEventConfig & cfg) { const auto index = cfg.first[MSREventPosition::index]; if (result.MSRValues.find(index) == result.MSRValues.end()) { uint64 val{ 0 }; msr->read(index, &val); result.MSRValues[index] = val; } }; for (const auto& cfg : msrConfig.programmable) { read(cfg); } for (const auto& cfg : msrConfig.fixed) { read(cfg); } } template void PCM::readAndAggregatePackageCStateResidencies(std::shared_ptr msr, CounterStateType & result) { // reading package C state counters uint64 cCStateResidency[PCM::MAX_C_STATE + 1]; std::fill(cCStateResidency, cCStateResidency + PCM::MAX_C_STATE + 1, 0); for(int i=0; i <= int(PCM::MAX_C_STATE) ;++i) if(pkgCStateMsr && pkgCStateMsr[i]) msr->read(pkgCStateMsr[i], &(cCStateResidency[i])); for (int i = 0; i <= int(PCM::MAX_C_STATE); ++i) { atomic_fetch_add((std::atomic *)(result.CStateResidency + i), cCStateResidency[i]); } } void PCM::readPCICFGRegisters(SystemCounterState& systemState) { auto read = [this, &systemState](const RawEventConfig& cfg) { const RawEventEncoding& reEnc = cfg.first; systemState.PCICFGValues[reEnc].clear(); for (auto& reg : PCICFGRegisterLocations[reEnc]) { const auto width = reEnc[PCICFGEventPosition::width]; auto& h = reg.first; const auto& offset = reg.second; if (h.get()) { uint64 value = ~0ULL; uint32 value32 = 0; switch (width) { case 16: h->read32(offset, &value32); value = (uint64)extract_bits_ui(value32, 0, 15); break; case 32: h->read32(offset, &value32); value = (uint64)value32; break; case 64: h->read64(offset, &value); break; default: std::cerr << "ERROR: Unsupported width " << width << " for pcicfg register " << cfg.second << "\n"; } systemState.PCICFGValues[reEnc].push_back(value); } } }; for (const auto& cfg : pcicfgConfig.programmable) { read(cfg); } for (const auto& cfg : pcicfgConfig.fixed) { read(cfg); } } void PCM::readQPICounters(SystemCounterState & result) { // read QPI counters std::vector SocketProcessed(num_sockets, false); if (cpu_model == PCM::NEHALEM_EX || cpu_model == PCM::WESTMERE_EX) { for (int32 core = 0; core < num_cores; ++core) { if(isCoreOnline(core) == false) continue; if(core == socketRefCore[0]) MSR[core]->read(W_MSR_PMON_FIXED_CTR, &(result.uncoreTSC)); uint32 s = topology[core].socket; if (!SocketProcessed[s]) { TemporalThreadAffinity tempThreadAffinity(core); // speedup trick for Linux // incoming data responses from QPI link 0 MSR[core]->read(R_MSR_PMON_CTR1, &(result.incomingQPIPackets[s][0])); // incoming data responses from QPI link 1 (yes, from CTR0) MSR[core]->read(R_MSR_PMON_CTR0, &(result.incomingQPIPackets[s][1])); // incoming data responses from QPI link 2 MSR[core]->read(R_MSR_PMON_CTR8, &(result.incomingQPIPackets[s][2])); // incoming data responses from QPI link 3 MSR[core]->read(R_MSR_PMON_CTR9, &(result.incomingQPIPackets[s][3])); // outgoing idle flits from QPI link 0 MSR[core]->read(R_MSR_PMON_CTR3, &(result.outgoingQPIFlits[s][0])); // outgoing idle flits from QPI link 1 (yes, from CTR0) MSR[core]->read(R_MSR_PMON_CTR2, &(result.outgoingQPIFlits[s][1])); // outgoing idle flits from QPI link 2 MSR[core]->read(R_MSR_PMON_CTR10, &(result.outgoingQPIFlits[s][2])); // outgoing idle flits from QPI link 3 MSR[core]->read(R_MSR_PMON_CTR11, &(result.outgoingQPIFlits[s][3])); SocketProcessed[s] = true; } } } else if ((cpu_model == PCM::NEHALEM_EP || cpu_model == PCM::WESTMERE_EP)) { if (num_sockets == 2) { uint32 SCore[2] = { (uint32)socketRefCore[0], (uint32)socketRefCore[1] }; uint64 Total_Reads[2] = { 0, 0 }; uint64 Total_Writes[2] = { 0, 0 }; uint64 IOH_Reads[2] = { 0, 0 }; uint64 IOH_Writes[2] = { 0, 0 }; uint64 Remote_Reads[2] = { 0, 0 }; uint64 Remote_Writes[2] = { 0, 0 }; uint64 Local_Reads[2] = { 0, 0 }; uint64 Local_Writes[2] = { 0, 0 }; for (int s = 0; s < 2; ++s) { TemporalThreadAffinity tempThreadAffinity(SCore[s]); // speedup trick for Linux MSR[SCore[s]]->read(MSR_UNCORE_PMC0, &Total_Writes[s]); MSR[SCore[s]]->read(MSR_UNCORE_PMC1, &Total_Reads[s]); MSR[SCore[s]]->read(MSR_UNCORE_PMC2, &IOH_Reads[s]); MSR[SCore[s]]->read(MSR_UNCORE_PMC3, &IOH_Writes[s]); MSR[SCore[s]]->read(MSR_UNCORE_PMC4, &Remote_Reads[s]); MSR[SCore[s]]->read(MSR_UNCORE_PMC5, &Remote_Writes[s]); MSR[SCore[s]]->read(MSR_UNCORE_PMC6, &Local_Reads[s]); MSR[SCore[s]]->read(MSR_UNCORE_PMC7, &Local_Writes[s]); } #if 1 // compute Remote_Reads differently for (int s = 0; s < 2; ++s) { uint64 total = Total_Writes[s] + Total_Reads[s]; uint64 rem = IOH_Reads[s] + IOH_Writes[s] + Local_Reads[s] + Local_Writes[s] + Remote_Writes[s]; Remote_Reads[s] = (total > rem) ? (total - rem) : 0; } #endif // only an estimation (lower bound) - does not count NT stores correctly result.incomingQPIPackets[0][0] = Remote_Reads[1] + Remote_Writes[0]; result.incomingQPIPackets[0][1] = IOH_Reads[0]; result.incomingQPIPackets[1][0] = Remote_Reads[0] + Remote_Writes[1]; result.incomingQPIPackets[1][1] = IOH_Reads[1]; } else { // for a single socket systems no information is available result.incomingQPIPackets[0][0] = 0; } } else if (hasPCICFGUncore()) { for (int32 s = 0; (s < (int32)serverUncorePMUs.size()); ++s) { serverUncorePMUs[s]->freezeCounters(); for (uint32 port = 0; port < (uint32)getQPILinksPerSocket(); ++port) { result.incomingQPIPackets[s][port] = uint64(double(serverUncorePMUs[s]->getIncomingDataFlits(port)) / (64./getDataBytesPerFlit())); result.outgoingQPIFlits[s][port] = serverUncorePMUs[s]->getOutgoingFlits(port); result.TxL0Cycles[s][port] = serverUncorePMUs[s]->getUPIL0TxCycles(port); } serverUncorePMUs[s]->unfreezeCounters(); } } // end of reading QPI counters } template void PCM::readPackageThermalHeadroom(const uint32 socket, CounterStateType & result) { if(packageThermalMetricsAvailable()) { uint64 val = 0; MSR[socketRefCore[socket]]->read(MSR_PACKAGE_THERM_STATUS,&val); result.MSRValues[MSR_PACKAGE_THERM_STATUS] = val; result.ThermalHeadroom = extractThermalHeadroom(val); } else result.ThermalHeadroom = PCM_INVALID_THERMAL_HEADROOM; // not available } // Explicit instantiation needed in topology.cpp template void PCM::readAndAggregatePackageCStateResidencies(std::shared_ptr, UncoreCounterState &); template void PCM::readAndAggregateUncoreMCCounters(const uint32, UncoreCounterState&); template void PCM::readAndAggregateEnergyCounters(const uint32, UncoreCounterState&); template void PCM::readPackageThermalHeadroom(const uint32, SocketCounterState &); template void PCM::readAndAggregateCXLCMCounters(SystemCounterState &); SocketCounterState PCM::getSocketCounterState(uint32 socket) { SocketCounterState result; if (MSR.size()) { // reading core and uncore counter states for (int32 core = 0; core < num_cores; ++core) if (isCoreOnline(core) && (topology[core].socket == int32(socket))) result.readAndAggregate(MSR[core]); readAndAggregateUncoreMCCounters(socket, result); readAndAggregateEnergyCounters(socket, result); readPackageThermalHeadroom(socket, result); } return result; } void PCM::getAllCounterStates(SystemCounterState & systemState, std::vector & socketStates, std::vector & coreStates, const bool readAndAggregateSocketUncoreCounters) { // clear and zero-initialize all inputs systemState = SystemCounterState(); socketStates.clear(); socketStates.resize(num_sockets); coreStates.clear(); coreStates.resize(num_cores); std::vector > asyncCoreResults; for (int32 core = 0; core < num_cores; ++core) { // read core counters if (isCoreOnline(core)) { std::packaged_task task([this,&coreStates,&socketStates,core,readAndAggregateSocketUncoreCounters]() -> void { coreStates[core].readAndAggregate(MSR[core]); if (readAndAggregateSocketUncoreCounters) { socketStates[topology[core].socket].UncoreCounterState::readAndAggregate(MSR[core]); // read package C state counters } readMSRs(MSR[core], threadMSRConfig, coreStates[core]); } ); asyncCoreResults.push_back(task.get_future()); coreTaskQueues[core]->push(task); } // std::cout << "DEBUG2: " << core << " " << coreStates[core].InstRetiredAny << " \n"; } // std::cout << std::flush; for (uint32 s = 0; s < (uint32)num_sockets && readAndAggregateSocketUncoreCounters; ++s) { int32 refCore = socketRefCore[s]; if (refCore<0) refCore = 0; std::packaged_task task([this, s, &socketStates, refCore]() -> void { readAndAggregateUncoreMCCounters(s, socketStates[s]); readAndAggregateEnergyCounters(s, socketStates[s]); readPackageThermalHeadroom(s, socketStates[s]); readMSRs(MSR[refCore], packageMSRConfig, socketStates[s]); } ); asyncCoreResults.push_back(task.get_future()); coreTaskQueues[refCore]->push(task); } if (readAndAggregateSocketUncoreCounters) { readQPICounters(systemState); readPCICFGRegisters(systemState); } for (auto & ar : asyncCoreResults) ar.wait(); for (int32 core = 0; core < num_cores; ++core) { // aggregate core counters into sockets if(isCoreOnline(core)) socketStates[topology[core].socket] += coreStates[core]; } for (int32 s = 0; s < num_sockets; ++s) { // aggregate core counters from sockets into system state and // aggregate socket uncore iMC, energy and package C state counters into system systemState += socketStates[s]; } } void PCM::getUncoreCounterStates(SystemCounterState & systemState, std::vector & socketStates) { // clear and zero-initialize all inputs systemState = SystemCounterState(); socketStates.clear(); socketStates.resize(num_sockets); std::vector refCoreStates(num_sockets); for (uint32 s = 0; s < (uint32)num_sockets; ++s) { const int32 refCore = socketRefCore[s]; if(isCoreOnline(refCore)) { refCoreStates[s].readAndAggregateTSC(MSR[refCore]); } readAndAggregateUncoreMCCounters(s, socketStates[s]); readAndAggregateEnergyCounters(s, socketStates[s]); readPackageThermalHeadroom(s, socketStates[s]); } readQPICounters(systemState); for (int32 s = 0; s < num_sockets; ++s) { const int32 refCore = socketRefCore[s]; if(isCoreOnline(refCore)) { for(uint32 core=0; core < getNumCores(); ++core) { if(topology[core].socket == s && isCoreOnline(core)) socketStates[s] += refCoreStates[s]; } } // aggregate socket uncore iMC, energy counters into system systemState += socketStates[s]; } } CoreCounterState PCM::getCoreCounterState(uint32 core) { CoreCounterState result; if (MSR.size()) result.readAndAggregate(MSR[core]); return result; } uint32 PCM::getNumCores() const { return (uint32)num_cores; } uint32 PCM::getNumOnlineCores() const { return (uint32)num_online_cores; } uint32 PCM::getNumSockets() const { return (uint32)num_sockets; } uint32 PCM::getNumOnlineSockets() const { return (uint32)num_online_sockets; } uint32 PCM::getThreadsPerCore() const { return (uint32)threads_per_core; } bool PCM::getSMT() const { return threads_per_core > 1; } uint64 PCM::getNominalFrequency() const { return nominal_frequency; } uint32 PCM::getL3ScalingFactor() const { PCM_CPUID_INFO cpuinfo; pcm_cpuid(0xf,0x1,cpuinfo); return (uint32)cpuinfo.reg.ebx; } bool PCM::isSomeCoreOfflined() { PCM_CPUID_INFO cpuid_args; pcm_cpuid(0xB,1,cpuid_args); uint32 max_num_lcores_per_socket = cpuid_args.reg.ebx & 0xFFFF; uint32 max_num_lcores = max_num_lcores_per_socket * getNumSockets(); if(threads_per_core == 1 && (getNumOnlineCores() * 2 == max_num_lcores)) // HT is disabled in the BIOS { return false; } return !(getNumOnlineCores() == max_num_lcores); } ServerUncoreCounterState PCM::getServerUncoreCounterState(uint32 socket) { ServerUncoreCounterState result; if (socket < serverBW.size() && serverBW[socket].get()) { result.freeRunningCounter[ServerUncoreCounterState::ImcReads] = serverBW[socket]->getImcReads(); result.freeRunningCounter[ServerUncoreCounterState::ImcWrites] = serverBW[socket]->getImcWrites(); result.freeRunningCounter[ServerUncoreCounterState::PMMReads] = serverBW[socket]->getPMMReads(); result.freeRunningCounter[ServerUncoreCounterState::PMMWrites] = serverBW[socket]->getPMMWrites(); } if(serverUncorePMUs.size() && serverUncorePMUs[socket].get()) { serverUncorePMUs[socket]->freezeCounters(); for(uint32 port=0;port < (uint32)serverUncorePMUs[socket]->getNumQPIPorts();++port) { assert(port < result.xPICounter.size()); for (uint32 cnt = 0; cnt < ServerUncoreCounterState::maxCounters; ++cnt) result.xPICounter[port][cnt] = serverUncorePMUs[socket]->getQPILLCounter(port, cnt); assert(port < result.M3UPICounter.size()); for (uint32 cnt = 0; cnt < ServerUncoreCounterState::maxCounters; ++cnt) result.M3UPICounter[port][cnt] = serverUncorePMUs[socket]->getM3UPICounter(port, cnt); } for (uint32 channel = 0; channel < (uint32)serverUncorePMUs[socket]->getNumMCChannels(); ++channel) { assert(channel < result.DRAMClocks.size()); result.DRAMClocks[channel] = serverUncorePMUs[socket]->getDRAMClocks(channel); assert(channel < result.MCCounter.size()); for (uint32 cnt = 0; cnt < ServerUncoreCounterState::maxCounters; ++cnt) result.MCCounter[channel][cnt] = serverUncorePMUs[socket]->getMCCounter(channel, cnt); } for (uint32 channel = 0; channel < (uint32)serverUncorePMUs[socket]->getNumEDCChannels(); ++channel) { assert(channel < result.HBMClocks.size()); result.HBMClocks[channel] = serverUncorePMUs[socket]->getHBMClocks(channel); assert(channel < result.EDCCounter.size()); for (uint32 cnt = 0; cnt < ServerUncoreCounterState::maxCounters; ++cnt) result.EDCCounter[channel][cnt] = serverUncorePMUs[socket]->getEDCCounter(channel, cnt); } for (uint32 controller = 0; controller < (uint32)serverUncorePMUs[socket]->getNumMC(); ++controller) { assert(controller < result.M2MCounter.size()); for (uint32 cnt = 0; cnt < ServerUncoreCounterState::maxCounters; ++cnt) result.M2MCounter[controller][cnt] = serverUncorePMUs[socket]->getM2MCounter(controller, cnt); } serverUncorePMUs[socket]->unfreezeCounters(); } if (MSR.size()) { uint32 refCore = socketRefCore[socket]; TemporalThreadAffinity tempThreadAffinity(refCore); for (uint32 cbo = 0; socket < cboPMUs.size() && cbo < cboPMUs[socket].size() && cbo < ServerUncoreCounterState::maxCBOs; ++cbo) { for (int i = 0; i < ServerUncoreCounterState::maxCounters && size_t(i) < cboPMUs[socket][cbo].size(); ++i) { result.CBOCounter[cbo][i] = *(cboPMUs[socket][cbo].counterValue[i]); } } for (uint32 mdf = 0; socket < mdfPMUs.size() && mdf < mdfPMUs[socket].size() && mdf < ServerUncoreCounterState::maxMDFs; ++mdf) { for (int i = 0; i < ServerUncoreCounterState::maxCounters && size_t(i) < mdfPMUs[socket][mdf].size(); ++i) { if (mdfPMUs[socket][mdf].counterValue[i].get()) { result.MDFCounter[mdf][i] = *(mdfPMUs[socket][mdf].counterValue[i]); } } } for (uint32 stack = 0; socket < iioPMUs.size() && stack < iioPMUs[socket].size() && stack < ServerUncoreCounterState::maxIIOStacks; ++stack) { for (int i = 0; i < ServerUncoreCounterState::maxCounters && size_t(i) < iioPMUs[socket][stack].size(); ++i) { result.IIOCounter[stack][i] = *(iioPMUs[socket][stack].counterValue[i]); } } for (uint32 stack = 0; socket < irpPMUs.size() && stack < irpPMUs[socket].size() && stack < ServerUncoreCounterState::maxIIOStacks; ++stack) { for (int i = 0; i < ServerUncoreCounterState::maxCounters && size_t(i) < irpPMUs[socket][stack].size(); ++i) { if (irpPMUs[socket][stack].counterValue[i].get()) { result.IRPCounter[stack][i] = *(irpPMUs[socket][stack].counterValue[i]); } } } for (int i = 0; i < 2 && socket < uboxPMUs.size(); ++i) { result.UBOXCounter[i] = *(uboxPMUs[socket].counterValue[i]); result.UncClocks = getUncoreClocks(socket); } for (int i = 0; i < ServerUncoreCounterState::maxCounters && socket < pcuPMUs.size() && size_t(i) < pcuPMUs[socket].size(); ++i) { result.PCUCounter[i] = *pcuPMUs[socket].counterValue[i]; } for (size_t p = 0; p < getNumCXLPorts(socket); ++p) { for (int i = 0; i < ServerUncoreCounterState::maxCounters && socket < cxlPMUs.size() && size_t(i) < cxlPMUs[socket][p].first.size(); ++i) { result.CXLCMCounter[p][i] = *cxlPMUs[socket][p].first.counterValue[i]; } for (int i = 0; i < ServerUncoreCounterState::maxCounters && socket < cxlPMUs.size() && size_t(i) < cxlPMUs[socket][p].second.size(); ++i) { result.CXLDPCounter[p][i] = *cxlPMUs[socket][p].second.counterValue[i]; } } // std::cout << "values read: " << result.PCUCounter[0] << " " << result.PCUCounter[1] << " " << result.PCUCounter[2] << " " << result.PCUCounter[3] << "\n"; uint64 val=0; //MSR[refCore]->read(MSR_PKG_ENERGY_STATUS,&val); //std::cout << "Energy status: " << val << "\n"; MSR[refCore]->read(MSR_PACKAGE_THERM_STATUS,&val); result.PackageThermalHeadroom = extractThermalHeadroom(val); result.InvariantTSC = getInvariantTSC_Fast(refCore); readAndAggregatePackageCStateResidencies(MSR[refCore], result); } // std::cout << std::flush; readAndAggregateEnergyCounters(socket, result); return result; } #ifndef _MSC_VER void print_mcfg(const char * path) { int mcfg_handle = ::open(path, O_RDONLY); if (mcfg_handle < 0) { std::cerr << "PCM Error: Cannot open " << path << "\n"; throw std::exception(); } MCFGHeader header; ssize_t read_bytes = ::read(mcfg_handle, (void *)&header, sizeof(MCFGHeader)); if(read_bytes == 0) { std::cerr << "PCM Error: Cannot read " << path << "\n"; ::close(mcfg_handle); throw std::exception(); } const unsigned segments = header.nrecords(); header.print(); std::cout << "Segments: " << segments << "\n"; for(unsigned int i=0; i > ServerUncorePMUs::socket2iMCbus{}; std::vector > ServerUncorePMUs::socket2UPIbus{}; std::vector > ServerUncorePMUs::socket2M2Mbus{}; void initSocket2Bus(std::vector > & socket2bus, uint32 device, uint32 function, const uint32 DEV_IDS[], uint32 devIdsSize) { if (device == PCM_INVALID_DEV_ADDR || function == PCM_INVALID_FUNC_ADDR) { return; } Mutex::Scope _(socket2busMutex); if(!socket2bus.empty()) return; forAllIntelDevices( [&devIdsSize,&DEV_IDS, &socket2bus](const uint32 group, const uint32 bus, const uint32 /* device */, const uint32 /* function */, const uint32 device_id) { for (uint32 i = 0; i < devIdsSize; ++i) { // match if (DEV_IDS[i] == device_id) { // std::cout << "DEBUG: found bus " << std::hex << bus << " with device ID " << device_id << std::dec << "\n"; socket2bus.push_back(std::make_pair(group, bus)); break; } } }, device, function); //std::cout << std::flush; } int getBusFromSocket(const uint32 socket) { int cur_bus = 0; uint32 cur_socket = 0; // std::cout << "socket: " << socket << "\n"; while(cur_socket <= socket) { // std::cout << "reading from bus 0x" << std::hex << cur_bus << std::dec << " "; PciHandleType h(0, cur_bus, 5, 0); uint32 cpubusno = 0; h.read32(0x108, &cpubusno); // CPUBUSNO register cur_bus = (cpubusno >> 8)& 0x0ff; // std::cout << "socket: " << cur_socket << std::hex << " cpubusno: 0x" << std::hex << cpubusno << " " << cur_bus << std::dec << "\n"; if(socket == cur_socket) return cur_bus; ++cur_socket; ++cur_bus; if(cur_bus > 0x0ff) return -1; } //std::cout << std::flush; return -1; } PciHandleType * ServerUncorePMUs::createIntelPerfMonDevice(uint32 groupnr_, int32 bus_, uint32 dev_, uint32 func_, bool checkVendor) { if (PciHandleType::exists(groupnr_, (uint32)bus_, dev_, func_)) { PciHandleType * handle = new PciHandleType(groupnr_, bus_, dev_, func_); if(!checkVendor) return handle; uint32 vendor_id = 0; handle->read32(PCM_PCI_VENDOR_ID_OFFSET,&vendor_id); vendor_id &= 0x0ffff; if(vendor_id == PCM_INTEL_PCI_VENDOR_ID) return handle; delete handle; } return NULL; } bool PCM::isSecureBoot() const { static int flag = -1; if (MSR.size() > 0 && flag == -1) { // std::cerr << "DEBUG: checking MSR in isSecureBoot\n"; uint64 val = 0; if (MSR[0]->read(IA32_PERFEVTSEL0_ADDR, &val) != sizeof(val)) { flag = 0; // some problem with MSR read, not secure boot } // read works if (MSR[0]->write(IA32_PERFEVTSEL0_ADDR, val) != sizeof(val)/* && errno == 1 */) // errno works only on windows { // write does not work -> secure boot flag = 1; } else { flag = 0; // can write MSR -> no secure boot } } return flag == 1; } bool PCM::useLinuxPerfForUncore() const { static int use = -1; if (use != -1) { return 1 == use; } use = 0; bool secureBoot = isSecureBoot(); #ifdef PCM_USE_PERF const auto imcIDs = enumeratePerfPMUs("imc", 100); std::cerr << "INFO: Linux perf interface to program uncore PMUs is " << (imcIDs.empty()?"NOT ":"") << "present\n"; if (imcIDs.empty()) { use = 0; return 1 == use; } const char * perf_env = std::getenv("PCM_USE_UNCORE_PERF"); if (perf_env != NULL && std::string(perf_env) == std::string("1")) { std::cerr << "INFO: using Linux perf interface to program uncore PMUs because env variable PCM_USE_UNCORE_PERF=1\n"; use = 1; } if (secureBoot) { std::cerr << "INFO: Secure Boot detected. Using Linux perf for uncore PMU programming.\n"; use = 1; } #else if (1) { if (secureBoot) { std::cerr << "ERROR: Secure Boot detected. Recompile PCM with -DPCM_USE_PERF or disable Secure Boot.\n"; } } #endif return 1 == use; } ServerUncorePMUs::ServerUncorePMUs(uint32 socket_, const PCM * pcm) : iMCbus(-1) , UPIbus(-1) , M2Mbus(-1) , groupnr(0) , cpu_model(pcm->getCPUModel()) , qpi_speed(0) { if (pcm->useLinuxPerfForUncore()) { initPerf(socket_, pcm); } else { initRegisterLocations(pcm); initBuses(socket_, pcm); initDirect(socket_, pcm); } std::cerr << "Socket " << socket_ << ": " << getNumMC() << " memory controllers detected with total number of " << getNumMCChannels() << " channels. " << getNumQPIPorts() << " " << pcm->xPI() << " ports detected." << " " << m2mPMUs.size() << " M2M (mesh to memory) blocks detected." " " << hbm_m2mPMUs.size() << " HBM M2M blocks detected." " " << edcPMUs.size() << " EDC/HBM channels detected." " " << haPMUs.size() << " Home Agents detected." " " << m3upiPMUs.size() << " M3UPI blocks detected." "\n"; } void ServerUncorePMUs::initRegisterLocations(const PCM * pcm) { #define PCM_PCICFG_MC_INIT(controller, channel, arch) \ MCRegisterLocation.resize(controller + 1); \ MCRegisterLocation[controller].resize(channel + 1); \ MCRegisterLocation[controller][channel] = \ std::make_pair(arch##_MC##controller##_CH##channel##_REGISTER_DEV_ADDR, arch##_MC##controller##_CH##channel##_REGISTER_FUNC_ADDR); #define PCM_PCICFG_QPI_INIT(port, arch) \ XPIRegisterLocation.resize(port + 1); \ XPIRegisterLocation[port] = std::make_pair(arch##_QPI_PORT##port##_REGISTER_DEV_ADDR, arch##_QPI_PORT##port##_REGISTER_FUNC_ADDR); #define PCM_PCICFG_M3UPI_INIT(port, arch) \ M3UPIRegisterLocation.resize(port + 1); \ M3UPIRegisterLocation[port] = std::make_pair(arch##_M3UPI_PORT##port##_REGISTER_DEV_ADDR, arch##_M3UPI_PORT##port##_REGISTER_FUNC_ADDR); #define PCM_PCICFG_EDC_INIT(controller, clock, arch) \ EDCRegisterLocation.resize(controller + 1); \ EDCRegisterLocation[controller] = std::make_pair(arch##_EDC##controller##_##clock##_REGISTER_DEV_ADDR, arch##_EDC##controller##_##clock##_REGISTER_FUNC_ADDR); #define PCM_PCICFG_M2M_INIT(x, arch) \ M2MRegisterLocation.resize(x + 1); \ M2MRegisterLocation[x] = std::make_pair(arch##_M2M_##x##_REGISTER_DEV_ADDR, arch##_M2M_##x##_REGISTER_FUNC_ADDR); #define PCM_PCICFG_HBM_M2M_INIT(x, arch) \ HBM_M2MRegisterLocation.resize(x + 1); \ HBM_M2MRegisterLocation[x] = std::make_pair(arch##_HBM_M2M_##x##_REGISTER_DEV_ADDR, arch##_HBM_M2M_##x##_REGISTER_FUNC_ADDR); #define PCM_PCICFG_HA_INIT(x, arch) \ HARegisterLocation.resize(x + 1); \ HARegisterLocation[x] = std::make_pair(arch##_HA##x##_REGISTER_DEV_ADDR, arch##_HA##x##_REGISTER_FUNC_ADDR); if(cpu_model == PCM::JAKETOWN || cpu_model == PCM::IVYTOWN) { PCM_PCICFG_MC_INIT(0, 0, JKTIVT) PCM_PCICFG_MC_INIT(0, 1, JKTIVT) PCM_PCICFG_MC_INIT(0, 2, JKTIVT) PCM_PCICFG_MC_INIT(0, 3, JKTIVT) PCM_PCICFG_MC_INIT(1, 0, JKTIVT) PCM_PCICFG_MC_INIT(1, 1, JKTIVT) PCM_PCICFG_MC_INIT(1, 2, JKTIVT) PCM_PCICFG_MC_INIT(1, 3, JKTIVT) PCM_PCICFG_QPI_INIT(0, JKTIVT); PCM_PCICFG_QPI_INIT(1, JKTIVT); PCM_PCICFG_QPI_INIT(2, JKTIVT); } else if(cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX_DE || cpu_model == PCM::BDX) { PCM_PCICFG_MC_INIT(0, 0, HSX) PCM_PCICFG_MC_INIT(0, 1, HSX) PCM_PCICFG_MC_INIT(0, 2, HSX) PCM_PCICFG_MC_INIT(0, 3, HSX) PCM_PCICFG_MC_INIT(1, 0, HSX) PCM_PCICFG_MC_INIT(1, 1, HSX) PCM_PCICFG_MC_INIT(1, 2, HSX) PCM_PCICFG_MC_INIT(1, 3, HSX) PCM_PCICFG_QPI_INIT(0, HSX); PCM_PCICFG_QPI_INIT(1, HSX); PCM_PCICFG_QPI_INIT(2, HSX); PCM_PCICFG_HA_INIT(0, HSX); PCM_PCICFG_HA_INIT(1, HSX); } else if(cpu_model == PCM::SKX) { PCM_PCICFG_MC_INIT(0, 0, SKX) PCM_PCICFG_MC_INIT(0, 1, SKX) PCM_PCICFG_MC_INIT(0, 2, SKX) PCM_PCICFG_MC_INIT(0, 3, SKX) PCM_PCICFG_MC_INIT(1, 0, SKX) PCM_PCICFG_MC_INIT(1, 1, SKX) PCM_PCICFG_MC_INIT(1, 2, SKX) PCM_PCICFG_MC_INIT(1, 3, SKX) PCM_PCICFG_QPI_INIT(0, SKX); PCM_PCICFG_QPI_INIT(1, SKX); PCM_PCICFG_QPI_INIT(2, SKX); if (pcm->isCPX()) { PCM_PCICFG_QPI_INIT(3, CPX); PCM_PCICFG_QPI_INIT(4, CPX); PCM_PCICFG_QPI_INIT(5, CPX); } PCM_PCICFG_M2M_INIT(0, SKX) PCM_PCICFG_M2M_INIT(1, SKX) // M3UPI if (pcm->isCPX()) { // CPX PCM_PCICFG_M3UPI_INIT(0, CPX); PCM_PCICFG_M3UPI_INIT(1, CPX); PCM_PCICFG_M3UPI_INIT(2, CPX); PCM_PCICFG_M3UPI_INIT(3, CPX); PCM_PCICFG_M3UPI_INIT(4, CPX); PCM_PCICFG_M3UPI_INIT(5, CPX); } else { // SKX/CLX PCM_PCICFG_M3UPI_INIT(0, SKX); PCM_PCICFG_M3UPI_INIT(1, SKX); PCM_PCICFG_M3UPI_INIT(2, SKX); } } else if (cpu_model == PCM::ICX) { PCM_PCICFG_QPI_INIT(0, ICX); PCM_PCICFG_QPI_INIT(1, ICX); PCM_PCICFG_QPI_INIT(2, ICX); PCM_PCICFG_M3UPI_INIT(0, ICX); PCM_PCICFG_M3UPI_INIT(1, ICX); PCM_PCICFG_M3UPI_INIT(2, ICX); PCM_PCICFG_M2M_INIT(0, SERVER) PCM_PCICFG_M2M_INIT(1, SERVER) PCM_PCICFG_M2M_INIT(2, SERVER) PCM_PCICFG_M2M_INIT(3, SERVER) } else if (cpu_model == PCM::SPR) { PCM_PCICFG_QPI_INIT(0, SPR); PCM_PCICFG_QPI_INIT(1, SPR); PCM_PCICFG_QPI_INIT(2, SPR); PCM_PCICFG_QPI_INIT(3, SPR); PCM_PCICFG_M2M_INIT(0, SERVER) PCM_PCICFG_M2M_INIT(1, SERVER) PCM_PCICFG_M2M_INIT(2, SERVER) PCM_PCICFG_M2M_INIT(3, SERVER) PCM_PCICFG_HBM_M2M_INIT(0, SERVER) PCM_PCICFG_HBM_M2M_INIT(1, SERVER) PCM_PCICFG_HBM_M2M_INIT(2, SERVER) PCM_PCICFG_HBM_M2M_INIT(3, SERVER) PCM_PCICFG_HBM_M2M_INIT(4, SERVER) PCM_PCICFG_HBM_M2M_INIT(5, SERVER) PCM_PCICFG_HBM_M2M_INIT(6, SERVER) PCM_PCICFG_HBM_M2M_INIT(7, SERVER) PCM_PCICFG_HBM_M2M_INIT(8, SERVER) PCM_PCICFG_HBM_M2M_INIT(9, SERVER) PCM_PCICFG_HBM_M2M_INIT(10, SERVER) PCM_PCICFG_HBM_M2M_INIT(11, SERVER) PCM_PCICFG_HBM_M2M_INIT(12, SERVER) PCM_PCICFG_HBM_M2M_INIT(13, SERVER) PCM_PCICFG_HBM_M2M_INIT(14, SERVER) PCM_PCICFG_HBM_M2M_INIT(15, SERVER) } else if(cpu_model == PCM::KNL) { // 2 DDR4 Memory Controllers with 3 channels each PCM_PCICFG_MC_INIT(0, 0, KNL) PCM_PCICFG_MC_INIT(0, 1, KNL) PCM_PCICFG_MC_INIT(0, 2, KNL) PCM_PCICFG_MC_INIT(1, 0, KNL) PCM_PCICFG_MC_INIT(1, 1, KNL) PCM_PCICFG_MC_INIT(1, 2, KNL) // 8 MCDRAM (Multi-Channel [Stacked] DRAM) Memory Controllers PCM_PCICFG_EDC_INIT(0, ECLK, KNL) PCM_PCICFG_EDC_INIT(1, ECLK, KNL) PCM_PCICFG_EDC_INIT(2, ECLK, KNL) PCM_PCICFG_EDC_INIT(3, ECLK, KNL) PCM_PCICFG_EDC_INIT(4, ECLK, KNL) PCM_PCICFG_EDC_INIT(5, ECLK, KNL) PCM_PCICFG_EDC_INIT(6, ECLK, KNL) PCM_PCICFG_EDC_INIT(7, ECLK, KNL) } else if (cpu_model == PCM::SNOWRIDGE) { PCM_PCICFG_M2M_INIT(0, SERVER) PCM_PCICFG_M2M_INIT(1, SERVER) PCM_PCICFG_M2M_INIT(2, SERVER) PCM_PCICFG_M2M_INIT(3, SERVER) } else { std::cerr << "Error: Uncore PMU for processor with model id " << cpu_model << " is not supported.\n"; throw std::exception(); } #undef PCM_PCICFG_MC_INIT #undef PCM_PCICFG_QPI_INIT #undef PCM_PCICFG_M3UPI_INIT #undef PCM_PCICFG_EDC_INIT #undef PCM_PCICFG_M2M_INIT #undef PCM_PCICFG_HA_INIT } void ServerUncorePMUs::initBuses(uint32 socket_, const PCM * pcm) { const uint32 total_sockets_ = pcm->getNumSockets(); if (M2MRegisterLocation.size()) { initSocket2Bus(socket2M2Mbus, M2MRegisterLocation[0].first, M2MRegisterLocation[0].second, M2M_DEV_IDS, (uint32)sizeof(M2M_DEV_IDS) / sizeof(M2M_DEV_IDS[0])); if (socket_ < socket2M2Mbus.size()) { groupnr = socket2M2Mbus[socket_].first; M2Mbus = socket2M2Mbus[socket_].second; } else { std::cerr << "PCM error: socket_ " << socket_ << " >= socket2M2Mbus.size() " << socket2M2Mbus.size() << "\n"; } if (total_sockets_ != socket2M2Mbus.size()) { std::cerr << "PCM warning: total_sockets_ " << total_sockets_ << " does not match socket2M2Mbus.size() " << socket2M2Mbus.size() << "\n"; } } if (MCRegisterLocation.size() > 0 && MCRegisterLocation[0].size() > 0) { initSocket2Bus(socket2iMCbus, MCRegisterLocation[0][0].first, MCRegisterLocation[0][0].second, IMC_DEV_IDS, (uint32)sizeof(IMC_DEV_IDS) / sizeof(IMC_DEV_IDS[0])); if (total_sockets_ == socket2iMCbus.size()) { if (total_sockets_ == socket2M2Mbus.size() && socket2iMCbus[socket_].first != socket2M2Mbus[socket_].first) { std::cerr << "PCM error: mismatching PCICFG group number for M2M and IMC perfmon devices.\n"; M2Mbus = -1; } groupnr = socket2iMCbus[socket_].first; iMCbus = socket2iMCbus[socket_].second; } else if (total_sockets_ <= 4) { iMCbus = getBusFromSocket(socket_); if (iMCbus < 0) { std::cerr << "Cannot find bus for socket " << socket_ << " on system with " << total_sockets_ << " sockets.\n"; throw std::exception(); } else { std::cerr << "PCM Warning: the bus for socket " << socket_ << " on system with " << total_sockets_ << " sockets could not find via PCI bus scan. Using cpubusno register. Bus = " << iMCbus << "\n"; } } else { std::cerr << "Cannot find bus for socket " << socket_ << " on system with " << total_sockets_ << " sockets.\n"; throw std::exception(); } } #if 1 if (total_sockets_ == 1) { /* * For single socket systems, do not worry at all about QPI ports. This * eliminates QPI LL programming error messages on single socket systems * with BIOS that hides QPI performance counting PCI functions. It also * eliminates register programming that is not needed since no QPI traffic * is possible with single socket systems. */ return; } #endif #ifdef PCM_NOQPI return; #endif if (PCM::hasUPI(cpu_model) && XPIRegisterLocation.size() > 0) { initSocket2Bus(socket2UPIbus, XPIRegisterLocation[0].first, XPIRegisterLocation[0].second, UPI_DEV_IDS, (uint32)sizeof(UPI_DEV_IDS) / sizeof(UPI_DEV_IDS[0])); if(total_sockets_ == socket2UPIbus.size()) { UPIbus = socket2UPIbus[socket_].second; if(groupnr != socket2UPIbus[socket_].first) { UPIbus = -1; std::cerr << "PCM error: mismatching PCICFG group number for UPI and IMC perfmon devices.\n"; } } else { std::cerr << "PCM error: Did not find UPI perfmon device on every socket in a multisocket system.\n"; } } else { UPIbus = iMCbus; } // std::cerr << "DEBUG: UPIbus: " << UPIbus << "\n"; } void ServerUncorePMUs::initDirect(uint32 socket_, const PCM * pcm) { { std::vector > imcHandles; auto lastWorkingChannels = imcHandles.size(); for (auto & ctrl: MCRegisterLocation) { for (auto & channel : ctrl) { PciHandleType * handle = createIntelPerfMonDevice(groupnr, iMCbus, channel.first, channel.second, true); if (handle) imcHandles.push_back(std::shared_ptr(handle)); } if (imcHandles.size() > lastWorkingChannels) { num_imc_channels.push_back((uint32)(imcHandles.size() - lastWorkingChannels)); } lastWorkingChannels = imcHandles.size(); } for (auto & handle : imcHandles) { if (cpu_model == PCM::KNL) { imcPMUs.push_back( UncorePMU( std::make_shared(handle, KNX_MC_CH_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTL0_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTL1_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTL2_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTL3_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTR0_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTR1_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTR2_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_CTR3_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_FIXED_CTL_ADDR), std::make_shared(handle, KNX_MC_CH_PCI_PMON_FIXED_CTR_ADDR)) ); } else { imcPMUs.push_back( UncorePMU( std::make_shared(handle, XPF_MC_CH_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTL0_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTL1_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTL2_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTL3_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTR0_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTR1_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTR2_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_CTR3_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_FIXED_CTL_ADDR), std::make_shared(handle, XPF_MC_CH_PCI_PMON_FIXED_CTR_ADDR)) ); } } } auto populateM2MPMUs = [](uint32 groupnr, int32 M2Mbus, int32 cpu_model, const std::vector > & M2MRegisterLocation, UncorePMUVector & m2mPMUs) { std::vector > m2mHandles; if (M2Mbus >= 0) { for (auto & reg : M2MRegisterLocation) { PciHandleType * handle = createIntelPerfMonDevice(groupnr, M2Mbus, reg.first, reg.second, true); if (handle) m2mHandles.push_back(std::shared_ptr(handle)); } } for (auto & handle : m2mHandles) { if (cpu_model == PCM::ICX || cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::SPR) { m2mPMUs.push_back( UncorePMU( std::make_shared(handle, SERVER_M2M_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTL0_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTL1_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTL2_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTL3_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTR0_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTR1_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTR2_ADDR), std::make_shared(handle, SERVER_M2M_PCI_PMON_CTR3_ADDR) ) ); } else { m2mPMUs.push_back( UncorePMU( std::make_shared(handle, SKX_M2M_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTL0_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTL1_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTL2_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTL3_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTR0_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTR1_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTR2_ADDR), std::make_shared(handle, SKX_M2M_PCI_PMON_CTR3_ADDR) ) ); } } }; populateM2MPMUs(groupnr, M2Mbus, cpu_model, M2MRegisterLocation, m2mPMUs); populateM2MPMUs(groupnr, M2Mbus, cpu_model, HBM_M2MRegisterLocation, hbm_m2mPMUs); int numChannels = 0; if (cpu_model == PCM::SPR) { numChannels = 3; } if (cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::ICX) { numChannels = 2; if (PCM::getCPUModelFromCPUID() == PCM::ICX_D) { numChannels = 3; } } auto createIMCPMU = [](const size_t addr, const size_t mapSize) -> UncorePMU { const auto alignedAddr = addr & ~4095ULL; const auto alignDelta = addr & 4095ULL; auto handle = std::make_shared(alignedAddr, mapSize, false); return UncorePMU( std::make_shared(handle, SERVER_MC_CH_PMON_BOX_CTL_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTL0_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTL1_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTL2_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTL3_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTR0_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTR1_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTR2_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_CTR3_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_FIXED_CTL_OFFSET + alignDelta), std::make_shared(handle, SERVER_MC_CH_PMON_FIXED_CTR_OFFSET + alignDelta) ); }; auto initAndCheckSocket2Ubox0Bus = [&socket_]() -> bool { initSocket2Ubox0Bus(); if (socket_ >= socket2UBOX0bus.size()) { std::cerr << "ERROR: socket " << socket_ << " is not found in socket2UBOX0bus. socket2UBOX0bus.size =" << socket2UBOX0bus.size() << std::endl; return false; } return true; }; if (numChannels > 0) { if (initAndCheckSocket2Ubox0Bus()) { auto memBars = getServerMemBars((uint32)m2mPMUs.size(), socket2UBOX0bus[socket_].first, socket2UBOX0bus[socket_].second); for (auto & memBar : memBars) { for (int channel = 0; channel < numChannels; ++channel) { imcPMUs.push_back(createIMCPMU(memBar + SERVER_MC_CH_PMON_BASE_ADDR + channel * SERVER_MC_CH_PMON_STEP, SERVER_MC_CH_PMON_SIZE)); } num_imc_channels.push_back(numChannels); } } } if (imcPMUs.empty()) { std::cerr << "PCM error: no memory controllers found.\n"; throw std::exception(); } if (cpu_model == PCM::KNL) { std::vector > edcHandles; for (auto & reg : EDCRegisterLocation) { PciHandleType * handle = createIntelPerfMonDevice(groupnr, iMCbus, reg.first, reg.second, true); if (handle) edcHandles.push_back(std::shared_ptr(handle)); } for (auto & handle : edcHandles) { edcPMUs.push_back( UncorePMU( std::make_shared(handle, KNX_EDC_CH_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTL0_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTL1_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTL2_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTL3_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTR0_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTR1_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTR2_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_CTR3_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_FIXED_CTL_ADDR), std::make_shared(handle, KNX_EDC_CH_PCI_PMON_FIXED_CTR_ADDR)) ); } } if (hbm_m2mPMUs.empty() == false) { // HBM if (initAndCheckSocket2Ubox0Bus()) { const auto bar = getServerSCFBar(socket2UBOX0bus[socket_].first, socket2UBOX0bus[socket_].second); for (size_t box = 0; box < hbm_m2mPMUs.size(); ++box) { for (int channel = 0; channel < 2; ++channel) { edcPMUs.push_back(createIMCPMU(bar + SERVER_HBM_CH_PMON_BASE_ADDR + box * SERVER_HBM_BOX_PMON_STEP + channel * SERVER_HBM_CH_PMON_STEP, SERVER_HBM_CH_PMON_SIZE)); } } } } std::vector > m3upiHandles; if (UPIbus >= 0) { for (auto& reg : M3UPIRegisterLocation) { PciHandleType* handle = createIntelPerfMonDevice(groupnr, UPIbus, reg.first, reg.second, true); if (handle) m3upiHandles.push_back(std::shared_ptr(handle)); } } for (auto& handle : m3upiHandles) { if (cpu_model == PCM::ICX) { m3upiPMUs.push_back( UncorePMU( std::make_shared(handle, ICX_M3UPI_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTL0_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTL1_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTL2_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTL3_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTR0_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTR1_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTR2_ADDR), std::make_shared(handle, ICX_M3UPI_PCI_PMON_CTR3_ADDR) ) ); } else { m3upiPMUs.push_back( UncorePMU( std::make_shared(handle, M3UPI_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, M3UPI_PCI_PMON_CTL0_ADDR), std::make_shared(handle, M3UPI_PCI_PMON_CTL1_ADDR), std::make_shared(handle, M3UPI_PCI_PMON_CTL2_ADDR), std::shared_ptr(), std::make_shared(handle, M3UPI_PCI_PMON_CTR0_ADDR), std::make_shared(handle, M3UPI_PCI_PMON_CTR1_ADDR), std::make_shared(handle, M3UPI_PCI_PMON_CTR2_ADDR), std::shared_ptr() ) ); } } { std::vector > haHandles; for (auto & reg : HARegisterLocation) { auto handle = createIntelPerfMonDevice(groupnr, iMCbus, reg.first, reg.second, true); if (handle) haHandles.push_back(std::shared_ptr(handle)); } for (auto & handle : haHandles) { haPMUs.push_back( UncorePMU( std::make_shared(handle, XPF_HA_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTL0_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTL1_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTL2_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTL3_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTR0_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTR1_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTR2_ADDR), std::make_shared(handle, XPF_HA_PCI_PMON_CTR3_ADDR) ) ); } } if (pcm->getNumSockets() == 1) { /* * For single socket systems, do not worry at all about QPI ports. This * eliminates QPI LL programming error messages on single socket systems * with BIOS that hides QPI performance counting PCI functions. It also * eliminates register programming that is not needed since no QPI traffic * is possible with single socket systems. */ xpiPMUs.clear(); return; } #ifdef PCM_NOQPI xpiPMUs.clear(); std::cerr << getNumMC() << " memory controllers detected with total number of " << imcPMUs.size() << " channels. " << m2mPMUs.size() << " M2M (mesh to memory) blocks detected. " << haPMUs.size() << " Home Agents detected. " << m3upiPMUs.size() << " M3UPI blocks detected. " "\n"; return; #endif std::vector > qpiLLHandles; auto xPI = pcm->xPI(); try { for (size_t i = 0; i < XPIRegisterLocation.size(); ++i) { PciHandleType * handle = createIntelPerfMonDevice(groupnr, UPIbus, XPIRegisterLocation[i].first, XPIRegisterLocation[i].second, true); if (handle) qpiLLHandles.push_back(std::shared_ptr(handle)); else { if (i == 0 || i == 1) { std::cerr << "ERROR: " << xPI << " LL monitoring device (" << std::hex << groupnr << ":" << UPIbus << ":" << XPIRegisterLocation[i].first << ":" << XPIRegisterLocation[i].second << ") is missing. The " << xPI << " statistics will be incomplete or missing." << std::dec << "\n"; } else if (pcm->getCPUBrandString().find("E7") != std::string::npos) // Xeon E7 { std::cerr << "ERROR: " << xPI << " LL performance monitoring device for the third " << xPI << " link was not found on " << pcm->getCPUBrandString() << " processor in socket " << socket_ << ". Possibly BIOS hides the device. The " << xPI << " statistics will be incomplete or missing.\n"; } } } } catch (...) { std::cerr << "PCM Error: can not create " << xPI << " LL handles.\n"; throw std::exception(); } for (auto & handle : qpiLLHandles) { if (cpu_model == PCM::SKX) { xpiPMUs.push_back( UncorePMU( std::make_shared(handle, U_L_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTL0_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTL1_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTL2_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTL3_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTR0_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTR1_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTR2_ADDR), std::make_shared(handle, U_L_PCI_PMON_CTR3_ADDR) ) ); } else if (cpu_model == PCM::ICX) { xpiPMUs.push_back( UncorePMU( std::make_shared(handle, ICX_UPI_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTL0_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTL1_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTL2_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTL3_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTR0_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTR1_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTR2_ADDR), std::make_shared(handle, ICX_UPI_PCI_PMON_CTR3_ADDR) ) ); } else if (cpu_model == PCM::SPR) { xpiPMUs.push_back( UncorePMU( std::make_shared(handle, SPR_UPI_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, SPR_UPI_PCI_PMON_CTL0_ADDR + 8*0), std::make_shared(handle, SPR_UPI_PCI_PMON_CTL0_ADDR + 8*1), std::make_shared(handle, SPR_UPI_PCI_PMON_CTL0_ADDR + 8*2), std::make_shared(handle, SPR_UPI_PCI_PMON_CTL0_ADDR + 8*3), std::make_shared(handle, SPR_UPI_PCI_PMON_CTR0_ADDR + 8*0), std::make_shared(handle, SPR_UPI_PCI_PMON_CTR0_ADDR + 8*1), std::make_shared(handle, SPR_UPI_PCI_PMON_CTR0_ADDR + 8*2), std::make_shared(handle, SPR_UPI_PCI_PMON_CTR0_ADDR + 8*3) ) ); } else { xpiPMUs.push_back( UncorePMU( std::make_shared(handle, Q_P_PCI_PMON_BOX_CTL_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTL0_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTL1_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTL2_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTL3_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTR0_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTR1_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTR2_ADDR), std::make_shared(handle, Q_P_PCI_PMON_CTR3_ADDR) ) ); } } } bool ServerUncorePMUs::HBMAvailable() const { return edcPMUs.empty() == false; } #ifdef PCM_USE_PERF class PerfVirtualFilterRegister; class PerfVirtualControlRegister : public HWRegister { friend class PerfVirtualCounterRegister; friend class PerfVirtualFilterRegister; friend class IDXPerfVirtualFilterRegister; int fd; int socket; int pmuID; perf_event_attr event; bool fixed; void close() { if (fd >= 0) { ::close(fd); fd = -1; } } PerfVirtualControlRegister(const PerfVirtualControlRegister &) = delete; PerfVirtualControlRegister & operator = (const PerfVirtualControlRegister &) = delete; public: PerfVirtualControlRegister(int socket_, int pmuID_, bool fixed_ = false) : fd(-1), socket(socket_), pmuID(pmuID_), fixed(fixed_) { event = PCM_init_perf_event_attr(false); event.type = pmuID; } void operator = (uint64 val) override { close(); event.config = fixed ? 0xff : val; const auto core = PCM::getInstance()->socketRefCore[socket]; if ((fd = syscall(SYS_perf_event_open, &event, -1, core, -1, 0)) <= 0) { std::cerr << "Linux Perf: Error on programming PMU " << pmuID << ": " << strerror(errno) << "\n"; std::cerr << "config: 0x" << std::hex << event.config << " config1: 0x" << event.config1 << " config2: 0x" << event.config2 << std::dec << "\n"; if (errno == 24) std::cerr << PCM_ULIMIT_RECOMMENDATION; return; } } operator uint64 () override { return event.config; } ~PerfVirtualControlRegister() { close(); } int getFD() const { return fd; } int getPMUID() const { return pmuID; } }; class PerfVirtualCounterRegister : public HWRegister { std::shared_ptr controlReg; public: PerfVirtualCounterRegister(const std::shared_ptr & controlReg_) : controlReg(controlReg_) { } void operator = (uint64 /* val */) override { // no-op } operator uint64 () override { uint64 result = 0; if (controlReg.get() && (controlReg->getFD() >= 0)) { int status = ::read(controlReg->getFD(), &result, sizeof(result)); if (status != sizeof(result)) { std::cerr << "PCM Error: failed to read from Linux perf handle " << controlReg->getFD() << " PMU " << controlReg->getPMUID() << "\n"; } } return result; } }; class PerfVirtualFilterRegister : public HWRegister { uint64 lastValue; std::array, 4> controlRegs; int filterNr; public: PerfVirtualFilterRegister(std::array, 4> & controlRegs_, int filterNr_) : lastValue(0), controlRegs(controlRegs_), filterNr(filterNr_) { } void operator = (uint64 val) override { lastValue = val; for (auto & ctl: controlRegs) { union { uint64 config1; uint32 config1HL[2]; } cvt; cvt.config1 = ctl->event.config1; cvt.config1HL[filterNr] = val; ctl->event.config1 = cvt.config1; } } operator uint64 () override { return lastValue; } }; class IDXPerfVirtualFilterRegister : public HWRegister { uint64 lastValue; std::shared_ptr controlReg; int filterNr; public: IDXPerfVirtualFilterRegister(std::shared_ptr controlReg_, int filterNr_) : lastValue(0), controlReg(controlReg_), filterNr(filterNr_) { } void operator = (uint64 val) override { lastValue = val; /* struct { u64 wq:32; u64 tc:8; u64 pg_sz:4; u64 xfer_sz:8; u64 eng:8; } filter_cfg; */ switch (filterNr) { case 0: //FLT_WQ controlReg->event.config1 = ((controlReg->event.config1 & 0xFFFFFFF00000000) | (val & 0xFFFFFFFF)); break; case 1: //FLT_TC controlReg->event.config1 = ((controlReg->event.config1 & 0xFFFFF00FFFFFFFF) | ((val & 0xFF) << 32)); break; case 2: //FLT_PG_SZ controlReg->event.config1 = ((controlReg->event.config1 & 0xFFFF0FFFFFFFFFF) | ((val & 0xF) << 40)); break; case 3: //FLT_XFER_SZ controlReg->event.config1 = ((controlReg->event.config1 & 0xFF00FFFFFFFFFFF) | ((val & 0xFF) << 44)); break; case 4: //FLT_ENG controlReg->event.config1 = ((controlReg->event.config1 & 0x00FFFFFFFFFFFFF) | ((val & 0xFF) << 52)); break; default: break; } } operator uint64 () override { return lastValue; } }; std::vector enumeratePerfPMUs(const std::string & type, int max_id) { auto getPerfPMUID = [](const std::string & type, int num) { int id = -1; std::ostringstream pmuIDPath(std::ostringstream::out); pmuIDPath << std::string("/sys/bus/event_source/devices/uncore_") << type; if (num != -1) { pmuIDPath << "_" << num; } pmuIDPath << "/type"; const std::string pmuIDStr = readSysFS(pmuIDPath.str().c_str(), true); if (pmuIDStr.size()) { id = std::atoi(pmuIDStr.c_str()); } return id; }; std::vector ids; for (int i = -1; i < max_id; ++i) { int pmuID = getPerfPMUID(type, i); if (pmuID > 0) { //std::cout << "DEBUG: " << type << " pmu id " << pmuID << " found\n"; ids.push_back(pmuID); } } return ids; } void populatePerfPMUs(unsigned socket_, const std::vector & ids, std::vector & pmus, bool fixed, bool filter0, bool filter1) { for (const auto & id : ids) { std::array, 4> controlRegs = { std::make_shared(socket_, id), std::make_shared(socket_, id), std::make_shared(socket_, id), std::make_shared(socket_, id) }; std::shared_ptr counterReg0 = std::make_shared(controlRegs[0]); std::shared_ptr counterReg1 = std::make_shared(controlRegs[1]); std::shared_ptr counterReg2 = std::make_shared(controlRegs[2]); std::shared_ptr counterReg3 = std::make_shared(controlRegs[3]); std::shared_ptr fixedControlReg = std::make_shared(socket_, id, true); std::shared_ptr fixedCounterReg = std::make_shared(fixedControlReg); std::shared_ptr filterReg0 = std::make_shared(controlRegs, 0); std::shared_ptr filterReg1 = std::make_shared(controlRegs, 1); pmus.push_back( UncorePMU( std::make_shared(), controlRegs[0], controlRegs[1], controlRegs[2], controlRegs[3], counterReg0, counterReg1, counterReg2, counterReg3, fixed ? fixedControlReg : std::shared_ptr(), fixed ? fixedCounterReg : std::shared_ptr(), filter0 ? filterReg0 : std::shared_ptr(), filter1 ? filterReg1 : std::shared_ptr() ) ); } } std::vector > enumerateIDXPerfPMUs(const std::string & type, int max_id) { uint32 numaNode=0xff; auto getPerfPMUID = [](const std::string & type, int num) { int id = -1; std::ostringstream pmuIDPath(std::ostringstream::out); pmuIDPath << std::string("/sys/bus/event_source/devices/") << type; if (num != -1) { pmuIDPath << num; } pmuIDPath << "/type"; const std::string pmuIDStr = readSysFS(pmuIDPath.str().c_str(), true); if (pmuIDStr.size()) { id = std::atoi(pmuIDStr.c_str()); } return id; }; //Enumurate IDX devices by linux sysfs scan std::vector > ids; for (int i = -1; i < max_id; ++i) { int pmuID = getPerfPMUID(type, i); if (pmuID > 0) { numaNode = 0xff; std::ostringstream devNumaNodePath(std::ostringstream::out); devNumaNodePath << std::string("/sys/bus/dsa/devices/") << type << i << "/numa_node"; const std::string devNumaNodeStr = readSysFS(devNumaNodePath.str().c_str(), true); if (devNumaNodeStr.size()) { numaNode = std::atoi(devNumaNodeStr.c_str()); if (numaNode == (std::numeric_limits::max)()) { numaNode = 0xff; //translate to special value for numa disable case. } } //std::cout << "IDX DEBUG: " << type << " pmu id " << pmuID << " found\n"; //std::cout << "IDX DEBUG: numa node file path=" << devNumaNodePath.str().c_str() << ", value=" << numaNode << std::endl; ids.push_back(std::make_pair(pmuID, numaNode)); } } return ids; } void populateIDXPerfPMUs(unsigned socket_, const std::vector > & ids, std::vector & pmus) { for (const auto & id : ids) { uint32 n_regs = SPR_IDX_ACCEL_COUNTER_MAX_NUM; std::vector > CounterControlRegs; std::vector > CounterValueRegs; std::vector > CounterFilterWQRegs, CounterFilterENGRegs, CounterFilterTCRegs, CounterFilterPGSZRegs, CounterFilterXFERSZRegs; for (size_t r = 0; r < n_regs; ++r) { auto CounterControlReg = std::make_shared(socket_, id.first); CounterControlRegs.push_back(CounterControlReg); CounterValueRegs.push_back(std::make_shared(CounterControlReg)); CounterFilterWQRegs.push_back(std::make_shared(CounterControlReg, 0)); CounterFilterTCRegs.push_back(std::make_shared(CounterControlReg, 1)); CounterFilterPGSZRegs.push_back(std::make_shared(CounterControlReg, 2)); CounterFilterXFERSZRegs.push_back(std::make_shared(CounterControlReg, 3)); CounterFilterENGRegs.push_back(std::make_shared(CounterControlReg, 4)); } pmus.push_back( IDX_PMU( true, id.second, 0xff,//No support of socket location in perf driver mode. std::make_shared(), std::make_shared(), std::make_shared(), CounterControlRegs, CounterValueRegs, CounterFilterWQRegs, CounterFilterENGRegs, CounterFilterTCRegs, CounterFilterPGSZRegs, CounterFilterXFERSZRegs )); } } #endif void ServerUncorePMUs::initPerf(uint32 socket_, const PCM * /*pcm*/) { #ifdef PCM_USE_PERF auto imcIDs = enumeratePerfPMUs("imc", 100); auto m2mIDs = enumeratePerfPMUs("m2m", 100); auto haIDs = enumeratePerfPMUs("ha", 100); auto numMemControllers = std::max(m2mIDs.size(), haIDs.size()); for (size_t i = 0; i < numMemControllers; ++i) { const int channelsPerController = imcIDs.size() / numMemControllers; num_imc_channels.push_back(channelsPerController); } populatePerfPMUs(socket_, imcIDs, imcPMUs, true); populatePerfPMUs(socket_, m2mIDs, m2mPMUs, false); populatePerfPMUs(socket_, enumeratePerfPMUs("qpi", 100), xpiPMUs, false); populatePerfPMUs(socket_, enumeratePerfPMUs("upi", 100), xpiPMUs, false); populatePerfPMUs(socket_, enumeratePerfPMUs("m3upi", 100), m3upiPMUs, false); populatePerfPMUs(socket_, haIDs, haPMUs, false); #endif } size_t ServerUncorePMUs::getNumMCChannels(const uint32 controller) const { if (controller < num_imc_channels.size()) { return num_imc_channels[controller]; } return 0; } ServerUncorePMUs::~ServerUncorePMUs() { } void ServerUncorePMUs::programServerUncoreMemoryMetrics(const ServerUncoreMemoryMetrics & metrics, const int rankA, const int rankB) { PCM * pcm = PCM::getInstance(); uint32 MCCntConfig[4] = {0,0,0,0}; uint32 EDCCntConfig[4] = {0,0,0,0}; if(rankA < 0 && rankB < 0) { auto setEvents2_3 = [&](const uint32 partial_write_event) { auto noPmem = [&pcm]() -> bool { if (pcm->PMMTrafficMetricsAvailable() == false) { std::cerr << "PCM Error: PMM/Pmem metrics are not available on your platform\n"; return true; } return false; }; switch (metrics) { case PmemMemoryMode: case PmemMixedMode: if (noPmem()) return false; MCCntConfig[EventPosition::PMM_MM_MISS_CLEAN] = MC_CH_PCI_PMON_CTL_EVENT(0xd3) + MC_CH_PCI_PMON_CTL_UMASK(2); // monitor TAGCHK.MISS_CLEAN on counter 2 MCCntConfig[EventPosition::PMM_MM_MISS_DIRTY] = MC_CH_PCI_PMON_CTL_EVENT(0xd3) + MC_CH_PCI_PMON_CTL_UMASK(4); // monitor TAGCHK.MISS_DIRTY on counter 3 break; case Pmem: if (noPmem()) return false; MCCntConfig[EventPosition::PMM_READ] = MC_CH_PCI_PMON_CTL_EVENT(0xe3); // monitor PMM_RDQ_REQUESTS on counter 2 MCCntConfig[EventPosition::PMM_WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0xe7); // monitor PMM_WPQ_REQUESTS on counter 3 break; case PartialWrites: MCCntConfig[EventPosition::PARTIAL] = partial_write_event; break; default: std::cerr << "PCM Error: unknown metrics: " << metrics << "\n"; return false; } return true; }; switch(cpu_model) { case PCM::KNL: MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x03) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 0: CAS.RD MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x03) + MC_CH_PCI_PMON_CTL_UMASK(2); // monitor reads on counter 1: CAS.WR EDCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x01) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 0: RPQ EDCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x02) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 1: WPQ break; case PCM::SNOWRIDGE: case PCM::ICX: if (metrics == PmemMemoryMode) { MCCntConfig[EventPosition::NM_HIT] = MC_CH_PCI_PMON_CTL_EVENT(0xd3) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 0: UNC_M_TAGCHK.HIT } else { MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(0x0f); // monitor reads on counter 0: CAS_COUNT.RD MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(0x30); // monitor writes on counter 1: CAS_COUNT.WR } if (setEvents2_3(MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(0x0c)) == false) // monitor partial writes on counter 2: CAS_COUNT.RD_UNDERFILL { return; } break; case PCM::SPR: { EDCCntConfig[EventPosition::READ] = MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x05) + MC_CH_PCI_PMON_CTL_UMASK(0xcf); // monitor reads on counter 0: CAS_COUNT.RD EDCCntConfig[EventPosition::WRITE] = MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x05) + MC_CH_PCI_PMON_CTL_UMASK(0xf0); // monitor writes on counter 1: CAS_COUNT.WR } if (setEvents2_3(MC_CH_PCI_PMON_CTL_EVENT(0x05) + MC_CH_PCI_PMON_CTL_UMASK(0xcc)) == false) // monitor partial writes on counter 2: CAS_COUNT.RD_UNDERFILL { return; } break; default: MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(3); // monitor reads on counter 0: CAS_COUNT.RD MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(12); // monitor writes on counter 1: CAS_COUNT.WR if (setEvents2_3(MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(2)) == false) // monitor partial writes on counter 2: CAS_COUNT.RD_UNDERFILL { return; } } } else { switch(cpu_model) { case PCM::IVYTOWN: MCCntConfig[EventPosition::READ_RANK_A] = MC_CH_PCI_PMON_CTL_EVENT((0xb0 + rankA)) + MC_CH_PCI_PMON_CTL_UMASK(0xff); // RD_CAS_RANK(rankA) all banks MCCntConfig[EventPosition::WRITE_RANK_A] = MC_CH_PCI_PMON_CTL_EVENT((0xb8 + rankA)) + MC_CH_PCI_PMON_CTL_UMASK(0xff); // WR_CAS_RANK(rankA) all banks MCCntConfig[EventPosition::READ_RANK_B] = MC_CH_PCI_PMON_CTL_EVENT((0xb0 + rankB)) + MC_CH_PCI_PMON_CTL_UMASK(0xff); // RD_CAS_RANK(rankB) all banks MCCntConfig[EventPosition::WRITE_RANK_B] = MC_CH_PCI_PMON_CTL_EVENT((0xb8 + rankB)) + MC_CH_PCI_PMON_CTL_UMASK(0xff); // WR_CAS_RANK(rankB) all banks break; case PCM::HASWELLX: case PCM::BDX_DE: case PCM::BDX: case PCM::SKX: MCCntConfig[EventPosition::READ_RANK_A] = MC_CH_PCI_PMON_CTL_EVENT((0xb0 + rankA)) + MC_CH_PCI_PMON_CTL_UMASK(16); // RD_CAS_RANK(rankA) all banks MCCntConfig[EventPosition::WRITE_RANK_A] = MC_CH_PCI_PMON_CTL_EVENT((0xb8 + rankA)) + MC_CH_PCI_PMON_CTL_UMASK(16); // WR_CAS_RANK(rankA) all banks MCCntConfig[EventPosition::READ_RANK_B] = MC_CH_PCI_PMON_CTL_EVENT((0xb0 + rankB)) + MC_CH_PCI_PMON_CTL_UMASK(16); // RD_CAS_RANK(rankB) all banks MCCntConfig[EventPosition::WRITE_RANK_B] = MC_CH_PCI_PMON_CTL_EVENT((0xb8 + rankB)) + MC_CH_PCI_PMON_CTL_UMASK(16); // WR_CAS_RANK(rankB) all banks break; case PCM::KNL: MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x03) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 0: CAS.RD MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x03) + MC_CH_PCI_PMON_CTL_UMASK(2); // monitor reads on counter 1: CAS.WR EDCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x01) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 0: RPQ EDCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x02) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 1: WPQ break; default: std::cerr << "PCM Error: your processor " << pcm->getCPUBrandString() << " model " << cpu_model << " does not support the required performance events \n"; return; } } programIMC(MCCntConfig); if (pcm->HBMmemoryTrafficMetricsAvailable()) programEDC(EDCCntConfig); programM2M(); xpiPMUs.clear(); // no QPI events used return; } void ServerUncorePMUs::program() { PCM * pcm = PCM::getInstance(); uint32 MCCntConfig[4] = {0, 0, 0, 0}; uint32 EDCCntConfig[4] = {0, 0, 0, 0}; switch(cpu_model) { case PCM::KNL: MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x03) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 0: CAS_COUNT.RD MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x03) + MC_CH_PCI_PMON_CTL_UMASK(2); // monitor writes on counter 1: CAS_COUNT.WR EDCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x01) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 0: RPQ EDCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x02) + MC_CH_PCI_PMON_CTL_UMASK(1); // monitor reads on counter 1: WPQ break; case PCM::SNOWRIDGE: case PCM::ICX: MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(0x0f); // monitor reads on counter 0: CAS_COUNT.RD MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(0x30); // monitor writes on counter 1: CAS_COUNT.WR break; case PCM::SPR: EDCCntConfig[EventPosition::READ] = MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x05) + MC_CH_PCI_PMON_CTL_UMASK(0xcf); // monitor reads on counter 0: CAS_COUNT.RD EDCCntConfig[EventPosition::WRITE] = MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x05) + MC_CH_PCI_PMON_CTL_UMASK(0xf0); // monitor writes on counter 1: CAS_COUNT.WR break; default: MCCntConfig[EventPosition::READ] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(3); // monitor reads on counter 0: CAS_COUNT.RD MCCntConfig[EventPosition::WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0x04) + MC_CH_PCI_PMON_CTL_UMASK(12); // monitor writes on counter 1: CAS_COUNT.WR } if (pcm->PMMTrafficMetricsAvailable()) { MCCntConfig[EventPosition::PMM_READ] = MC_CH_PCI_PMON_CTL_EVENT(0xe3); // monitor PMM_RDQ_REQUESTS on counter 2 MCCntConfig[EventPosition::PMM_WRITE] = MC_CH_PCI_PMON_CTL_EVENT(0xe7); // monitor PMM_WPQ_REQUESTS on counter 3 } programIMC(MCCntConfig); if (pcm->HBMmemoryTrafficMetricsAvailable()) programEDC(EDCCntConfig); programM2M(); uint32 event[4]; if (PCM::hasUPI(cpu_model)) { // monitor TxL0_POWER_CYCLES event[0] = Q_P_PCI_PMON_CTL_EVENT(0x26); // monitor RxL_FLITS.ALL_DATA on counter 1 event[1] = Q_P_PCI_PMON_CTL_EVENT(0x03) + Q_P_PCI_PMON_CTL_UMASK(0xF); // monitor TxL_FLITS.NON_DATA+ALL_DATA on counter 2 event[2] = Q_P_PCI_PMON_CTL_EVENT(0x02) + Q_P_PCI_PMON_CTL_UMASK((0x97|0x0F)); // monitor UPI CLOCKTICKS event[ServerUncoreCounterState::EventPosition::xPI_CLOCKTICKS] = Q_P_PCI_PMON_CTL_EVENT(0x01); } else { // monitor DRS data received on counter 0: RxL_FLITS_G1.DRS_DATA event[0] = Q_P_PCI_PMON_CTL_EVENT(0x02) + Q_P_PCI_PMON_CTL_EVENT_EXT + Q_P_PCI_PMON_CTL_UMASK(8); // monitor NCB data received on counter 1: RxL_FLITS_G2.NCB_DATA event[1] = Q_P_PCI_PMON_CTL_EVENT(0x03) + Q_P_PCI_PMON_CTL_EVENT_EXT + Q_P_PCI_PMON_CTL_UMASK(4); // monitor outgoing data+nondata flits on counter 2: TxL_FLITS_G0.DATA + TxL_FLITS_G0.NON_DATA event[2] = Q_P_PCI_PMON_CTL_EVENT(0x00) + Q_P_PCI_PMON_CTL_UMASK(6); // monitor QPI clocks event[ServerUncoreCounterState::EventPosition::xPI_CLOCKTICKS] = Q_P_PCI_PMON_CTL_EVENT(0x14); // QPI clocks (CLOCKTICKS) } programXPI(event); programHA(); } void ServerUncorePMUs::programXPI(const uint32 * event) { const uint32 extra = PCM::hasUPI(cpu_model) ? UNC_PMON_UNIT_CTL_RSV : UNC_PMON_UNIT_CTL_FRZ_EN; for (uint32 i = 0; i < (uint32)xpiPMUs.size(); ++i) { // QPI LL PMU if (xpiPMUs[i].initFreeze(extra, " Please see BIOS options to enable the export of QPI/UPI performance monitoring devices (devices 8 and 9: function 2).\n") == false) { std::cout << "Link " << (i + 1) << " is disabled\n"; continue; } PCM::program(xpiPMUs[i], event, event + 4, extra); } cleanupQPIHandles(); } void ServerUncorePMUs::cleanupQPIHandles() { for(auto i = xpiPMUs.begin(); i != xpiPMUs.end(); ++i) { if (!i->valid()) { xpiPMUs.erase(i); cleanupQPIHandles(); return; } } } void ServerUncorePMUs::cleanupPMUs() { for (auto & pmu : xpiPMUs) { pmu.cleanup(); } for (auto & pmu : imcPMUs) { pmu.cleanup(); } for (auto & pmu : edcPMUs) { pmu.cleanup(); } for (auto & pmu : m2mPMUs) { pmu.cleanup(); } for (auto & pmu : haPMUs) { pmu.cleanup(); } } uint64 ServerUncorePMUs::getImcReads() { return getImcReadsForChannels((uint32)0, (uint32)imcPMUs.size()); } uint64 ServerUncorePMUs::getImcReadsForController(uint32 controller) { assert(controller < num_imc_channels.size()); uint32 beginChannel = 0; for (uint32 i = 0; i < controller; ++i) { beginChannel += num_imc_channels[i]; } const uint32 endChannel = beginChannel + num_imc_channels[controller]; return getImcReadsForChannels(beginChannel, endChannel); } uint64 ServerUncorePMUs::getImcReadsForChannels(uint32 beginChannel, uint32 endChannel) { uint64 result = 0; for (uint32 i = beginChannel; i < endChannel && i < imcPMUs.size(); ++i) { result += getMCCounter(i, EventPosition::READ); } return result; } uint64 ServerUncorePMUs::getImcWrites() { uint64 result = 0; for (uint32 i = 0; i < (uint32)imcPMUs.size(); ++i) { result += getMCCounter(i, EventPosition::WRITE); } return result; } uint64 ServerUncorePMUs::getPMMReads() { uint64 result = 0; for (uint32 i = 0; i < (uint32)m2mPMUs.size(); ++i) { result += getM2MCounter(i, EventPosition::PMM_READ); } return result; } uint64 ServerUncorePMUs::getPMMWrites() { uint64 result = 0; for (uint32 i = 0; i < (uint32)m2mPMUs.size(); ++i) { result += getM2MCounter(i, EventPosition::PMM_WRITE); } return result; } uint64 ServerUncorePMUs::getEdcReads() { uint64 result = 0; for (auto & pmu: edcPMUs) { result += *pmu.counterValue[EventPosition::READ]; } return result; } uint64 ServerUncorePMUs::getEdcWrites() { uint64 result = 0; for (auto & pmu : edcPMUs) { result += *pmu.counterValue[EventPosition::WRITE]; } return result; } uint64 ServerUncorePMUs::getIncomingDataFlits(uint32 port) { uint64 drs = 0, ncb = 0; if (port >= (uint32)xpiPMUs.size()) return 0; if (PCM::hasUPI(cpu_model) == false) { drs = *xpiPMUs[port].counterValue[0]; } ncb = *xpiPMUs[port].counterValue[1]; return drs + ncb; } uint64 ServerUncorePMUs::getOutgoingFlits(uint32 port) { return getQPILLCounter(port,2); } uint64 ServerUncorePMUs::getUPIL0TxCycles(uint32 port) { if (PCM::hasUPI(cpu_model)) return getQPILLCounter(port,0); return 0; } void ServerUncorePMUs::program_power_metrics(int mc_profile) { uint32 xPIEvents[4] = { 0,0,0,0 }; xPIEvents[ServerUncoreCounterState::EventPosition::xPI_TxL0P_POWER_CYCLES] = (uint32)Q_P_PCI_PMON_CTL_EVENT((PCM::hasUPI(cpu_model) ? 0x27 : 0x0D)); // L0p Tx Cycles (TxL0P_POWER_CYCLES) xPIEvents[ServerUncoreCounterState::EventPosition::xPI_L1_POWER_CYCLES] = (uint32)Q_P_PCI_PMON_CTL_EVENT((PCM::hasUPI(cpu_model) ? 0x21 : 0x12)); // L1 Cycles (L1_POWER_CYCLES) xPIEvents[ServerUncoreCounterState::EventPosition::xPI_CLOCKTICKS] = (uint32)Q_P_PCI_PMON_CTL_EVENT((PCM::hasUPI(cpu_model) ? 0x01 : 0x14)); // QPI/UPI clocks (CLOCKTICKS) programXPI(xPIEvents); uint32 MCCntConfig[4] = {0,0,0,0}; unsigned int UNC_M_POWER_CKE_CYCLES = 0x83; switch (cpu_model) { case PCM::ICX: case PCM::SNOWRIDGE: case PCM::SPR: UNC_M_POWER_CKE_CYCLES = 0x47; break; } switch(mc_profile) { case 0: // POWER_CKE_CYCLES.RANK0 and POWER_CKE_CYCLES.RANK1 MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(1) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(1) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(2) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(2) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; break; case 1: // POWER_CKE_CYCLES.RANK2 and POWER_CKE_CYCLES.RANK3 MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(4) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(4) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(8) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(8) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; break; case 2: // POWER_CKE_CYCLES.RANK4 and POWER_CKE_CYCLES.RANK5 MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x10) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x10) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x20) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x20) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; break; case 3: // POWER_CKE_CYCLES.RANK6 and POWER_CKE_CYCLES.RANK7 MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x40) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x40) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x80) + MC_CH_PCI_PMON_CTL_INVERT + MC_CH_PCI_PMON_CTL_THRESH(1); MCCntConfig[3] = MC_CH_PCI_PMON_CTL_EVENT(UNC_M_POWER_CKE_CYCLES) + MC_CH_PCI_PMON_CTL_UMASK(0x80) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; break; case 4: // POWER_SELF_REFRESH MCCntConfig[0] = MC_CH_PCI_PMON_CTL_EVENT(0x43); MCCntConfig[1] = MC_CH_PCI_PMON_CTL_EVENT(0x43) + MC_CH_PCI_PMON_CTL_THRESH(1) + MC_CH_PCI_PMON_CTL_EDGE_DET; MCCntConfig[2] = MC_CH_PCI_PMON_CTL_EVENT(0x85); break; } programIMC(MCCntConfig); } void enableAndResetMCFixedCounter(UncorePMU& pmu) { // enable fixed counter (DRAM clocks) *pmu.fixedCounterControl = MC_CH_PCI_PMON_FIXED_CTL_EN; // reset it *pmu.fixedCounterControl = MC_CH_PCI_PMON_FIXED_CTL_EN + MC_CH_PCI_PMON_FIXED_CTL_RST; } void ServerUncorePMUs::programIMC(const uint32 * MCCntConfig) { const uint32 extraIMC = (cpu_model == PCM::SKX)?UNC_PMON_UNIT_CTL_RSV:UNC_PMON_UNIT_CTL_FRZ_EN; for (uint32 i = 0; i < (uint32)imcPMUs.size(); ++i) { // imc PMU imcPMUs[i].initFreeze(extraIMC); enableAndResetMCFixedCounter(imcPMUs[i]); PCM::program(imcPMUs[i], MCCntConfig, MCCntConfig + 4, extraIMC); } } void ServerUncorePMUs::programEDC(const uint32 * EDCCntConfig) { for (uint32 i = 0; i < (uint32)edcPMUs.size(); ++i) { edcPMUs[i].initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); // HBM clocks enabled by default if (cpu_model == PCM::KNL) { *edcPMUs[i].fixedCounterControl = EDC_CH_PCI_PMON_FIXED_CTL_EN; } else { enableAndResetMCFixedCounter(edcPMUs[i]); } PCM::program(edcPMUs[i], EDCCntConfig, EDCCntConfig + 4, UNC_PMON_UNIT_CTL_FRZ_EN); } } void ServerUncorePMUs::programM2M() { uint64 cfg[4] = {0, 0, 0, 0}; switch (cpu_model) { case PCM::SPR: cfg[EventPosition::M2M_CLOCKTICKS] = M2M_PCI_PMON_CTL_EVENT(0x01); // CLOCKTICKS cfg[EventPosition::PMM_READ] = M2M_PCI_PMON_CTL_EVENT(0x24) + M2M_PCI_PMON_CTL_UMASK(0x20) + UNC_PMON_CTL_UMASK_EXT(0x03); // UNC_M2M_IMC_READS.TO_PMM cfg[EventPosition::PMM_WRITE] = M2M_PCI_PMON_CTL_EVENT(0x25) + M2M_PCI_PMON_CTL_UMASK(0x80) + UNC_PMON_CTL_UMASK_EXT(0x18); // UNC_M2M_IMC_WRITES.TO_PMM break; case PCM::ICX: cfg[EventPosition::NM_HIT] = M2M_PCI_PMON_CTL_EVENT(0x2c) + M2M_PCI_PMON_CTL_UMASK(3); // UNC_M2M_TAG_HIT.NM_DRD_HIT_* events (CLEAN | DIRTY) cfg[EventPosition::M2M_CLOCKTICKS] = 0; // CLOCKTICKS cfg[EventPosition::PMM_READ] = M2M_PCI_PMON_CTL_EVENT(0x37) + M2M_PCI_PMON_CTL_UMASK(0x20) + UNC_PMON_CTL_UMASK_EXT(0x07); // UNC_M2M_IMC_READS.TO_PMM cfg[EventPosition::PMM_WRITE] = M2M_PCI_PMON_CTL_EVENT(0x38) + M2M_PCI_PMON_CTL_UMASK(0x80) + UNC_PMON_CTL_UMASK_EXT(0x1C); // UNC_M2M_IMC_WRITES.TO_PMM break; default: cfg[EventPosition::NM_HIT] = M2M_PCI_PMON_CTL_EVENT(0x2c) + M2M_PCI_PMON_CTL_UMASK(3); // UNC_M2M_TAG_HIT.NM_DRD_HIT_* events (CLEAN | DIRTY) cfg[EventPosition::M2M_CLOCKTICKS] = 0; // CLOCKTICKS cfg[EventPosition::PMM_READ] = M2M_PCI_PMON_CTL_EVENT(0x37) + M2M_PCI_PMON_CTL_UMASK(0x8); // UNC_M2M_IMC_READS.TO_PMM cfg[EventPosition::PMM_WRITE] = M2M_PCI_PMON_CTL_EVENT(0x38) + M2M_PCI_PMON_CTL_UMASK(0x20); // UNC_M2M_IMC_WRITES.TO_PMM } programM2M(cfg); } void ServerUncorePMUs::programM2M(const uint64* M2MCntConfig) { { for (auto & pmu : m2mPMUs) { // std::cout << "programming m2m pmu "<< i++ << std::endl; pmu.initFreeze(UNC_PMON_UNIT_CTL_RSV); PCM::program(pmu, M2MCntConfig, M2MCntConfig + 4, UNC_PMON_UNIT_CTL_RSV); } } } void ServerUncorePMUs::programM3UPI(const uint32* M3UPICntConfig) { { for (auto& pmu : m3upiPMUs) { pmu.initFreeze(UNC_PMON_UNIT_CTL_RSV); PCM::program(pmu, M3UPICntConfig, M3UPICntConfig + 4, UNC_PMON_UNIT_CTL_RSV); } } } void ServerUncorePMUs::programHA(const uint32 * config) { for (auto & pmu : haPMUs) { pmu.initFreeze(UNC_PMON_UNIT_CTL_RSV); PCM::program(pmu, config, config + 4, UNC_PMON_UNIT_CTL_RSV); } } uint64 ServerUncorePMUs::getHARequests() { uint64 result = 0; for (auto & pmu: haPMUs) { result += *pmu.counterValue[PCM::EventPosition::REQUESTS_ALL]; } return result; } uint64 ServerUncorePMUs::getHALocalRequests() { uint64 result = 0; for (auto & pmu: haPMUs) { result += *pmu.counterValue[PCM::EventPosition::REQUESTS_LOCAL]; } return result; } void ServerUncorePMUs::programHA() { uint32 config[4]; config[0] = 0; config[1] = 0; #ifdef PCM_HA_REQUESTS_READS_ONLY // HA REQUESTS READ: LOCAL + REMOTE config[PCM::EventPosition::REQUESTS_ALL] = HA_PCI_PMON_CTL_EVENT(0x01) + HA_PCI_PMON_CTL_UMASK((1 + 2)); // HA REQUESTS READ: LOCAL ONLY config[PCM::EventPosition::REQUESTS_LOCAL] = HA_PCI_PMON_CTL_EVENT(0x01) + HA_PCI_PMON_CTL_UMASK((1)); #else // HA REQUESTS READ+WRITE+REMOTE+LOCAL config[PCM::EventPosition::REQUESTS_ALL] = HA_PCI_PMON_CTL_EVENT(0x01) + HA_PCI_PMON_CTL_UMASK((1 + 2 + 4 + 8)); // HA REQUESTS READ+WRITE (LOCAL only) config[PCM::EventPosition::REQUESTS_LOCAL] = HA_PCI_PMON_CTL_EVENT(0x01) + HA_PCI_PMON_CTL_UMASK((1 + 4)); #endif programHA(config); } void ServerUncorePMUs::freezeCounters() { for (auto& pmuVector : allPMUs) { for (auto& pmu : *pmuVector) { pmu.freeze((cpu_model == PCM::SKX) ? UNC_PMON_UNIT_CTL_RSV : UNC_PMON_UNIT_CTL_FRZ_EN); } } } void ServerUncorePMUs::unfreezeCounters() { for (auto& pmuVector : allPMUs) { for (auto& pmu : *pmuVector) { pmu.unfreeze((cpu_model == PCM::SKX) ? UNC_PMON_UNIT_CTL_RSV : UNC_PMON_UNIT_CTL_FRZ_EN); } } } uint64 ServerUncorePMUs::getQPIClocks(uint32 port) { return getQPILLCounter(port, ServerUncoreCounterState::EventPosition::xPI_CLOCKTICKS); } uint64 ServerUncorePMUs::getQPIL0pTxCycles(uint32 port) { return getQPILLCounter(port, ServerUncoreCounterState::EventPosition::xPI_TxL0P_POWER_CYCLES); } uint64 ServerUncorePMUs::getQPIL1Cycles(uint32 port) { return getQPILLCounter(port, ServerUncoreCounterState::EventPosition::xPI_L1_POWER_CYCLES); } uint64 ServerUncorePMUs::getDRAMClocks(uint32 channel) { uint64 result = 0; if (channel < (uint32)imcPMUs.size()) result = *(imcPMUs[channel].fixedCounterValue); // std::cout << "DEBUG: DRAMClocks on channel " << channel << " = " << result << "\n"; return result; } uint64 ServerUncorePMUs::getHBMClocks(uint32 channel) { uint64 result = 0; if (channel < (uint32)edcPMUs.size()) result = *edcPMUs[channel].fixedCounterValue; // std::cout << "DEBUG: HBMClocks on EDC" << channel << " = " << result << "\n"; return result; } uint64 ServerUncorePMUs::getPMUCounter(std::vector & pmu, const uint32 id, const uint32 counter) { uint64 result = 0; if (id < (uint32)pmu.size() && counter < 4 && pmu[id].counterValue[counter].get() != nullptr) { result = *(pmu[id].counterValue[counter]); } else { //std::cout << "DEBUG: Invalid ServerUncorePMUs::getPMUCounter(" << id << ", " << counter << ") \n"; } // std::cout << "DEBUG: ServerUncorePMUs::getPMUCounter(" << id << ", " << counter << ") = " << result << "\n"; return result; } uint64 ServerUncorePMUs::getMCCounter(uint32 channel, uint32 counter) { return getPMUCounter(imcPMUs, channel, counter); } uint64 ServerUncorePMUs::getEDCCounter(uint32 channel, uint32 counter) { return getPMUCounter(edcPMUs, channel, counter); } uint64 ServerUncorePMUs::getM2MCounter(uint32 box, uint32 counter) { return getPMUCounter(m2mPMUs, box, counter); } uint64 ServerUncorePMUs::getQPILLCounter(uint32 port, uint32 counter) { return getPMUCounter(xpiPMUs, port, counter); } uint64 ServerUncorePMUs::getM3UPICounter(uint32 port, uint32 counter) { // std::cout << "DEBUG: ServerUncorePMUs::getM3UPICounter(" << port << ", " << counter << ") = " << getPMUCounter(m3upiPMUs, port, counter) << "\n"; return getPMUCounter(m3upiPMUs, port, counter); } void ServerUncorePMUs::enableJKTWorkaround(bool enable) { { PciHandleType reg(groupnr,iMCbus,14,0); uint32 value = 0; reg.read32(0x84, &value); if(enable) value |= 2; else value &= (~2); reg.write32(0x84, value); } { PciHandleType reg(groupnr,iMCbus,8,0); uint32 value = 0; reg.read32(0x80, &value); if(enable) value |= 2; else value &= (~2); reg.write32(0x80, value); } { PciHandleType reg(groupnr,iMCbus,9,0); uint32 value = 0; reg.read32(0x80, &value); if(enable) value |= 2; else value &= (~2); reg.write32(0x80, value); } } #define PCM_MEM_CAPACITY (1024ULL*1024ULL*64ULL) // 64 MByte void ServerUncorePMUs::initMemTest(ServerUncorePMUs::MemTestParam & param) { auto & memBufferBlockSize = param.first; auto & memBuffers = param.second; #ifdef __linux__ size_t capacity = PCM_MEM_CAPACITY; char * buffer = (char *)mmap(NULL, capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); if (buffer == MAP_FAILED) { std::cerr << "ERROR: mmap failed\n"; return; } const int64 onlineNodes = (int64)readMaxFromSysFS("/sys/devices/system/node/online"); unsigned long long maxNode = (unsigned long long)(onlineNodes + 1); if (maxNode == 0) { std::cerr << "ERROR: max node is 0 \n"; return; } if (maxNode >= 63) maxNode = 63; const unsigned long long nodeMask = (1ULL << maxNode) - 1ULL; if (0 != syscall(SYS_mbind, buffer, capacity, 3 /* MPOL_INTERLEAVE */, &nodeMask, maxNode, 0)) { std::cerr << "ERROR: mbind failed. nodeMask: " << nodeMask << " maxNode: " << maxNode << "\n"; return; } memBuffers.push_back((uint64 *)buffer); memBufferBlockSize = capacity; #elif defined(_MSC_VER) ULONG HighestNodeNumber; if (!GetNumaHighestNodeNumber(&HighestNodeNumber)) { std::cerr << "ERROR: GetNumaHighestNodeNumber call failed.\n"; return; } memBufferBlockSize = 4096; for (int i = 0; i < PCM_MEM_CAPACITY / memBufferBlockSize; ++i) { LPVOID result = VirtualAllocExNuma( GetCurrentProcess(), NULL, memBufferBlockSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, i % (HighestNodeNumber + 1) ); if (result == NULL) { std::cerr << "ERROR: " << i << " VirtualAllocExNuma failed.\n"; for (auto& b : memBuffers) { VirtualFree(b, memBufferBlockSize, MEM_RELEASE); } memBuffers.clear(); break; } else { memBuffers.push_back((uint64 *)result); } } #else std::cerr << "ERROR: memory test is not implemented. QPI/UPI speed and utilization metrics may not be reliable.\n"; #endif for (auto& b : memBuffers) std::fill(b, b + (memBufferBlockSize / sizeof(uint64)), 0ULL); } void ServerUncorePMUs::doMemTest(const ServerUncorePMUs::MemTestParam & param) { const auto & memBufferBlockSize = param.first; const auto & memBuffers = param.second; // read and write each cache line once for (auto& b : memBuffers) for (unsigned int i = 0; i < memBufferBlockSize / sizeof(uint64); i += 64 / sizeof(uint64)) { (b[i])++; } } void ServerUncorePMUs::cleanupMemTest(const ServerUncorePMUs::MemTestParam & param) { const auto & memBufferBlockSize = param.first; const auto & memBuffers = param.second; for (auto& b : memBuffers) { #if defined(__linux__) munmap(b, memBufferBlockSize); #elif defined(_MSC_VER) VirtualFree(b, memBufferBlockSize, MEM_RELEASE); #elif defined(__FreeBSD__) || defined(__APPLE__) (void) b; // avoid the unused variable warning (void) memBufferBlockSize; // avoid the unused variable warning #else #endif } } uint64 ServerUncorePMUs::computeQPISpeed(const uint32 core_nr, const int cpumodel) { if(qpi_speed.empty()) { PCM * pcm = PCM::getInstance(); TemporalThreadAffinity aff(core_nr); qpi_speed.resize(getNumQPIPorts()); auto getSpeed = [&] (size_t i) { if (i == 1) return 0ULL; // link 1 should have the same speed as link 0, skip it uint64 result = 0; if (PCM::hasUPI(cpumodel) == false && i < XPIRegisterLocation.size()) { PciHandleType reg(groupnr,UPIbus, XPIRegisterLocation[i].first, QPI_PORT0_MISC_REGISTER_FUNC_ADDR); uint32 value = 0; reg.read32(QPI_RATE_STATUS_ADDR, &value); value &= 7; // extract lower 3 bits if(value) result = static_cast((4000000000ULL + ((uint64)value)*800000000ULL)*2ULL); } if(result == 0ULL) { if (PCM::hasUPI(cpumodel) == false) std::cerr << "Warning: QPI_RATE_STATUS register is not available on port " << i << ". Computing QPI speed using a measurement loop.\n"; // compute qpi speed const uint64 timerGranularity = 1000000ULL; // mks MemTestParam param; initMemTest(param); uint64 startClocks = getQPIClocks((uint32)i); uint64 startTSC = pcm->getTickCount(timerGranularity, core_nr); uint64 endTSC; do { doMemTest(param); endTSC = pcm->getTickCount(timerGranularity, core_nr); } while (endTSC - startTSC < 200000ULL); // spin for 200 ms uint64 endClocks = getQPIClocks((uint32)i); cleanupMemTest(param); result = (uint64(double(endClocks - startClocks) * PCM::getBytesPerLinkCycle(cpumodel) * double(timerGranularity) / double(endTSC - startTSC))); if(cpumodel == PCM::HASWELLX || cpumodel == PCM::BDX) /* BDX_DE does not have QPI. */{ result /=2; // HSX runs QPI clocks with doubled speed } } return result; }; std::vector > getSpeedsAsync; for (size_t i = 0; i < getNumQPIPorts(); ++i) { getSpeedsAsync.push_back(std::async(std::launch::async, getSpeed, i)); } for (size_t i = 0; i < getNumQPIPorts(); ++i) { qpi_speed[i] = (i==1)? qpi_speed[0] : getSpeedsAsync[i].get(); // link 1 does not have own speed register, it runs with the speed of link 0 } if (PCM::hasUPI(cpumodel)) { // check the speed of link 3 if(qpi_speed.size() == 3 && qpi_speed[2] == 0) { std::cerr << "UPI link 3 is disabled\n"; qpi_speed.resize(2); xpiPMUs.resize(2); } } } if(!qpi_speed.empty()) { return *std::max_element(qpi_speed.begin(),qpi_speed.end()); } else { return 0; } } void ServerUncorePMUs::reportQPISpeed() const { PCM * m = PCM::getInstance(); std::cerr.precision(1); std::cerr << std::fixed; for (uint32 i = 0; i < (uint32)qpi_speed.size(); ++i) std::cerr << "Max " << m->xPI() << " link " << i << " speed: " << qpi_speed[i] / (1e9) << " GBytes/second (" << qpi_speed[i] / (1e9 * m->getBytesPerLinkTransfer()) << " GT/second)\n"; } uint64 PCM::CX_MSR_PMON_CTRY(uint32 Cbo, uint32 Ctr) const { if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) { return JKT_C0_MSR_PMON_CTR0 + ((JKTIVT_CBO_MSR_STEP)*Cbo) + Ctr; } else if(HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model) { return HSX_C0_MSR_PMON_CTR0 + ((HSX_CBO_MSR_STEP)*Cbo) + Ctr; } else if (ICX == cpu_model || SNOWRIDGE == cpu_model) { return CX_MSR_PMON_BOX_CTL(Cbo) + SERVER_CHA_MSR_PMON_CTR0_OFFSET + Ctr; } else if (SPR == cpu_model) { return SPR_CHA0_MSR_PMON_CTR0 + SPR_CHA_MSR_STEP * Cbo + Ctr; } return 0; } uint64 PCM::CX_MSR_PMON_BOX_FILTER(uint32 Cbo) const { if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) { return JKT_C0_MSR_PMON_BOX_FILTER + ((JKTIVT_CBO_MSR_STEP)*Cbo); } else if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model) { return HSX_C0_MSR_PMON_BOX_FILTER + ((HSX_CBO_MSR_STEP)*Cbo); } else if (KNL == cpu_model) { return KNL_CHA0_MSR_PMON_BOX_CTL + ((KNL_CHA_MSR_STEP)*Cbo); } else if (ICX == cpu_model) { return CX_MSR_PMON_BOX_CTL(Cbo) + SERVER_CHA_MSR_PMON_BOX_FILTER_OFFSET; } else if (SPR == cpu_model) { return SPR_CHA0_MSR_PMON_BOX_FILTER + SPR_CHA_MSR_STEP * Cbo; } return 0; } uint64 PCM::CX_MSR_PMON_BOX_FILTER1(uint32 Cbo) const { if(IVYTOWN == cpu_model) { return IVT_C0_MSR_PMON_BOX_FILTER1 + ((JKTIVT_CBO_MSR_STEP)*Cbo); } else if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model) { return HSX_C0_MSR_PMON_BOX_FILTER1 + ((HSX_CBO_MSR_STEP)*Cbo); } return 0; } uint64 PCM::CX_MSR_PMON_CTLY(uint32 Cbo, uint32 Ctl) const { if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) { return JKT_C0_MSR_PMON_CTL0 + ((JKTIVT_CBO_MSR_STEP)*Cbo) + Ctl; } else if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model) { return HSX_C0_MSR_PMON_CTL0 + ((HSX_CBO_MSR_STEP)*Cbo) + Ctl; } else if (ICX == cpu_model || SNOWRIDGE == cpu_model) { return CX_MSR_PMON_BOX_CTL(Cbo) + SERVER_CHA_MSR_PMON_CTL0_OFFSET + Ctl; } else if (SPR == cpu_model) { return SPR_CHA0_MSR_PMON_CTL0 + SPR_CHA_MSR_STEP * Cbo + Ctl; } return 0; } uint64 PCM::CX_MSR_PMON_BOX_CTL(uint32 Cbo) const { if(JAKETOWN == cpu_model || IVYTOWN == cpu_model) { return JKT_C0_MSR_PMON_BOX_CTL + ((JKTIVT_CBO_MSR_STEP)*Cbo); } else if (HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model) { return HSX_C0_MSR_PMON_BOX_CTL + ((HSX_CBO_MSR_STEP)*Cbo); } else if (KNL == cpu_model) { return KNL_CHA0_MSR_PMON_BOX_CTRL + ((KNL_CHA_MSR_STEP)*Cbo); } else if (ICX == cpu_model) { return ICX_CHA_MSR_PMON_BOX_CTL[Cbo]; } else if (SPR == cpu_model) { return SPR_CHA0_MSR_PMON_BOX_CTRL + SPR_CHA_MSR_STEP * Cbo; } else if (SNOWRIDGE == cpu_model) { return SNR_CHA_MSR_PMON_BOX_CTL[Cbo]; } return 0; } // Return the first device found with specific vendor/device IDs PciHandleType * getDeviceHandle(uint32 vendorId, uint32 deviceId) { #ifdef __linux__ const std::vector & mcfg = PciHandleMM::getMCFGRecords(); #else std::vector mcfg; MCFGRecord segment; segment.PCISegmentGroupNumber = 0; segment.startBusNumber = 0; segment.endBusNumber = 0xff; mcfg.push_back(segment); #endif for(uint32 s = 0; s < (uint32)mcfg.size(); ++s) { for (uint32 bus = (uint32)mcfg[s].startBusNumber; bus <= (uint32)mcfg[s].endBusNumber; ++bus) { for (uint32 device = 0; device < 0x20; ++device) { for (uint32 function = 0; function < 0x8; ++function) { if (PciHandleType::exists(mcfg[s].PCISegmentGroupNumber, bus, device, function)) { PciHandleType * h = new PciHandleType(mcfg[s].PCISegmentGroupNumber, bus, device, function); uint32 value; h->read32(0, &value); const uint32 vid = value & 0xffff; const uint32 did = (value >> 16) & 0xffff; if (vid == vendorId && did == deviceId) return h; delete h; } } } } } return NULL; } inline uint32 weight32(uint32 n) { uint32 count = 0; while (n) { n &= (n - 1); count++; } return count; } uint32 PCM::getMaxNumOfCBoxes() const { static int num = -1; if (num >= 0) { return (uint32)num; } const auto refCore = socketRefCore[0]; uint64 val = 0; switch (cpu_model) { case SPR: { PciHandleType * h = getDeviceHandle(PCM_INTEL_PCI_VENDOR_ID, 0x325b); if (h) { uint32 value; h->read32(0x9c, &value); num = (uint32)weight32(value); h->read32(0xa0, &value); num += (uint32)weight32(value); delete h; } } break; case KNL: case SKX: case ICX: { /* * on KNL two physical cores share CHA. * The number of CHAs in the processor is stored in bits 5:0 * of NCUPMONConfig [0x702] MSR. */ const auto NCUPMONConfig = 0x702; MSR[refCore]->read(NCUPMONConfig, &val); } num = (uint32)(val & 63); break; case SNOWRIDGE: num = (uint32)num_phys_cores_per_socket / 4; break; default: /* * on other supported CPUs there is one CBox per physical core. This calculation will get us * the number of physical cores per socket which is the expected * value to be returned. */ num = (uint32)num_phys_cores_per_socket; } #ifdef PCM_USE_PERF if (num == 0) { num = (uint32)enumeratePerfPMUs("cbox", 100).size(); } if (num == 0) { num = (uint32)enumeratePerfPMUs("cha", 100).size(); } #endif assert(num >= 0); return (uint32)num; } uint32 PCM::getMaxNumOfIIOStacks() const { if (iioPMUs.size() > 0) { assert(iioPMUs[0].size() == irpPMUs[0].size()); return (uint32)iioPMUs[0].size(); } return 0; } uint32 PCM::getMaxNumOfMDFs() const { if (mdfPMUs.size() > 0) { return (uint32)mdfPMUs[0].size(); } return 0; } void PCM::programCboOpcodeFilter(const uint32 opc0, UncorePMU & pmu, const uint32 nc_, const uint32 opc1, const uint32 loc, const uint32 rem) { if(JAKETOWN == cpu_model) { *pmu.filter[0] = JKT_CBO_MSR_PMON_BOX_FILTER_OPC(opc0); } else if(IVYTOWN == cpu_model || HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model) { *pmu.filter[1] = IVTHSX_CBO_MSR_PMON_BOX_FILTER1_OPC(opc0); } else if(SKX == cpu_model) { *pmu.filter[1] = SKX_CHA_MSR_PMON_BOX_FILTER1_OPC0(opc0) + SKX_CHA_MSR_PMON_BOX_FILTER1_OPC1(opc1) + (rem?SKX_CHA_MSR_PMON_BOX_FILTER1_REM(1):0ULL) + (loc?SKX_CHA_MSR_PMON_BOX_FILTER1_LOC(1):0ULL) + SKX_CHA_MSR_PMON_BOX_FILTER1_NM(1) + SKX_CHA_MSR_PMON_BOX_FILTER1_NOT_NM(1) + (nc_?SKX_CHA_MSR_PMON_BOX_FILTER1_NC(1):0ULL); } else { std::cerr << "ERROR: programCboOpcodeFilter function is not implemented for cpu model " << cpu_model << std::endl; throw std::exception(); } } void PCM::programIIOCounters(uint64 rawEvents[4], int IIOStack) { std::vector IIO_units; if (IIOStack == -1) { int stacks_count; switch (getCPUModel()) { case PCM::SPR: stacks_count = SPR_M2IOSF_NUM; break; case PCM::ICX: stacks_count = ICX_IIO_STACK_COUNT; break; case PCM::SNOWRIDGE: stacks_count = SNR_IIO_STACK_COUNT; break; case PCM::SKX: default: stacks_count = SKX_IIO_STACK_COUNT; break; } IIO_units.reserve(stacks_count); for (int stack = 0; stack < stacks_count; ++stack) { IIO_units.push_back(stack); } } else IIO_units.push_back(IIOStack); for (int32 i = 0; (i < num_sockets) && MSR.size() && iioPMUs.size(); ++i) { uint32 refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux for (const auto & unit: IIO_units) { if (iioPMUs[i].count(unit) == 0) { std::cerr << "IIO PMU unit (stack) " << unit << " is not found \n"; continue; } auto & pmu = iioPMUs[i][unit]; pmu.initFreeze(UNC_PMON_UNIT_CTL_RSV); program(pmu, &rawEvents[0], &rawEvents[4], UNC_PMON_UNIT_CTL_RSV); } } } void PCM::programIRPCounters(uint64 rawEvents[4], int IIOStack) { std::vector IIO_units; if (IIOStack == -1) { for (uint32 stack = 0; stack < getMaxNumOfIIOStacks(); ++stack) { IIO_units.push_back(stack); } } else { IIO_units.push_back(IIOStack); } for (int32 i = 0; (i < num_sockets) && MSR.size() && irpPMUs.size(); ++i) { uint32 refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux for (const auto& unit : IIO_units) { if (irpPMUs[i].count(unit) == 0) { std::cerr << "IRP PMU unit (stack) " << unit << " is not found \n"; continue; } auto& pmu = irpPMUs[i][unit]; pmu.initFreeze(UNC_PMON_UNIT_CTL_RSV); program(pmu, &rawEvents[0], &rawEvents[2], UNC_PMON_UNIT_CTL_RSV); } } } void PCM::programPCIeEventGroup(eventGroup_t &eventGroup) { assert(eventGroup.size() > 0); uint64 events[4] = {0}; uint64 umask[4] = {0}; switch (cpu_model) { case PCM::SPR: case PCM::ICX: case PCM::SNOWRIDGE: for (uint32 idx = 0; idx < eventGroup.size(); ++idx) events[idx] = eventGroup[idx]; programCbo(events); break; case PCM::SKX: //JKT through СLX generations allow programming only one required event at a time. if (eventGroup[0] & SKX_CHA_MSR_PMON_BOX_FILTER1_NC(1)) umask[0] |= (uint64)(SKX_CHA_TOR_INSERTS_UMASK_IRQ(1)); else umask[0] |= (uint64)(SKX_CHA_TOR_INSERTS_UMASK_PRQ(1)); if (eventGroup[0] & SKX_CHA_MSR_PMON_BOX_FILTER1_RSV(1)) umask[0] |= (uint64)(SKX_CHA_TOR_INSERTS_UMASK_HIT(1)); else umask[0] |= (uint64)(SKX_CHA_TOR_INSERTS_UMASK_MISS(1)); events[0] += CBO_MSR_PMON_CTL_EVENT(0x35) + CBO_MSR_PMON_CTL_UMASK(umask[0]); programCbo(events, SKX_CHA_MSR_PMON_BOX_GET_OPC0(eventGroup[0]), SKX_CHA_MSR_PMON_BOX_GET_NC(eventGroup[0])); break; case PCM::BDX_DE: case PCM::BDX: case PCM::KNL: case PCM::HASWELLX: case PCM::IVYTOWN: case PCM::JAKETOWN: events[0] = CBO_MSR_PMON_CTL_EVENT(0x35); events[0] += BDX_CBO_MSR_PMON_BOX_GET_FLT(eventGroup[0]) ? CBO_MSR_PMON_CTL_UMASK(0x3) : CBO_MSR_PMON_CTL_UMASK(1); events[0] += BDX_CBO_MSR_PMON_BOX_GET_TID(eventGroup[0]) ? CBO_MSR_PMON_CTL_TID_EN : 0ULL; programCbo(events, BDX_CBO_MSR_PMON_BOX_GET_OPC0(eventGroup[0]), 0, BDX_CBO_MSR_PMON_BOX_GET_TID(eventGroup[0]) ? 0x3e : 0ULL); break; } } void PCM::programCbo(const uint64 * events, const uint32 opCode, const uint32 nc_, const uint32 llc_lookup_tid_filter, const uint32 loc, const uint32 rem) { for (size_t i = 0; (i < cboPMUs.size()) && MSR.size(); ++i) { uint32 refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux for(uint32 cbo = 0; cbo < getMaxNumOfCBoxes() && cbo < cboPMUs[i].size(); ++cbo) { cboPMUs[i][cbo].initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); if (ICX != cpu_model && SNOWRIDGE != cpu_model && SPR != cpu_model) programCboOpcodeFilter(opCode, cboPMUs[i][cbo], nc_, 0, loc, rem); if((HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || SKX == cpu_model) && llc_lookup_tid_filter != 0) *cboPMUs[i][cbo].filter[0] = llc_lookup_tid_filter; PCM::program(cboPMUs[i][cbo], events, events + ServerUncoreCounterState::maxCounters, UNC_PMON_UNIT_CTL_FRZ_EN); for (int c = 0; c < ServerUncoreCounterState::maxCounters && size_t(c) < cboPMUs[i][cbo].size(); ++c) { *cboPMUs[i][cbo].counterValue[c] = 0; } } } } void PCM::programCboRaw(const uint64* events, const uint64 filter0, const uint64 filter1) { for (size_t i = 0; (i < cboPMUs.size()) && MSR.size(); ++i) { uint32 refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux for (uint32 cbo = 0; cbo < getMaxNumOfCBoxes(); ++cbo) { cboPMUs[i][cbo].initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); if (cboPMUs[i][cbo].filter[0].get()) { *cboPMUs[i][cbo].filter[0] = filter0; } if (cboPMUs[i][cbo].filter[1].get()) { *cboPMUs[i][cbo].filter[1] = filter1; } PCM::program(cboPMUs[i][cbo], events, events + 4, UNC_PMON_UNIT_CTL_FRZ_EN); for (int c = 0; c < 4; ++c) { *cboPMUs[i][cbo].counterValue[c] = 0; } } } } void PCM::programMDF(const uint64* events) { for (size_t i = 0; (i < mdfPMUs.size()) && MSR.size(); ++i) { uint32 refCore = socketRefCore[i]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux for (auto & pmu : mdfPMUs[i]) { pmu.initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); PCM::program(pmu, events, events + 4, UNC_PMON_UNIT_CTL_FRZ_EN); } } } void PCM::programUBOX(const uint64* events) { for (size_t s = 0; (s < uboxPMUs.size()) && MSR.size(); ++s) { uint32 refCore = socketRefCore[s]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux uboxPMUs[s].initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); *uboxPMUs[s].fixedCounterControl = UCLK_FIXED_CTL_EN; if (events) { PCM::program(uboxPMUs[s], events, events + 2, 0); } } } void PCM::controlQATTelemetry(uint32 dev, uint32 operation) { if (getNumOfIDXAccelDevs(IDX_QAT) == 0 || dev >= getNumOfIDXAccelDevs(IDX_QAT) || operation >= PCM::QAT_TLM_MAX) return; auto &gControl_reg = idxPMUs[IDX_QAT][dev].generalControl; switch (operation) { case PCM::QAT_TLM_START: case PCM::QAT_TLM_STOP: case PCM::QAT_TLM_REFRESH: *gControl_reg = operation; break; default: break; } } void PCM::programCXLCM(const uint64* events) { for (auto & sPMUs : cxlPMUs) { for (auto& pmus : sPMUs) { pmus.first.initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); assert(pmus.first.size() == 8); PCM::program(pmus.first, events, events + 8, UNC_PMON_UNIT_CTL_FRZ_EN); } } } void PCM::programCXLDP(const uint64* events) { for (auto& sPMUs : cxlPMUs) { for (auto& pmus : sPMUs) { pmus.second.initFreeze(UNC_PMON_UNIT_CTL_FRZ_EN); assert(pmus.second.size() == 4); PCM::program(pmus.second, events, events + 4, UNC_PMON_UNIT_CTL_FRZ_EN); } } } void PCM::programCXLCM() { uint64 CXLCMevents[8] = { 0,0,0,0,0,0,0,0 }; CXLCMevents[EventPosition::CXL_TxC_MEM] = UNC_PMON_CTL_EVENT(0x02) + UNC_PMON_CTL_UMASK(0x10); // CXLCM_TxC_PACK_BUF_INSERTS.MEM_DATA CXLCMevents[EventPosition::CXL_TxC_CACHE] = UNC_PMON_CTL_EVENT(0x02) + UNC_PMON_CTL_UMASK(0x04);// CXLCM_TxC_PACK_BUF_INSERTS.CACHE_DATA programCXLCM(CXLCMevents); } void PCM::programIDXAccelCounters(uint32 accel, std::vector &events, std::vector &filters_wq, std::vector &filters_eng, std::vector &filters_tc, std::vector &filters_pgsz, std::vector &filters_xfersz) { uint32 maxCTR = getMaxNumOfIDXAccelCtrs(accel); //limit the number of physical counter to use if (events.size() == 0 || accel >= IDX_MAX || getNumOfIDXAccelDevs(accel) == 0) return; //invalid input parameter or IDX accel dev NOT exist if (events.size() < maxCTR) maxCTR = events.size(); for (auto & pmu : idxPMUs[accel]) { pmu.initFreeze(); for (uint32 i = 0; i < maxCTR; i++) { auto &ctrl_reg = pmu.counterControl[i]; auto &filter_wq_reg = pmu.counterFilterWQ[i]; auto &filter_eng_reg = pmu.counterFilterENG[i]; auto &filter_tc_reg = pmu.counterFilterTC[i]; auto &filter_pgsz_reg = pmu.counterFilterPGSZ[i]; auto &filter_xfersz_reg = pmu.counterFilterXFERSZ[i]; if (pmu.getPERFMode() == false) { //disable the counter before raw program in PMU direct mode. *ctrl_reg = 0x0; } *filter_wq_reg = extract_bits_ui(filters_wq.at(i), 0, 15); *filter_eng_reg = extract_bits_ui(filters_eng.at(i), 0, 15); *filter_tc_reg = extract_bits_ui(filters_tc.at(i), 0, 7); *filter_pgsz_reg = extract_bits_ui(filters_pgsz.at(i), 0, 7); *filter_xfersz_reg = extract_bits_ui(filters_xfersz.at(i), 0, 7); if (pmu.getPERFMode() == false) { *ctrl_reg = events.at(i); } else{ switch (accel) { case IDX_IAA: case IDX_DSA: //translate the event config from raw to perf format in Linux perf mode. //please reference the bitmap from DSA EAS spec and linux idxd driver perfmon interface. *ctrl_reg = ((extract_bits(events.at(i), 8, 11)) | ((extract_bits(events.at(i), 32, 59)) << 4)); break; case IDX_QAT://QAT NOT support perf mode break; default: break; } } } pmu.resetUnfreeze(); } } IDXCounterState PCM::getIDXAccelCounterState(uint32 accel, uint32 dev, uint32 counter_id) { IDXCounterState result; if (accel >= IDX_MAX || dev >= getNumOfIDXAccelDevs(accel) || counter_id >= getMaxNumOfIDXAccelCtrs(accel)) return result; result.data = *idxPMUs[accel][dev].counterValue[counter_id]; return result; } uint32 PCM::getNumOfIDXAccelDevs(int accel) const { if (accel >= IDX_MAX) return 0; return idxPMUs[accel].size(); } uint32 PCM::getMaxNumOfIDXAccelCtrs(int accel) const { uint32 retval = 0; if (supportIDXAccelDev() == true) { if (accel == IDX_IAA || accel == IDX_DSA) { retval = SPR_IDX_ACCEL_COUNTER_MAX_NUM; } else if(accel == IDX_QAT) { retval = SPR_QAT_ACCEL_COUNTER_MAX_NUM; } } return retval; } uint32 PCM::getNumaNodeOfIDXAccelDev(uint32 accel, uint32 dev) const { uint32 numa_node = 0xff; if (accel >= IDX_MAX || dev >= getNumOfIDXAccelDevs(accel)) return numa_node; numa_node = idxPMUs[accel][dev].getNumaNode(); return numa_node; } uint32 PCM::getCPUSocketIdOfIDXAccelDev(uint32 accel, uint32 dev) const { uint32 socketid = 0xff; if (accel >= IDX_MAX || dev >= getNumOfIDXAccelDevs(accel)) return socketid; socketid = idxPMUs[accel][dev].getSocketId(); return socketid; } bool PCM::supportIDXAccelDev() const { bool retval = false; switch (this->getCPUModel()) { case PCM::SPR: retval = true; break; default: retval = false; break; } return retval; } uint64 PCM::getCBOCounterState(const uint32 socket_, const uint32 ctr_) { uint64 result = 0; const uint32 refCore = socketRefCore[socket_]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux for(auto & pmu: cboPMUs[socket_]) { result += *pmu.counterValue[ctr_]; } return result; } uint64 PCM::getUncoreClocks(const uint32 socket_) { uint64 result = 0; if (socket_ < uboxPMUs.size()) { result = *uboxPMUs[socket_].fixedCounterValue; } return result; } PCIeCounterState PCM::getPCIeCounterState(const uint32 socket_, const uint32 ctr_) { PCIeCounterState result; result.data = getCBOCounterState(socket_, ctr_); return result; } uint64 PCM::getPCIeCounterData(const uint32 socket_, const uint32 ctr_) { return getCBOCounterState(socket_, ctr_); } void PCM::initLLCReadMissLatencyEvents(uint64 * events, uint32 & opCode) { if (LLCReadMissLatencyMetricsAvailable() == false) { return; } uint64 umask = 3ULL; // MISS_OPCODE switch (cpu_model) { case ICX: case SPR: case SNOWRIDGE: umask = 1ULL; break; case SKX: umask = (uint64)(SKX_CHA_TOR_INSERTS_UMASK_IRQ(1)) + (uint64)(SKX_CHA_TOR_INSERTS_UMASK_MISS(1)); break; } uint64 umask_ext = 0; switch (cpu_model) { case ICX: umask_ext = 0xC817FE; break; case SPR: umask_ext = 0x00C817FE; break; case SNOWRIDGE: umask_ext = 0xC827FE; break; } const uint64 all_umasks = CBO_MSR_PMON_CTL_UMASK(umask) + UNC_PMON_CTL_UMASK_EXT(umask_ext); events[EventPosition::TOR_OCCUPANCY] = CBO_MSR_PMON_CTL_EVENT(0x36) + all_umasks; // TOR_OCCUPANCY (must be on counter 0) events[EventPosition::TOR_INSERTS] = CBO_MSR_PMON_CTL_EVENT(0x35) + all_umasks; // TOR_INSERTS opCode = (SKX == cpu_model) ? 0x202 : 0x182; } void PCM::programCbo() { uint64 events[ServerUncoreCounterState::maxCounters]; std::fill(events, events + ServerUncoreCounterState::maxCounters, 0); uint32 opCode = 0; initLLCReadMissLatencyEvents(events, opCode); initCHARequestEvents(events); programCbo(events, opCode); programUBOX(nullptr); } void PCM::initCHARequestEvents(uint64 * config) { if (localMemoryRequestRatioMetricAvailable() && hasCHA()) { #ifdef PCM_HA_REQUESTS_READS_ONLY // HA REQUESTS READ: LOCAL + REMOTE config[EventPosition::REQUESTS_ALL] = CBO_MSR_PMON_CTL_EVENT(0x50) + CBO_MSR_PMON_CTL_UMASK((1 + 2)); // HA REQUESTS READ: LOCAL ONLY config[EventPosition::REQUESTS_LOCAL] = CBO_MSR_PMON_CTL_EVENT(0x50) + CBO_MSR_PMON_CTL_UMASK((1)); #else // HA REQUESTS READ+WRITE+REMOTE+LOCAL config[EventPosition::REQUESTS_ALL] = CBO_MSR_PMON_CTL_EVENT(0x50) + CBO_MSR_PMON_CTL_UMASK((1 + 2 + 4 + 8)); // HA REQUESTS READ+WRITE (LOCAL only) config[EventPosition::REQUESTS_LOCAL] = CBO_MSR_PMON_CTL_EVENT(0x50) + CBO_MSR_PMON_CTL_UMASK((1 + 4)); #endif } } CounterWidthExtender::CounterWidthExtender(AbstractRawCounter * raw_counter_, uint64 counter_width_, uint32 watchdog_delay_ms_) : raw_counter(raw_counter_), counter_width(counter_width_), watchdog_delay_ms(watchdog_delay_ms_) { last_raw_value = (*raw_counter)(); extended_value = last_raw_value; //std::cout << "Initial Value " << extended_value << "\n"; UpdateThread = new std::thread( [&]() { while (1) { MySleepMs(static_cast(this->watchdog_delay_ms)); /* uint64 dummy = */ this->read(); } } ); } CounterWidthExtender::~CounterWidthExtender() { delete UpdateThread; if (raw_counter) delete raw_counter; } UncorePMU::UncorePMU(const HWRegisterPtr& unitControl_, const HWRegisterPtr& counterControl0, const HWRegisterPtr& counterControl1, const HWRegisterPtr& counterControl2, const HWRegisterPtr& counterControl3, const HWRegisterPtr& counterValue0, const HWRegisterPtr& counterValue1, const HWRegisterPtr& counterValue2, const HWRegisterPtr& counterValue3, const HWRegisterPtr& fixedCounterControl_, const HWRegisterPtr& fixedCounterValue_, const HWRegisterPtr& filter0, const HWRegisterPtr& filter1 ) : cpu_model_(0), unitControl(unitControl_), counterControl{ counterControl0, counterControl1, counterControl2, counterControl3 }, counterValue{ counterValue0, counterValue1, counterValue2, counterValue3 }, fixedCounterControl(fixedCounterControl_), fixedCounterValue(fixedCounterValue_), filter{ filter0 , filter1 } { assert(counterControl.size() == counterValue.size()); } UncorePMU::UncorePMU(const HWRegisterPtr& unitControl_, const std::vector& counterControl_, const std::vector& counterValue_, const HWRegisterPtr& fixedCounterControl_, const HWRegisterPtr& fixedCounterValue_, const HWRegisterPtr& filter0, const HWRegisterPtr& filter1 ): cpu_model_(0), unitControl(unitControl_), counterControl{counterControl_}, counterValue{counterValue_}, fixedCounterControl(fixedCounterControl_), fixedCounterValue(fixedCounterValue_), filter{ filter0 , filter1 } { assert(counterControl.size() == counterValue.size()); } uint32 UncorePMU::getCPUModel() { if (cpu_model_ == 0) { cpu_model_ = PCM::getInstance()->getCPUModel(); } return cpu_model_; } void UncorePMU::cleanup() { for (auto& cc: counterControl) { if (cc.get()) *cc = 0; } if (unitControl.get()) *unitControl = 0; if (fixedCounterControl.get()) *fixedCounterControl = 0; } void UncorePMU::freeze(const uint32 extra) { *unitControl = (getCPUModel() == PCM::SPR) ? SPR_UNC_PMON_UNIT_CTL_FRZ : (extra + UNC_PMON_UNIT_CTL_FRZ); } void UncorePMU::unfreeze(const uint32 extra) { *unitControl = (getCPUModel() == PCM::SPR) ? 0 : extra; } bool UncorePMU::initFreeze(const uint32 extra, const char* xPICheckMsg) { if (unitControl.get() == nullptr) { return true; // this PMU does not have unit control register => no op } if (getCPUModel() == PCM::SPR) { *unitControl = SPR_UNC_PMON_UNIT_CTL_FRZ; // freeze *unitControl = SPR_UNC_PMON_UNIT_CTL_FRZ + SPR_UNC_PMON_UNIT_CTL_RST_CONTROL; // freeze and reset control registers return true; } // freeze enable *unitControl = extra; if (xPICheckMsg) { if ((extra & UNC_PMON_UNIT_CTL_VALID_BITS_MASK) != ((*unitControl) & UNC_PMON_UNIT_CTL_VALID_BITS_MASK)) { unitControl = nullptr; return false; } } // freeze *unitControl = extra + UNC_PMON_UNIT_CTL_FRZ; #ifdef PCM_UNCORE_PMON_BOX_CHECK_STATUS const uint64 val = *unitControl; if ((val & UNC_PMON_UNIT_CTL_VALID_BITS_MASK) != (extra + UNC_PMON_UNIT_CTL_FRZ)) { std::cerr << "ERROR: PMU counter programming seems not to work. PMON_BOX_CTL=0x" << std::hex << val << " needs to be =0x" << (UNC_PMON_UNIT_CTL_FRZ_EN + UNC_PMON_UNIT_CTL_FRZ) << std::dec << "\n"; if (xPICheckMsg) { std::cerr << xPICheckMsg; } } #endif return true; } void UncorePMU::resetUnfreeze(const uint32 extra) { if (getCPUModel() == PCM::SPR) { *unitControl = SPR_UNC_PMON_UNIT_CTL_FRZ + SPR_UNC_PMON_UNIT_CTL_RST_COUNTERS; // freeze and reset counter registers *unitControl = 0; // unfreeze return; } // reset counter values *unitControl = extra + UNC_PMON_UNIT_CTL_FRZ + UNC_PMON_UNIT_CTL_RST_COUNTERS; // unfreeze counters *unitControl = extra; } IDX_PMU::IDX_PMU(const bool perfMode_, const uint32 numaNode_, const uint32 socketId_, const HWRegisterPtr& resetControl_, const HWRegisterPtr& freezeControl_, const HWRegisterPtr& generalControl_, const std::vector & counterControl, const std::vector & counterValue, const std::vector & counterFilterWQ, const std::vector & counterFilterENG, const std::vector & counterFilterTC, const std::vector & counterFilterPGSZ, const std::vector & counterFilterXFERSZ ) : cpu_model_(0), perf_mode_(perfMode_), numa_node_(numaNode_), socket_id_(socketId_), resetControl(resetControl_), freezeControl(freezeControl_), generalControl(generalControl_), counterControl{counterControl}, counterValue{counterValue}, counterFilterWQ{counterFilterWQ}, counterFilterENG{counterFilterENG}, counterFilterTC{counterFilterTC}, counterFilterPGSZ{counterFilterPGSZ}, counterFilterXFERSZ{counterFilterXFERSZ} { assert(counterControl.size() == counterValue.size()); } uint32 IDX_PMU::getCPUModel() { if (cpu_model_ == 0) { cpu_model_ = PCM::getInstance()->getCPUModel(); } return cpu_model_; } void IDX_PMU::cleanup() { for (auto& cc: counterControl) { if (cc.get()) { *cc = 0; } } if (resetControl.get()) { *resetControl = 0x3; } if (generalControl.get()) { *generalControl = 0x0; } //std::cout << "IDX_PMU::cleanup \n"; } void IDX_PMU::freeze() { *freezeControl = 0xFFFFFFFF; } void IDX_PMU::unfreeze() { *freezeControl = 0x0; } bool IDX_PMU::initFreeze() { if (resetControl.get() == nullptr || freezeControl.get() == nullptr) { return true; // does not have reset/freeze control register => no op } *resetControl = 0x2; // reset counter freeze(); // freeze counter return true; } void IDX_PMU::resetUnfreeze() { unfreeze(); // unfreeze counter } bool IDX_PMU::getPERFMode() { return perf_mode_; } uint32 IDX_PMU::getNumaNode() const { return numa_node_; } uint32 IDX_PMU::getSocketId() const { return socket_id_; } IIOCounterState PCM::getIIOCounterState(int socket, int IIOStack, int counter) { IIOCounterState result; result.data = 0; if (socket < (int)iioPMUs.size() && iioPMUs[socket].count(IIOStack) > 0) { result.data = *iioPMUs[socket][IIOStack].counterValue[counter]; } return result; } void PCM::getIIOCounterStates(int socket, int IIOStack, IIOCounterState * result) { uint32 refCore = socketRefCore[socket]; TemporalThreadAffinity tempThreadAffinity(refCore); // speedup trick for Linux for (int c = 0; c < 4; ++c) { result[c] = getIIOCounterState(socket, IIOStack, c); } } void PCM::setupCustomCoreEventsForNuma(PCM::ExtendedCustomCoreEventDescription& conf) const { switch (this->getCPUModel()) { case PCM::WESTMERE_EX: // OFFCORE_RESPONSE.ANY_REQUEST.LOCAL_DRAM: Offcore requests satisfied by the local DRAM conf.OffcoreResponseMsrValue[0] = 0x40FF; // OFFCORE_RESPONSE.ANY_REQUEST.REMOTE_DRAM: Offcore requests satisfied by a remote DRAM conf.OffcoreResponseMsrValue[1] = 0x20FF; break; case PCM::JAKETOWN: case PCM::IVYTOWN: // OFFCORE_RESPONSE.*.LOCAL_DRAM conf.OffcoreResponseMsrValue[0] = 0x780400000 | 0x08FFF; // OFFCORE_RESPONSE.*.REMOTE_DRAM conf.OffcoreResponseMsrValue[1] = 0x7ff800000 | 0x08FFF; break; case PCM::HASWELLX: // OFFCORE_RESPONSE.*.LOCAL_DRAM conf.OffcoreResponseMsrValue[0] = 0x600400000 | 0x08FFF; // OFFCORE_RESPONSE.*.REMOTE_DRAM conf.OffcoreResponseMsrValue[1] = 0x63f800000 | 0x08FFF; break; case PCM::BDX: // OFFCORE_RESPONSE.ALL_REQUESTS.L3_MISS.LOCAL_DRAM conf.OffcoreResponseMsrValue[0] = 0x0604008FFF; // OFFCORE_RESPONSE.ALL_REQUESTS.L3_MISS.REMOTE_DRAM conf.OffcoreResponseMsrValue[1] = 0x067BC08FFF; break; case PCM::SKX: // OFFCORE_RESPONSE.ALL_REQUESTS.L3_MISS_LOCAL_DRAM.ANY_SNOOP conf.OffcoreResponseMsrValue[0] = 0x3FC0008FFF | (1 << 26); // OFFCORE_RESPONSE.ALL_REQUESTS.L3_MISS_REMOTE_(HOP0,HOP1,HOP2P)_DRAM.ANY_SNOOP conf.OffcoreResponseMsrValue[1] = 0x3FC0008FFF | (1 << 27) | (1 << 28) | (1 << 29); break; case PCM::ICX: std::cerr << "INFO: Monitored accesses include demand + L2 cache prefetcher, code read and RFO.\n"; // OCR.READS_TO_CORE.LOCAL_DRAM conf.OffcoreResponseMsrValue[0] = 0x0104000477; // OCR.READS_TO_CORE.REMOTE_DRAM conf.OffcoreResponseMsrValue[1] = 0x0730000477; break; case PCM::SPR: std::cout << "INFO: Monitored accesses include demand + L2 cache prefetcher, code read and RFO.\n"; // OCR.READS_TO_CORE.LOCAL_DRAM conf.OffcoreResponseMsrValue[0] = 0x104004477; // OCR.READS_TO_CORE.REMOTE_DRAM and OCR.READS_TO_CORE.SNC_DRAM conf.OffcoreResponseMsrValue[1] = 0x730004477 | 0x708004477; break; default: throw UnsupportedProcessorException(); } } } // namespace pcm pcm-202307/src/cpucounters.h000066400000000000000000005110561445420033100157060ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // Thomas Willhalm // and others #ifndef CPUCOUNTERS_HEADER #define CPUCOUNTERS_HEADER /*! \file cpucounters.h \brief Main CPU counters header Include this header file if you want to access CPU counters (core and uncore - including memory controller chips and QPI) */ #include "version.h" #ifndef PCM_API #define PCM_API #endif #undef PCM_HA_REQUESTS_READS_ONLY #undef PCM_DEBUG_TOPOLOGY // debug of topology enumeration routine #undef PCM_UNCORE_PMON_BOX_CHECK_STATUS // debug only #include "types.h" #include "topologyentry.h" #include "msr.h" #include "pci.h" #include "bw.h" #include "width_extender.h" #include "exceptions/unsupported_processor_exception.hpp" #include "uncore_pmu_discovery.h" #include #include #include #include #include #include #include #include #include #ifdef PCM_USE_PERF #include #include #define PCM_PERF_COUNT_HW_REF_CPU_CYCLES (9) #endif #ifndef _MSC_VER #define NOMINMAX #include #include #include #include #include #include #endif #ifdef _MSC_VER #if _MSC_VER>= 1600 #include #endif #endif #ifdef __linux__ #include "resctrl.h" #endif namespace pcm { #ifdef _MSC_VER void PCM_API restrictDriverAccess(LPCTSTR path); #endif class SystemCounterState; class SocketCounterState; class CoreCounterState; class BasicCounterState; class ServerUncoreCounterState; class PCM; class CoreTaskQueue; class SystemRoot; /* CPU performance monitoring routines A set of performance monitoring routines for recent Intel CPUs */ class HWRegister { public: virtual void operator = (uint64 val) = 0; // write operation virtual operator uint64 () = 0; //read operation virtual ~HWRegister() {} }; class PCICFGRegister64 : public HWRegister { std::shared_ptr handle; size_t offset; public: PCICFGRegister64(const std::shared_ptr & handle_, size_t offset_) : handle(handle_), offset(offset_) { } void operator = (uint64 val) override { cvt_ds cvt; cvt.ui64 = val; handle->write32(offset, cvt.ui32.low); handle->write32(offset + sizeof(uint32), cvt.ui32.high); } operator uint64 () override { uint64 result = 0; handle->read64(offset, &result); return result; } }; class PCICFGRegister32 : public HWRegister { std::shared_ptr handle; size_t offset; public: PCICFGRegister32(const std::shared_ptr & handle_, size_t offset_) : handle(handle_), offset(offset_) { } void operator = (uint64 val) override { handle->write32(offset, (uint32)val); } operator uint64 () override { uint32 result = 0; handle->read32(offset, &result); return result; } }; class MMIORegister64 : public HWRegister { std::shared_ptr handle; size_t offset; public: MMIORegister64(const std::shared_ptr & handle_, size_t offset_) : handle(handle_), offset(offset_) { } void operator = (uint64 val) override { // std::cout << std::hex << "MMIORegister64 writing " << val << " at offset " << offset << std::dec << std::endl; handle->write64(offset, val); } operator uint64 () override { const uint64 val = handle->read64(offset); // std::cout << std::hex << "MMIORegister64 read " << val << " from offset " << offset << std::dec << std::endl; return val; } }; class MMIORegister32 : public HWRegister { std::shared_ptr handle; size_t offset; public: MMIORegister32(const std::shared_ptr & handle_, size_t offset_) : handle(handle_), offset(offset_) { } void operator = (uint64 val) override { // std::cout << std::hex << "MMIORegister32 writing " << val << " at offset " << offset << std::dec << std::endl; handle->write32(offset, (uint32)val); } operator uint64 () override { const uint64 val = (uint64)handle->read32(offset); // std::cout << std::hex << "MMIORegister32 read " << val << " from offset " << offset << std::dec << std::endl; return val; } }; class MSRRegister : public HWRegister { std::shared_ptr handle; size_t offset; public: MSRRegister(const std::shared_ptr & handle_, size_t offset_) : handle(handle_), offset(offset_) { } void operator = (uint64 val) override { handle->write(offset, val); } operator uint64 () override { uint64 value = 0; handle->read(offset, &value); // std::cout << "reading MSR " << offset << " returning " << value << std::endl; return value; } }; class CounterWidthExtenderRegister : public HWRegister { std::shared_ptr handle; public: CounterWidthExtenderRegister(const std::shared_ptr & handle_) : handle(handle_) { } void operator = (uint64 val) override { if (val == 0) { handle->reset(); } else { std::cerr << "ERROR: writing non-zero values to CounterWidthExtenderRegister is not supported\n"; throw std::exception(); } } operator uint64 () override { return handle->read();; } }; class UncorePMU { typedef std::shared_ptr HWRegisterPtr; uint32 cpu_model_; uint32 getCPUModel(); HWRegisterPtr unitControl; public: std::vector counterControl; std::vector counterValue; HWRegisterPtr fixedCounterControl; HWRegisterPtr fixedCounterValue; HWRegisterPtr filter[2]; UncorePMU(const HWRegisterPtr& unitControl_, const HWRegisterPtr& counterControl0, const HWRegisterPtr& counterControl1, const HWRegisterPtr& counterControl2, const HWRegisterPtr& counterControl3, const HWRegisterPtr& counterValue0, const HWRegisterPtr& counterValue1, const HWRegisterPtr& counterValue2, const HWRegisterPtr& counterValue3, const HWRegisterPtr& fixedCounterControl_ = HWRegisterPtr(), const HWRegisterPtr& fixedCounterValue_ = HWRegisterPtr(), const HWRegisterPtr& filter0 = HWRegisterPtr(), const HWRegisterPtr& filter1 = HWRegisterPtr() ); UncorePMU(const HWRegisterPtr& unitControl_, const std::vector & counterControl_, const std::vector & counterValue_, const HWRegisterPtr& fixedCounterControl_ = HWRegisterPtr(), const HWRegisterPtr& fixedCounterValue_ = HWRegisterPtr(), const HWRegisterPtr& filter0 = HWRegisterPtr(), const HWRegisterPtr& filter1 = HWRegisterPtr() ); UncorePMU() : cpu_model_(0U) {} size_t size() const { return counterControl.size(); } virtual ~UncorePMU() {} bool valid() const { return unitControl.get() != nullptr; } void cleanup(); void freeze(const uint32 extra); bool initFreeze(const uint32 extra, const char* xPICheckMsg = nullptr); void unfreeze(const uint32 extra); void resetUnfreeze(const uint32 extra); }; class IDX_PMU { typedef std::shared_ptr HWRegisterPtr; uint32 cpu_model_; uint32 getCPUModel(); bool perf_mode_; uint32 numa_node_; uint32 socket_id_; HWRegisterPtr resetControl; HWRegisterPtr freezeControl; public: HWRegisterPtr generalControl; std::vector counterControl; std::vector counterValue; std::vector counterFilterWQ; std::vector counterFilterENG; std::vector counterFilterTC; std::vector counterFilterPGSZ; std::vector counterFilterXFERSZ; IDX_PMU(const bool perfMode_, const uint32 numaNode_, const uint32 socketId_, const HWRegisterPtr& resetControl_, const HWRegisterPtr& freezeControl_, const HWRegisterPtr& generalControl_, const std::vector & counterControl, const std::vector & counterValue, const std::vector & counterFilterWQ, const std::vector & counterFilterENG, const std::vector & counterFilterTC, const std::vector & counterFilterPGSZ, const std::vector & counterFilterXFERSZ ); IDX_PMU() : cpu_model_(0U), perf_mode_(false), numa_node_(0), socket_id_(0) {} size_t size() const { return counterControl.size(); } virtual ~IDX_PMU() {} bool valid() const { return resetControl.get() != nullptr; } void cleanup(); void freeze(); bool initFreeze(); void unfreeze(); void resetUnfreeze(); bool getPERFMode(); uint32 getNumaNode() const; uint32 getSocketId() const; }; enum ServerUncoreMemoryMetrics { PartialWrites, Pmem, PmemMemoryMode, PmemMixedMode }; //! Object to access uncore counters in a socket/processor with microarchitecture codename SandyBridge-EP (Jaketown) or Ivytown-EP or Ivytown-EX class ServerUncorePMUs { friend class PCM; int32 iMCbus,UPIbus,M2Mbus; uint32 groupnr; int32 cpu_model; typedef std::vector UncorePMUVector; UncorePMUVector imcPMUs; UncorePMUVector edcPMUs; UncorePMUVector xpiPMUs; UncorePMUVector m3upiPMUs; UncorePMUVector m2mPMUs; UncorePMUVector haPMUs; UncorePMUVector hbm_m2mPMUs; std::vector allPMUs{ &imcPMUs, &edcPMUs, &xpiPMUs, &m3upiPMUs , &m2mPMUs, &haPMUs, &hbm_m2mPMUs }; std::vector qpi_speed; std::vector num_imc_channels; // number of memory channels in each memory controller std::vector > XPIRegisterLocation; // (device, function) std::vector > M3UPIRegisterLocation; // (device, function) std::vector > > MCRegisterLocation; // MCRegisterLocation[controller]: (device, function) std::vector > EDCRegisterLocation; // EDCRegisterLocation: (device, function) std::vector > M2MRegisterLocation; // M2MRegisterLocation: (device, function) std::vector > HARegisterLocation; // HARegisterLocation: (device, function) std::vector > HBM_M2MRegisterLocation; // HBM_M2MRegisterLocation: (device, function) static std::vector > socket2iMCbus; static std::vector > socket2UPIbus; static std::vector > socket2M2Mbus; ServerUncorePMUs(); // forbidden ServerUncorePMUs(ServerUncorePMUs &); // forbidden ServerUncorePMUs & operator = (const ServerUncorePMUs &); // forbidden static PciHandleType * createIntelPerfMonDevice(uint32 groupnr, int32 bus, uint32 dev, uint32 func, bool checkVendor = false); void programIMC(const uint32 * MCCntConfig); void programEDC(const uint32 * EDCCntConfig); void programM2M(const uint64 * M2MCntConfig); void programM2M(); void programHA(const uint32 * config); void programHA(); void programXPI(const uint32 * XPICntConfig); void programM3UPI(const uint32* M3UPICntConfig); typedef std::pair > MemTestParam; void initMemTest(MemTestParam & param); void doMemTest(const MemTestParam & param); void cleanupMemTest(const MemTestParam & param); void cleanupQPIHandles(); void cleanupPMUs(); void initDirect(uint32 socket_, const PCM * pcm); void initPerf(uint32 socket_, const PCM * pcm); void initBuses(uint32 socket_, const PCM * pcm); void initRegisterLocations(const PCM * pcm); uint64 getPMUCounter(std::vector & pmu, const uint32 id, const uint32 counter); bool HBMAvailable() const; public: enum EventPosition { READ=0, WRITE=1, READ_RANK_A=0, WRITE_RANK_A=1, READ_RANK_B=2, WRITE_RANK_B=3, PARTIAL=2, PMM_READ=2, PMM_WRITE=3, PMM_MM_MISS_CLEAN=2, PMM_MM_MISS_DIRTY=3, NM_HIT=0, // NM : Near Memory (DRAM cache) in Memory Mode M2M_CLOCKTICKS=1 }; //! \brief Initialize access data structures //! \param socket_ socket id //! \param pcm pointer to PCM instance ServerUncorePMUs(uint32 socket_, const PCM * pcm); //! \brief Program performance counters (disables programming power counters) void program(); //! \brief Get the number of integrated controller reads (in cache lines) uint64 getImcReads(); //! \brief Get the number of integrated controller reads for given controller (in cache lines) //! \param controller controller ID/number uint64 getImcReadsForController(uint32 controller); //! \brief Get the number of integrated controller reads for given channels (in cache lines) //! \param beginChannel first channel in the range //! \param endChannel last channel + 1: the range is [beginChannel, endChannel). endChannel is not included. uint64 getImcReadsForChannels(uint32 beginChannel, uint32 endChannel); //! \brief Get the number of integrated controller writes (in cache lines) uint64 getImcWrites(); //! \brief Get the number of requests to home agent (BDX/HSX only) uint64 getHALocalRequests(); //! \brief Get the number of local requests to home agent (BDX/HSX only) uint64 getHARequests(); //! \brief Get the number of PMM memory reads (in cache lines) uint64 getPMMReads(); //! \brief Get the number of PMM memory writes (in cache lines) uint64 getPMMWrites(); //! \brief Get the number of cache lines read by EDC (embedded DRAM controller) uint64 getEdcReads(); //! \brief Get the number of cache lines written by EDC (embedded DRAM controller) uint64 getEdcWrites(); //! \brief Get the number of incoming data flits to the socket through a port //! \param port QPI port id uint64 getIncomingDataFlits(uint32 port); //! \brief Get the number of outgoing data and non-data or idle flits (depending on the architecture) from the socket through a port //! \param port QPI port id uint64 getOutgoingFlits(uint32 port); ~ServerUncorePMUs(); //! \brief Program power counters (disables programming performance counters) //! \param mc_profile memory controller measurement profile. See description of profiles in pcm-power.cpp void program_power_metrics(int mc_profile); //! \brief Program memory counters (disables programming performance counters) //! \param rankA count DIMM rank1 statistics (disables memory channel monitoring) //! \param rankB count DIMM rank2 statistics (disables memory channel monitoring) //! \brief metrics metric set (see the ServerUncoreMemoryMetrics enum) void programServerUncoreMemoryMetrics(const ServerUncoreMemoryMetrics & metrics, const int rankA = -1, const int rankB = -1); //! \brief Get number of QPI LL clocks on a QPI port //! \param port QPI port number uint64 getQPIClocks(uint32 port); //! \brief Get number cycles on a QPI port when the link was in a power saving half-lane mode //! \param port QPI port number uint64 getQPIL0pTxCycles(uint32 port); //! \brief Get number cycles on a UPI port when the link was in a L0 mode (fully active) //! \param port UPI port number uint64 getUPIL0TxCycles(uint32 port); //! \brief Get number cycles on a QPI port when the link was in a power saving shutdown mode //! \param port QPI port number uint64 getQPIL1Cycles(uint32 port); //! \brief Get number DRAM channel cycles //! \param channel channel number uint64 getDRAMClocks(uint32 channel); //! \brief Get number HBM channel cycles //! \param channel channel number uint64 getHBMClocks(uint32 channel); //! \brief Direct read of memory controller PMU counter (counter meaning depends on the programming: power/performance/etc) //! \param channel channel number //! \param counter counter number uint64 getMCCounter(uint32 channel, uint32 counter); //! \brief Direct read of embedded DRAM memory controller PMU counter (counter meaning depends on the programming: power/performance/etc) //! \param channel channel number //! \param counter counter number uint64 getEDCCounter(uint32 channel, uint32 counter); //! \brief Direct read of QPI LL PMU counter (counter meaning depends on the programming: power/performance/etc) //! \param port port number //! \param counter counter number uint64 getQPILLCounter(uint32 port, uint32 counter); //! \brief Direct read of M3UPI PMU counter (counter meaning depends on the programming: power/performance/etc) //! \param port port number //! \param counter counter number uint64 getM3UPICounter(uint32 port, uint32 counter); //! \brief Direct read of M2M counter //! \param box box ID/number //! \param counter counter number uint64 getM2MCounter(uint32 box, uint32 counter); //! \brief Freezes event counting void freezeCounters(); //! \brief Unfreezes event counting void unfreezeCounters(); //! \brief Measures/computes the maximum theoretical QPI link bandwidth speed in GByte/seconds uint64 computeQPISpeed(const uint32 ref_core, const int cpumodel); //! \brief Enable correct counting of various LLC events (with memory access perf penalty) void enableJKTWorkaround(bool enable); //! \brief Returns the number of detected QPI ports size_t getNumQPIPorts() const { return xpiPMUs.size(); } //! \brief Returns the speed of the QPI link uint64 getQPILinkSpeed(const uint32 linkNr) const { return qpi_speed.empty() ? 0 : qpi_speed[linkNr]; } //! \brief Print QPI Speeds void reportQPISpeed() const; //! \brief Returns the number of detected integrated memory controllers uint32 getNumMC() const { return (uint32)num_imc_channels.size(); } //! \brief Returns the total number of detected memory channels on all integrated memory controllers size_t getNumMCChannels() const { return (size_t)imcPMUs.size(); } //! \brief Returns the total number of detected memory channels on given integrated memory controller //! \param controller controller number size_t getNumMCChannels(const uint32 controller) const; //! \brief Returns the total number of detected memory channels on all embedded DRAM controllers (EDC) size_t getNumEDCChannels() const { return edcPMUs.size(); } }; class SimpleCounterState { template friend uint64 getNumberOfEvents(const T & before, const T & after); friend class PCM; uint64 data; public: SimpleCounterState() : data(0) { } uint64 getRawData() const {return data;} virtual ~SimpleCounterState() { } }; typedef SimpleCounterState PCIeCounterState; typedef SimpleCounterState IIOCounterState; typedef SimpleCounterState IDXCounterState; typedef std::vector eventGroup_t; class PerfVirtualControlRegister; /*! \brief CPU Performance Monitor This singleton object needs to be instantiated for each process before accessing counting and measuring routines */ class PCM_API PCM { friend class BasicCounterState; friend class UncoreCounterState; friend class Socket; friend class ServerUncore; friend class PerfVirtualControlRegister; friend class Aggregator; friend class ServerUncorePMUs; PCM(); // forbidden to call directly because it is a singleton PCM(const PCM &) = delete; PCM & operator = (const PCM &) = delete; int32 cpu_family; int32 cpu_model; bool hybrid = false; int32 cpu_stepping; int64 cpu_microcode_level; uint32 max_cpuid; int32 threads_per_core; int32 num_cores; int32 num_sockets; int32 num_phys_cores_per_socket; int32 num_online_cores; int32 num_online_sockets; uint32 core_gen_counter_num_max; uint32 core_gen_counter_num_used; uint32 core_gen_counter_width; uint32 core_fixed_counter_num_max; uint32 core_fixed_counter_num_used; uint32 core_fixed_counter_width; uint64 core_global_ctrl_value{0ULL}; uint32 uncore_gen_counter_num_max; uint32 uncore_gen_counter_num_used; uint32 uncore_gen_counter_width; uint32 uncore_fixed_counter_num_max; uint32 uncore_fixed_counter_num_used; uint32 uncore_fixed_counter_width; uint32 perfmon_version; int32 perfmon_config_anythread; uint64 nominal_frequency; uint64 max_qpi_speed; // in GBytes/second uint32 L3ScalingFactor; int32 pkgThermalSpecPower, pkgMinimumPower, pkgMaximumPower; std::vector topology; SystemRoot* systemTopology; std::string errorMessage; static PCM * instance; bool programmed_core_pmu{false}; std::vector > MSR; std::vector > serverUncorePMUs; std::vector pcuPMUs; std::vector > iioPMUs; std::vector > irpPMUs; std::vector uboxPMUs; std::vector > idxPMUs; double joulesPerEnergyUnit; std::vector > energy_status; std::vector > dram_energy_status; std::vector > cboPMUs; std::vector > mdfPMUs; std::vector>> cxlPMUs; // socket X CXL ports X UNIT {0,1} std::vector > memory_bw_local; std::vector > memory_bw_total; #ifdef __linux__ Resctrl resctrl; #endif bool useResctrl; std::shared_ptr clientBW; std::shared_ptr clientImcReads; std::shared_ptr clientImcWrites; std::shared_ptr clientGtRequests; std::shared_ptr clientIaRequests; std::shared_ptr clientIoRequests; std::vector > serverBW; std::shared_ptr uncorePMUDiscovery; bool disable_JKT_workaround; bool blocked; // track if time-driven counter update is running or not: PCM is blocked uint64 * coreCStateMsr; // MSR addresses of core C-state free-running counters uint64 * pkgCStateMsr; // MSR addresses of package C-state free-running counters std::vector > coreTaskQueues; bool L2CacheHitRatioAvailable; bool L3CacheHitRatioAvailable; bool L3CacheMissesAvailable; bool L2CacheMissesAvailable; bool L2CacheHitsAvailable; bool L3CacheHitsNoSnoopAvailable; bool L3CacheHitsSnoopAvailable; bool L3CacheHitsAvailable; bool forceRTMAbortMode; std::vector FrontendBoundSlots, BadSpeculationSlots, BackendBoundSlots, RetiringSlots, AllSlotsRaw; bool isFixedCounterSupported(unsigned c); bool vm = false; bool linux_arch_perfmon = false; public: enum { MAX_C_STATE = 10 }; // max C-state on Intel architecture //! \brief Returns true if the specified core C-state residency metric is supported bool isCoreCStateResidencySupported(int state) { if (state == 0 || state == 1) return true; return (coreCStateMsr != NULL && state <= ((int)MAX_C_STATE) && coreCStateMsr[state] != 0); } //! \brief Returns true if the specified package C-state residency metric is supported bool isPackageCStateResidencySupported(int state) { if (state == 0) { return true; } return (pkgCStateMsr != NULL && state <= ((int)MAX_C_STATE) && pkgCStateMsr[state] != 0); } //! \brief Redirects output destination to provided file, instead of std::cout and std::cerr (optional) static void setOutput(const std::string filename, const bool cerrToo = false); //! \brief Restores output, closes output file if opened void restoreOutput(); //! \brief Set Run State. // Arguments: // -- 1 - program is running // -- 0 -pgram is sleeping void setRunState(int new_state) { run_state = new_state; } //! \brief Returns program's Run State. // Results: // -- 1 - program is running // -- 0 -pgram is sleeping int getRunState(void) { return run_state; } bool isBlocked(void) { return blocked; } void setBlocked(const bool new_blocked) { blocked = new_blocked; } //! Mode of programming (parameter in the program() method) enum ProgramMode { DEFAULT_EVENTS = 0, /*!< Default choice of events, the additional parameter is not needed and ignored */ CUSTOM_CORE_EVENTS = 1, /*!< Custom set of core events specified in the parameter to the program method. The parameter must be a pointer to array of four \c CustomCoreEventDescription values */ EXT_CUSTOM_CORE_EVENTS = 2, /*!< Custom set of core events specified in the parameter to the program method. The parameter must be a pointer to a \c ExtendedCustomCoreEventDescription data structure */ INVALID_MODE /*!< Non-programmed mode */ }; //! Return codes (e.g. for program(..) method) enum ErrorCode { Success = 0, MSRAccessDenied = 1, PMUBusy = 2, UnknownError }; enum PerfmonField { INVALID, /* Use to parse invalid field */ OPCODE, EVENT_SELECT, UMASK, RESET, EDGE_DET, IGNORED, OVERFLOW_ENABLE, ENABLE, INVERT, THRESH, CH_MASK, FC_MASK, /* Below are not part of perfmon definition */ H_EVENT_NAME, V_EVENT_NAME, MULTIPLIER, DIVIDER, COUNTER_INDEX }; enum PCIeWidthMode { X1, X4, X8, X16, XFF }; enum { // offsets/enumeration of IIO stacks IIO_CBDMA = 0, // shared with DMI IIO_PCIe0 = 1, IIO_PCIe1 = 2, IIO_PCIe2 = 3, IIO_MCP0 = 4, IIO_MCP1 = 5 }; // Offsets/enumeration of IIO stacks Skylake server. enum SkylakeIIOStacks { SKX_IIO_CBDMA_DMI = 0, SKX_IIO_PCIe0 = 1, SKX_IIO_PCIe1 = 2, SKX_IIO_PCIe2 = 3, SKX_IIO_MCP0 = 4, SKX_IIO_MCP1 = 5, SKX_IIO_STACK_COUNT = 6 }; // Offsets/enumeration of IIO stacks for IceLake server. enum IcelakeIIOStacks { ICX_IIO_PCIe0 = 0, ICX_IIO_PCIe1 = 1, ICX_IIO_MCP0 = 2, ICX_IIO_PCIe2 = 3, ICX_IIO_PCIe3 = 4, ICX_IIO_CBDMA_DMI = 5, ICX_IIO_STACK_COUNT = 6 }; // Offsets/enumeration of IIO stacks for IceLake server. enum SnowridgeIIOStacks { SNR_IIO_QAT = 0, SNR_IIO_CBDMA_DMI = 1, SNR_IIO_NIS = 2, SNR_IIO_HQM = 3, SNR_IIO_PCIe0 = 4, SNR_IIO_STACK_COUNT = 5 }; enum IDX_IP { IDX_IAA = 0, IDX_DSA, IDX_QAT, IDX_MAX }; enum IDX_OPERATION { QAT_TLM_STOP = 0, QAT_TLM_START, QAT_TLM_REFRESH, QAT_TLM_MAX }; enum IDX_STATE { IDX_STATE_OFF = 0, IDX_STATE_ON, }; struct SimplePCIeDevInfo { enum PCIeWidthMode width; std::string pciDevName; std::string busNumber; SimplePCIeDevInfo() : width(XFF) { } }; /*! \brief Custom Core event description See "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: System Programming Guide, Part 2" for the concrete values of the data structure fields, e.g. Appendix A.2 "Performance Monitoring Events for Intel(r) Core(tm) Processor Family and Xeon Processor Family" */ struct CustomCoreEventDescription { int32 event_number = 0, umask_value = 0; }; /*! \brief Extended custom core event description In contrast to CustomCoreEventDescription supports configuration of all fields. See "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: System Programming Guide, Part 2" for the concrete values of the data structure fields, e.g. Appendix A.2 "Performance Monitoring Events for Intel(r) Core(tm) Processor Family and Xeon Processor Family" */ struct ExtendedCustomCoreEventDescription { FixedEventControlRegister * fixedCfg; // if NULL, then default configuration performed for fixed counters uint32 nGPCounters; // number of general purpose counters EventSelectRegister * gpCounterCfg; // general purpose counters, if NULL, then default configuration performed for GP counters EventSelectRegister * gpCounterHybridAtomCfg; // general purpose counters for Atom cores in hybrid processors uint64 OffcoreResponseMsrValue[2]; uint64 LoadLatencyMsrValue, FrontendMsrValue; bool defaultUncoreProgramming{true}; static uint64 invalidMsrValue() { return ~0ULL; } ExtendedCustomCoreEventDescription() : fixedCfg(NULL), nGPCounters(0), gpCounterCfg(nullptr), gpCounterHybridAtomCfg(nullptr), LoadLatencyMsrValue(invalidMsrValue()), FrontendMsrValue(invalidMsrValue()) { OffcoreResponseMsrValue[0] = 0; OffcoreResponseMsrValue[1] = 0; } }; struct CustomIIOEventDescription { /* We program the same counters to every IIO Stacks */ std::string eventNames[4]; IIOPMUCNTCTLRegister eventOpcodes[4]; int multiplier[4]; //Some IIO event requires transformation to get meaningful output (i.e. DWord to bytes) int divider[4]; //We usually like to have some kind of divider (i.e. /10e6 ) }; struct MSREventPosition { enum constants { index = 0, type = 1 }; }; enum MSRType { Static = 0, Freerun = 1 }; private: ProgramMode mode; CustomCoreEventDescription coreEventDesc[PERF_MAX_CUSTOM_COUNTERS]; CustomCoreEventDescription hybridAtomEventDesc[PERF_MAX_CUSTOM_COUNTERS]; std::vector socketRefCore; bool canUsePerf; #ifdef PCM_USE_PERF typedef std::vector > PerfEventHandleContainer; PerfEventHandleContainer perfEventHandle; std::vector perfEventTaskHandle; void readPerfData(uint32 core, std::vector & data); void closePerfHandles(const bool silent = false); enum { PERF_INST_RETIRED_POS = 0, PERF_CPU_CLK_UNHALTED_THREAD_POS = 1, PERF_CPU_CLK_UNHALTED_REF_POS = 2, PERF_GEN_EVENT_0_POS = 3, PERF_GEN_EVENT_1_POS = 4, PERF_GEN_EVENT_2_POS = 5, PERF_GEN_EVENT_3_POS = 6, PERF_TOPDOWN_SLOTS_POS = PERF_GEN_EVENT_0_POS + PERF_MAX_CUSTOM_COUNTERS, PERF_TOPDOWN_FRONTEND_POS = PERF_TOPDOWN_SLOTS_POS + 1, PERF_TOPDOWN_BADSPEC_POS = PERF_TOPDOWN_SLOTS_POS + 2, PERF_TOPDOWN_BACKEND_POS = PERF_TOPDOWN_SLOTS_POS + 3, PERF_TOPDOWN_RETIRING_POS = PERF_TOPDOWN_SLOTS_POS + 4 }; std::array perfTopDownPos; enum { PERF_GROUP_LEADER_COUNTER = PERF_INST_RETIRED_POS, PERF_TOPDOWN_GROUP_LEADER_COUNTER = PERF_TOPDOWN_SLOTS_POS }; #endif static std::ofstream * outfile; // output file stream static std::streambuf * backup_ofile; // backup of original output = cout static std::streambuf * backup_ofile_cerr; // backup of original output = cerr int run_state; // either running (1) or sleeping (0) bool needToRestoreNMIWatchdog; bool cleanupPEBS{false}; std::vector > lastProgrammedCustomCounters; uint32 checkCustomCoreProgramming(std::shared_ptr msr); ErrorCode programCoreCounters(int core, const PCM::ProgramMode mode, const ExtendedCustomCoreEventDescription * pExtDesc, std::vector & programmedCustomCounters, const std::vector & tids); bool PMUinUse(); void cleanupPMU(const bool silent = false); void cleanupRDT(const bool silent = false); void computeQPISpeedBeckton(int core_nr); void destroyMSR(); void computeNominalFrequency(); static bool isCPUModelSupported(const int model_); std::string getSupportedUarchCodenames() const; std::string getUnsupportedMessage() const; bool detectModel(); bool checkModel(); void initCStateSupportTables(); bool discoverSystemTopology(); void printSystemTopology() const; bool initMSR(); bool detectNominalFrequency(); void showSpecControlMSRs(); void initEnergyMonitoring(); void initUncoreObjects(); /*! * \brief initializes each core with an RMID * * \returns nothing */ void initRDT(); /*! * \brief Initializes RDT * * Initializes RDT infrastructure through resctrl Linux driver or direct MSR programming. * For the latter: initializes each core event MSR with an RMID for QOS event (L3 cache monitoring or memory bandwidth monitoring) * \returns nothing */ void initQOSevent(const uint64 event, const int32 core); void programBecktonUncore(int core); void programNehalemEPUncore(int core); void enableJKTWorkaround(bool enable); template void readAndAggregateMemoryBWCounters(const uint32 core, CounterStateType & counterState); template void readAndAggregateUncoreMCCounters(const uint32 socket, CounterStateType & counterState); template void readAndAggregateEnergyCounters(const uint32 socket, CounterStateType & counterState); template void readPackageThermalHeadroom(const uint32 socket, CounterStateType & counterState); template void readAndAggregatePackageCStateResidencies(std::shared_ptr msr, CounterStateType & result); public: struct RawPMUConfig; void programCXLCM(); template void readAndAggregateCXLCMCounters(CounterStateType & counterState); private: template void readMSRs(std::shared_ptr msr, const RawPMUConfig & msrConfig, CounterStateType & result); void readQPICounters(SystemCounterState & counterState); void readPCICFGRegisters(SystemCounterState& result); void reportQPISpeed() const; void readCoreCounterConfig(const bool complainAboutMSR = false); void readCPUMicrocodeLevel(); void globalFreezeUncoreCountersInternal(const unsigned long long int freeze); uint64 CX_MSR_PMON_CTRY(uint32 Cbo, uint32 Ctr) const; uint64 CX_MSR_PMON_BOX_FILTER(uint32 Cbo) const; uint64 CX_MSR_PMON_BOX_FILTER1(uint32 Cbo) const; uint64 CX_MSR_PMON_CTLY(uint32 Cbo, uint32 Ctl) const; uint64 CX_MSR_PMON_BOX_CTL(uint32 Cbo) const; void programCboOpcodeFilter(const uint32 opc0, UncorePMU & pmu, const uint32 nc_, const uint32 opc1, const uint32 loc, const uint32 rem); void initLLCReadMissLatencyEvents(uint64 * events, uint32 & opCode); void initCHARequestEvents(uint64 * events); void programCbo(); uint64 getCBOCounterState(const uint32 socket, const uint32 ctr_); template static void program(UncorePMU& pmu, const Iterator& eventsBegin, const Iterator& eventsEnd, const uint32 extra) { if (!eventsBegin) return; Iterator curEvent = eventsBegin; const auto cpu_model = PCM::getInstance()->getCPUModel(); for (int c = 0; curEvent != eventsEnd && size_t(c) < pmu.size(); ++c, ++curEvent) { auto ctrl = pmu.counterControl[c]; if (ctrl.get() != nullptr) { if (PCM::SPR == cpu_model) { *ctrl = *curEvent; } else { *ctrl = MC_CH_PCI_PMON_CTL_EN; *ctrl = MC_CH_PCI_PMON_CTL_EN | *curEvent; } } } if (extra) { pmu.resetUnfreeze(extra); } } void programPCU(uint32 * events, const uint64 filter); void programUBOX(const uint64* events); void programCXLDP(const uint64* events); void programCXLCM(const uint64* events); void cleanupUncorePMUs(const bool silent = false); bool isCLX() const // Cascade Lake-SP { return (PCM::SKX == cpu_model) && (cpu_stepping > 4 && cpu_stepping < 8); } static bool isCPX(int cpu_model_, int cpu_stepping_) // Cooper Lake { return (PCM::SKX == cpu_model_) && (cpu_stepping_ >= 10); } bool isCPX() const { return isCPX(cpu_model, cpu_stepping); } void initUncorePMUsDirect(); void initUncorePMUsPerf(); bool isRDTDisabled() const; public: static bool isInitialized() { return instance != nullptr; } //! check if TMA level 1 metrics are supported bool isHWTMAL1Supported() const; enum EventPosition { TOR_OCCUPANCY = 0, TOR_INSERTS = 1, REQUESTS_ALL = 2, REQUESTS_LOCAL = 3, CXL_TxC_MEM = 0, // works only on counters 0-3 CXL_TxC_CACHE = 1 // works only on counters 0-3 }; //! check if in secure boot mode bool isSecureBoot() const; //! true if Linux perf for uncore PMU programming should AND can be used internally bool useLinuxPerfForUncore() const; //! true if the CPU is hybrid bool isHybrid() const { return hybrid; } /*! \brief The system, sockets, uncores, cores and threads are structured like a tree \returns a reference to a const System object representing the root of the tree */ SystemRoot const & getSystemTopology() const { return *systemTopology; } //! prints detailed system topology void printDetailedSystemTopology(); /*! \brief checks if QOS monitoring support present \returns true or false */ bool QOSMetricAvailable() const; /*! \brief checks L3 cache support for QOS present \returns true or false */ bool L3QOSMetricAvailable() const; /*! \brief checks if L3 cache monitoring present \returns true or false */ bool L3CacheOccupancyMetricAvailable() const; /*! \brief checks if local memory bandwidth monitoring present \returns true or false */ bool CoreLocalMemoryBWMetricAvailable() const; /*! \brief checks if total memory bandwidth monitoring present \returns true or false */ bool CoreRemoteMemoryBWMetricAvailable() const; /*! * \brief returns the max number of RMID supported by socket * * \returns maximum number of RMID supported by socket */ unsigned getMaxRMID() const; //! \brief Returns the number of CBO or CHA units per socket uint32 getMaxNumOfCBoxes() const; //! \brief Returns the number of IIO stacks per socket uint32 getMaxNumOfIIOStacks() const; //! \brief Returns the number of MDFs boxes per socket uint32 getMaxNumOfMDFs() const; /*! \brief Returns the number of IDX accel devs \param accel index of IDX accel */ uint32 getNumOfIDXAccelDevs(int accel) const; //! \brief Returns the number of IDX counters uint32 getMaxNumOfIDXAccelCtrs(int accel) const; //! \brief Returns the numa node of IDX accel dev uint32 getNumaNodeOfIDXAccelDev(uint32 accel, uint32 dev) const; //! \brief Returns the socketid of IDX accel dev uint32 getCPUSocketIdOfIDXAccelDev(uint32 accel, uint32 dev) const; //! \brief Returns the platform support IDX accel dev or NOT bool supportIDXAccelDev() const; /*! \brief Returns PCM object Returns PCM object. If the PCM has not been created before than an instance is created. PCM is a singleton. \return Pointer to PCM object */ static PCM * getInstance(); // the only way to get access /*! \brief Checks the status of PCM object Call this method to check if PCM gained access to model specific registers. The method is deprecated, see program error code instead. \return true iff access to model specific registers works without problems */ bool good(); // true if access to CPU counters works /*! \brief Returns the error message Call this when good() returns false, otherwise return an empty string */ const std::string & getErrorMessage() const { return errorMessage; } /*! \brief Programs performance counters \param mode_ mode of programming, see ProgramMode definition \param parameter_ optional parameter for some of programming modes \param silent set to true to silence diagnostic messages \param pid restrict core metrics only to specified pid (process id) Call this method before you start using the performance counting routines. \warning Using this routines with other tools that *program* Performance Monitoring Units (PMUs) on CPUs is not recommended because PMU can not be shared. Tools that are known to program PMUs: Intel(r) VTune(tm), Intel(r) Performance Tuning Utility (PTU). This code may make VTune or PTU measurements invalid. VTune or PTU measurement may make measurement with this code invalid. Please enable either usage of these routines or VTune/PTU/etc. */ ErrorCode program(const ProgramMode mode_ = DEFAULT_EVENTS, const void * parameter_ = NULL, const bool silent = false, const int pid = -1); // program counters and start counting /*! \brief checks the error and suggests solution and/or exits the process \param code error code from the 'program' call */ void checkError(const ErrorCode code); /*! \brief Programs uncore latency counters on microarchitectures codename SandyBridge-EP and later Xeon uarch \param enable_pmm enables DDR/PMM. See possible profile values in pcm-latency.cpp example Call this method before you start using the latency counter routines on microarchitecture codename SandyBridge-EP and later Xeon uarch \warning After this call the memory and QPI bandwidth counters on microarchitecture codename SandyBridge-EP and later Xeon uarch will not work. \warning Using this routines with other tools that *program* Performance Monitoring Units (PMUs) on CPUs is not recommended because PMU can not be shared. Tools that are known to program PMUs: Intel(r) VTune(tm), Intel(r) Performance Tuning Utility (PTU). This code may make VTune or PTU measurements invalid. VTune or PTU measurement may make measurement with this code invalid. Please enable either usage of these routines or VTune/PTU/etc. */ ErrorCode programServerUncoreLatencyMetrics(bool enable_pmm); /*! \brief Programs uncore power/energy counters on microarchitectures codename SandyBridge-EP and later Xeon uarch \param mc_profile profile for integrated memory controller PMU. See possible profile values in pcm-power.cpp example \param pcu_profile profile for power control unit PMU. See possible profile values in pcm-power.cpp example \param freq_bands array of three integer values for core frequency band monitoring. See usage in pcm-power.cpp example Call this method before you start using the power counter routines on microarchitecture codename SandyBridge-EP and later Xeon uarch \warning After this call the memory and QPI bandwidth counters on microarchitecture codename SandyBridge-EP and later Xeon uarch will not work. \warning Using this routines with other tools that *program* Performance Monitoring Units (PMUs) on CPUs is not recommended because PMU can not be shared. Tools that are known to program PMUs: Intel(r) VTune(tm), Intel(r) Performance Tuning Utility (PTU). This code may make VTune or PTU measurements invalid. VTune or PTU measurement may make measurement with this code invalid. Please enable either usage of these routines or VTune/PTU/etc. */ ErrorCode programServerUncorePowerMetrics(int mc_profile, int pcu_profile, int * freq_bands = NULL); /* \brief Program memory counters (disables programming performance counters) \param rankA count DIMM rank1 statistics (disables memory channel monitoring) \param rankB count DIMM rank2 statistics (disables memory channel monitoring) \brief metrics metric set (see the ServerUncoreMemoryMetrics enum) Call this method before you start using the memory counter routines on microarchitecture codename SandyBridge-EP and later Xeon uarch \warning Using this routines with other tools that *program* Performance Monitoring Units (PMUs) on CPUs is not recommended because PMU can not be shared. Tools that are known to program PMUs: Intel(r) VTune(tm), Intel(r) Performance Tuning Utility (PTU). This code may make VTune or PTU measurements invalid. VTune or PTU measurement may make measurement with this code invalid. Please enable either usage of these routines or VTune/PTU/etc. */ ErrorCode programServerUncoreMemoryMetrics(const ServerUncoreMemoryMetrics & metrics, int rankA = -1, int rankB = -1); // vector of IDs. E.g. for core {raw event} or {raw event, offcore response1 msr value, } or {raw event, offcore response1 msr value, offcore response2} // or for cha/cbo {raw event, filter value}, etc // + user-supplied name typedef std::array RawEventEncoding; typedef std::pair RawEventConfig; struct RawPMUConfig { std::vector programmable; std::vector fixed; }; enum { OCR0Pos = 1, OCR1Pos = 2, LoadLatencyPos = 3, FrontendPos = 4 }; typedef std::map RawPMUConfigs; ErrorCode program(const RawPMUConfigs& curPMUConfigs, const bool silent = false, const int pid = -1); struct PCICFGEventPosition { enum constants { deviceID = 0, offset = 1, type = 2, width = 5 }; }; typedef std::pair, uint32> PCICFGRegisterEncoding; // PciHandleType shared ptr, offset struct PCICFGRegisterEncodingHash { std::size_t operator()(const RawEventEncoding & e) const { std::size_t h1 = std::hash{}(e[PCICFGEventPosition::deviceID]); std::size_t h2 = std::hash{}(e[PCICFGEventPosition::offset]); std::size_t h3 = std::hash{}(e[PCICFGEventPosition::width]); return h1 ^ (h2 << 1ULL) ^ (h3 << 2ULL); } }; struct PCICFGRegisterEncodingCmp { bool operator ()(const RawEventEncoding& a, const RawEventEncoding& b) const { return a[PCICFGEventPosition::deviceID] == b[PCICFGEventPosition::deviceID] && a[PCICFGEventPosition::offset] == b[PCICFGEventPosition::offset] && a[PCICFGEventPosition::width] == b[PCICFGEventPosition::width]; } }; private: std::unordered_map, PCICFGRegisterEncodingHash, PCICFGRegisterEncodingCmp> PCICFGRegisterLocations{}; public: TopologyEntry::CoreType getCoreType(const unsigned coreID) const { assert(coreID < topology.size()); return topology[coreID].core_type; } std::pair getOCREventNr(const int event, const unsigned coreID) const { assert (coreID < topology.size()); if (hybrid) { switch (cpu_model) { case ADL: case RPL: if (topology[coreID].core_type == TopologyEntry::Atom) { return std::make_pair(OFFCORE_RESPONSE_0_EVTNR, event + 1); } break; } } bool useGLCOCREvent = false; switch (cpu_model) { case SPR: case ADL: // ADL big core (GLC) case RPL: useGLCOCREvent = true; break; } switch (event) { case 0: return std::make_pair(useGLCOCREvent ? GLC_OFFCORE_RESPONSE_0_EVTNR : OFFCORE_RESPONSE_0_EVTNR, OFFCORE_RESPONSE_0_UMASK); case 1: return std::make_pair(useGLCOCREvent ? GLC_OFFCORE_RESPONSE_1_EVTNR : OFFCORE_RESPONSE_1_EVTNR, OFFCORE_RESPONSE_1_UMASK); } assert (false && "wrong event nr in getOCREventNr"); return std::make_pair(0U, 0U); } //! \brief Freezes uncore event counting using global control MSR void globalFreezeUncoreCounters(); //! \brief Unfreezes uncore event counting using global control MSR void globalUnfreezeUncoreCounters(); //! \brief Freezes uncore event counting void freezeServerUncoreCounters(); //! \brief Unfreezes uncore event counting void unfreezeServerUncoreCounters(); /*! \brief Reads the power/energy counter state of a socket (works only on microarchitecture codename SandyBridge-EP) \param socket socket id \return State of power counters in the socket */ ServerUncoreCounterState getServerUncoreCounterState(uint32 socket); /*! \brief Cleanups resources and stops performance counting One needs to call this method when your program finishes or/and you are not going to use the performance counting routines anymore. */ void cleanup(const bool silent = false); /*! \brief Forces PMU reset If there is no chance to free up PMU from other applications you might try to call this method at your own risk. */ void resetPMU(); /*! \brief Reads all counter states (including system, sockets and cores) \param systemState system counter state (return parameter) \param socketStates socket counter states (return parameter) \param coreStates core counter states (return parameter) \param readAndAggregateSocketUncoreCounters read and aggregate socket uncore counters */ void getAllCounterStates(SystemCounterState & systemState, std::vector & socketStates, std::vector & coreStates, const bool readAndAggregateSocketUncoreCounters = true); /*! \brief Reads uncore counter states (including system and sockets) but no core counters \param systemState system counter state (return parameter) \param socketStates socket counter states (return parameter) */ void getUncoreCounterStates(SystemCounterState & systemState, std::vector & socketStates); /*! \brief Return true if the core in online \param os_core_id OS core id */ bool isCoreOnline(int32 os_core_id) const; /*! \brief Return true if the socket in online \param socket_id OS socket id */ bool isSocketOnline(int32 socket_id) const; /*! \brief Reads the counter state of the system System consists of several sockets (CPUs). Socket has a CPU in it. Socket (CPU) consists of several (logical) cores. \return State of counters in the entire system */ SystemCounterState getSystemCounterState(); /*! \brief Reads the counter state of a socket \param socket socket id \return State of counters in the socket */ SocketCounterState getSocketCounterState(uint32 socket); /*! \brief Reads the counter state of a (logical) core Be aware that during the measurement other threads may be scheduled on the same core by the operating system (this is called context-switching). The performance events caused by these threads will be counted as well. \param core core id \return State of counters in the core */ CoreCounterState getCoreCounterState(uint32 core); /*! \brief Reads number of logical cores in the system \return Number of logical cores in the system */ uint32 getNumCores() const; /*! \brief Reads number of online logical cores in the system \return Number of online logical cores in the system */ uint32 getNumOnlineCores() const; /*! \brief Reads number of sockets (CPUs) in the system \return Number of sockets in the system */ uint32 getNumSockets() const; /*! \brief Reads number of online sockets (CPUs) in the system \return Number of online sockets in the system */ uint32 getNumOnlineSockets() const; /*! \brief Reads how many hardware threads has a physical core "Hardware thread" is a logical core in a different terminology. If Intel(r) Hyperthreading(tm) is enabled then this function returns 2. \return Number of hardware threads per physical core */ uint32 getThreadsPerCore() const; /*! \brief Checks if SMT (HyperThreading) is enabled. \return true iff SMT (HyperThreading) is enabled. */ bool getSMT() const; // returns true iff SMT ("Hyperthreading") is on /*! \brief Reads the nominal core frequency \return Nominal frequency in Hz */ uint64 getNominalFrequency() const; // in Hz /*! \brief runs CPUID.0xF.0x01 to get the L3 up scaling factor to calculate L3 Occupancy * Scaling factor is returned in EBX register after running the CPU instruction * \return L3 up scaling factor */ uint32 getL3ScalingFactor() const; /*! \brief runs CPUID.0xB.0x01 to get maximum logical cores (including SMT) per socket. * max_lcores_per_socket is returned in EBX[15:0]. Compare this value with number of cores per socket * detected in the system to see if some cores are offlined * \return true iff max_lcores_per_socket == number of cores per socket detected */ bool isSomeCoreOfflined(); /*! \brief Returns the maximum number of custom (general-purpose) core events supported by CPU */ int32 getMaxCustomCoreEvents(); /*! \brief Returns cpu model id number from cpuid instruction */ static int getCPUModelFromCPUID(); //! \brief Identifiers of supported CPU models enum SupportedCPUModels { NEHALEM_EP = 26, NEHALEM = 30, ATOM = 28, ATOM_2 = 53, CENTERTON = 54, BAYTRAIL = 55, AVOTON = 77, CHERRYTRAIL = 76, APOLLO_LAKE = 92, GEMINI_LAKE = 122, DENVERTON = 95, SNOWRIDGE = 134, CLARKDALE = 37, WESTMERE_EP = 44, NEHALEM_EX = 46, WESTMERE_EX = 47, SANDY_BRIDGE = 42, JAKETOWN = 45, IVY_BRIDGE = 58, HASWELL = 60, HASWELL_ULT = 69, HASWELL_2 = 70, IVYTOWN = 62, HASWELLX = 63, BROADWELL = 61, BROADWELL_XEON_E3 = 71, BDX_DE = 86, SKL_UY = 78, KBL = 158, KBL_1 = 142, CML = 166, CML_1 = 165, ICL = 126, ICL_1 = 125, RKL = 167, TGL = 140, TGL_1 = 141, ADL = 151, ADL_1 = 154, RPL = 0xb7, RPL_1 = 0xba, RPL_2 = 0xbf, RPL_3 = 0xbe, BDX = 79, KNL = 87, SKL = 94, SKX = 85, ICX_D = 108, ICX = 106, SPR = 143, END_OF_MODEL_LIST = 0x0ffff }; #define PCM_SKL_PATH_CASES \ case PCM::SKL_UY: \ case PCM::KBL: \ case PCM::KBL_1: \ case PCM::CML: \ case PCM::ICL: \ case PCM::RKL: \ case PCM::TGL: \ case PCM::SKL: private: bool useSKLPath() const { switch (cpu_model) { PCM_SKL_PATH_CASES return true; } return false; } RawPMUConfig threadMSRConfig{}, packageMSRConfig{}, pcicfgConfig{}; public: //! \brief Reads CPU model id //! \return CPU model ID uint32 getCPUModel() const { return (uint32)cpu_model; } //! \brief Reads CPU stepping id //! \return CPU stepping ID uint32 getCPUStepping() const { return (uint32)cpu_stepping; } //! \brief Determines physical thread of given processor ID within a core //! \param os_id processor identifier //! \return physical thread identifier int32 getThreadId(uint32 os_id) const { return (int32)topology[os_id].thread_id; } //! \brief Determines physical core of given processor ID within a socket //! \param os_id processor identifier //! \return physical core identifier int32 getCoreId(uint32 os_id) const { return (int32)topology[os_id].core_id; } //! \brief Determines physical tile (cores sharing L2 cache) of given processor ID //! \param os_id processor identifier //! \return physical tile identifier int32 getTileId(uint32 os_id) const { return (int32)topology[os_id].tile_id; } //! \brief Determines socket of given core //! \param core_id core identifier //! \return socket identifier int32 getSocketId(uint32 core_id) const { return (int32)topology[core_id].socket; } size_t getNumCXLPorts(uint32 socket) const { if (socket < cxlPMUs.size()) { return cxlPMUs[socket].size(); } return 0; } //! \brief Returns the number of Intel(r) Quick Path Interconnect(tm) links per socket //! \return number of QPI links per socket uint64 getQPILinksPerSocket() const { switch (cpu_model) { case NEHALEM_EP: case WESTMERE_EP: case CLARKDALE: if (num_sockets == 2) return 2; else return 1; case NEHALEM_EX: case WESTMERE_EX: return 4; case JAKETOWN: case IVYTOWN: case HASWELLX: case BDX_DE: case BDX: case SKX: case ICX: case SPR: return (serverUncorePMUs.size() && serverUncorePMUs[0].get()) ? (serverUncorePMUs[0]->getNumQPIPorts()) : 0; } return 0; } //! \brief Returns the number of detected integrated memory controllers per socket uint32 getMCPerSocket() const { switch (cpu_model) { case NEHALEM_EP: case WESTMERE_EP: case CLARKDALE: return 1; case NEHALEM_EX: case WESTMERE_EX: return 2; case JAKETOWN: case IVYTOWN: case HASWELLX: case BDX_DE: case SKX: case ICX: case SPR: case BDX: case KNL: return (serverUncorePMUs.size() && serverUncorePMUs[0].get()) ? (serverUncorePMUs[0]->getNumMC()) : 0; } return 0; } //! \brief Returns the total number of detected memory channels on all integrated memory controllers per socket size_t getMCChannelsPerSocket() const { switch (cpu_model) { case NEHALEM_EP: case WESTMERE_EP: case CLARKDALE: return 3; case NEHALEM_EX: case WESTMERE_EX: return 4; case JAKETOWN: case IVYTOWN: case HASWELLX: case BDX_DE: case SKX: case ICX: case SPR: case BDX: case KNL: case SNOWRIDGE: return (serverUncorePMUs.size() && serverUncorePMUs[0].get()) ? (serverUncorePMUs[0]->getNumMCChannels()) : 0; } return 0; } //! \brief Returns the number of detected memory channels on given integrated memory controllers //! \param socket socket //! \param controller controller size_t getMCChannels(uint32 socket, uint32 controller) const { switch (cpu_model) { case NEHALEM_EP: case WESTMERE_EP: case CLARKDALE: return 3; case NEHALEM_EX: case WESTMERE_EX: return 4; case JAKETOWN: case IVYTOWN: case HASWELLX: case BDX_DE: case SKX: case ICX: case SPR: case BDX: case KNL: case SNOWRIDGE: return (socket < serverUncorePMUs.size() && serverUncorePMUs[socket].get()) ? (serverUncorePMUs[socket]->getNumMCChannels(controller)) : 0; } return 0; } //! \brief Returns the total number of detected memory channels on all integrated memory controllers per socket size_t getEDCChannelsPerSocket() const { switch (cpu_model) { case KNL: return (serverUncorePMUs.size() && serverUncorePMUs[0].get()) ? (serverUncorePMUs[0]->getNumEDCChannels()) : 0; } return 0; } //! \brief Returns the max number of instructions per cycle //! \return max number of instructions per cycle uint32 getMaxIPC() const { if (ICL == cpu_model || TGL == cpu_model || RKL == cpu_model) return 5; switch (cpu_model) { case ADL: case RPL: return 6; case SNOWRIDGE: return 4; case DENVERTON: return 3; case NEHALEM_EP: case WESTMERE_EP: case NEHALEM_EX: case WESTMERE_EX: case CLARKDALE: case SANDY_BRIDGE: case JAKETOWN: case IVYTOWN: case IVY_BRIDGE: case HASWELL: case HASWELLX: case BROADWELL: case BDX_DE: case BDX: PCM_SKL_PATH_CASES case SKX: return 4; case KNL: return 2; case ICX: return 5; case SPR: return 6; } if (isAtom()) { return 2; } std::cerr << "MaxIPC is not defined for your cpu model " << cpu_model << '\n'; assert (0); return 0; } //! \brief Returns the frequency of Power Control Unit uint64 getPCUFrequency() const { switch (cpu_model) { case JAKETOWN: case IVYTOWN: return 800000000ULL; // 800 MHz case HASWELLX: case BDX_DE: case BDX: case KNL: return 1000000000ULL; // 1 GHz case SKX: case ICX: case SNOWRIDGE: return 1100000000ULL; // 1.1 GHz } return 0; } //! \brief Returns whether it is a server part bool isServerCPU() const { switch (cpu_model) { case NEHALEM_EP: case NEHALEM_EX: case WESTMERE_EP: case WESTMERE_EX: case JAKETOWN: case IVYTOWN: case HASWELLX: case BDX: case BDX_DE: case SKX: case ICX: case SNOWRIDGE: case SPR: case KNL: return true; default: return false; }; } //! \brief Returns whether it is a client part bool isClientCPU() const { return !isServerCPU(); } //! \brief Return TSC timer value in time units //! \param multiplier use 1 for seconds, 1000 for ms, 1000000 for mks, etc (default is 1000: ms) //! \param core core to read on-chip TSC value (default is 0) //! \return time counter value uint64 getTickCount(uint64 multiplier = 1000 /* ms */, uint32 core = 0); uint64 getInvariantTSC_Fast(uint32 core = 0); //! \brief Returns uncore clock ticks on specified socket uint64 getUncoreClocks(const uint32 socket_); //! \brief Return QPI Link Speed in GBytes/second //! \warning Works only for Nehalem-EX (Xeon 7500) and Xeon E7 and E5 processors //! \return QPI Link Speed in GBytes/second uint64 getQPILinkSpeed(uint32 socketNr, uint32 linkNr) const { return hasPCICFGUncore() ? serverUncorePMUs[socketNr]->getQPILinkSpeed(linkNr) : max_qpi_speed; } //! \brief Returns how many joules are in an internal processor energy unit double getJoulesPerEnergyUnit() const { return joulesPerEnergyUnit; } //! \brief Returns thermal specification power of the package domain in Watt int32 getPackageThermalSpecPower() const { return pkgThermalSpecPower; } //! \brief Returns minimum power derived from electrical spec of the package domain in Watt int32 getPackageMinimumPower() const { return pkgMinimumPower; } //! \brief Returns maximum power derived from electrical spec of the package domain in Watt int32 getPackageMaximumPower() const { return pkgMaximumPower; } #ifndef NO_WINRING // In cases where loading the WinRing0 driver is not desirable as a fallback to MSR.sys, add -DNO_WINRING to compile command to remove ability to load driver //! \brief Loads and initializes Winring0 third party library for access to processor model specific and PCI configuration registers //! \return returns true in case of success static bool initWinRing0Lib(); #endif // NO_WINRING inline void disableJKTWorkaround() { disable_JKT_workaround = true; } enum PCIeEventCode { // PCIe read events (PCI devices reading from memory - application writes to disk/network/PCIe device) PCIeRdCur = 0x19E, // PCIe read current (full cache line) PCIeNSRd = 0x1E4, // PCIe non-snoop read (full cache line) // PCIe write events (PCI devices writing to memory - application reads from disk/network/PCIe device) PCIeWiLF = 0x194, // PCIe Write (non-allocating) (full cache line) PCIeItoM = 0x19C, // PCIe Write (allocating) (full cache line) PCIeNSWr = 0x1E5, // PCIe Non-snoop write (partial cache line) PCIeNSWrF = 0x1E6, // PCIe Non-snoop write (full cache line) // events shared by CPU and IO RFO = 0x180, // Demand Data RFO; share the same code for CPU, use tid to filter PCIe only traffic CRd = 0x181, // Demand Code Read DRd = 0x182, // Demand Data Read PRd = 0x187, // Partial Reads (UC) (MMIO Read) WiL = 0x18F, // Write Invalidate Line - partial (MMIO write), PL: Not documented in HSX/IVT ItoM = 0x1C8, // Request Invalidate Line; share the same code for CPU, use tid to filter PCIe only traffic SKX_RFO = 0x200, SKX_CRd = 0x201, SKX_DRd = 0x202, SKX_PRd = 0x207, SKX_WiL = 0x20F, SKX_RdCur = 0x21E, SKX_ItoM = 0x248, }; enum ChaPipelineQueue { None, IRQ, PRQ, }; enum CBoEventTid { RFOtid = 0x3E, ItoMtid = 0x3E, }; //! \brief Program uncore PCIe monitoring event(s) //! \param eventGroup - events to program for the same run void programPCIeEventGroup(eventGroup_t &eventGroup); uint64 getPCIeCounterData(const uint32 socket_, const uint32 ctr_); //! \brief Program CBO (or CHA on SKX+) counters //! \param events array with four raw event values //! \param opCode opcode match filter //! \param nc_ match non-coherent requests //! \param llc_lookup_tid_filter filter for LLC lookup event filter and TID filter (core and thread ID) //! \param loc match on local node target //! \param rem match on remote node target void programCbo(const uint64 * events, const uint32 opCode = 0, const uint32 nc_ = 0, const uint32 llc_lookup_tid_filter = 0, const uint32 loc = 1, const uint32 rem = 1); //! \brief Program CBO (or CHA on SKX+) counters //! \param events array with four raw event values //! \param filter0 raw filter value //! \param filter1 raw filter1 value void programCboRaw(const uint64* events, const uint64 filter0, const uint64 filter1); //! \brief Program MDF counters //! \param events array with four raw event values void programMDF(const uint64* events); //! \brief Get the state of PCIe counter(s) //! \param socket_ socket of the PCIe controller //! \return State of PCIe counter(s) PCIeCounterState getPCIeCounterState(const uint32 socket_, const uint32 ctr_ = 0); //! \brief Program uncore IIO events //! \param rawEvents events to program (raw format) //! \param IIOStack id of the IIO stack to program (-1 for all, if parameter omitted) void programIIOCounters(uint64 rawEvents[4], int IIOStack = -1); //! \brief Program uncore IRP events //! \param rawEvents events to program (raw format) //! \param IIOStack id of the IIO stack to program (-1 for all, if parameter omitted) void programIRPCounters(uint64 rawEvents[4], int IIOStack = -1); //! \brief Control QAT telemetry service //! \param dev device index //! \param operation control code void controlQATTelemetry(uint32 dev, uint32 operation); //! \brief Program IDX events //! \param events config of event to program //! \param filters_wq filters(work queue) of event to program //! \param filters_eng filters(engine) of event to program //! \param filters_tc filters(traffic class) of event to program //! \param filters_pgsz filters(page size) of event to program //! \param filters_xfersz filters(transfer size) of event to program void programIDXAccelCounters(uint32 accel, std::vector &events, std::vector &filters_wq, std::vector &filters_eng, std::vector &filters_tc, std::vector &filters_pgsz, std::vector &filters_xfersz); //! \brief Get the state of IIO counter //! \param socket socket of the IIO stack //! \param IIOStack id of the IIO stack //! \return State of IIO counter IIOCounterState getIIOCounterState(int socket, int IIOStack, int counter); //! \brief Get the states of the four IIO counters in bulk (faster than four single reads) //! \param socket socket of the IIO stack //! \param IIOStack id of the IIO stack //! \param result states of IIO counters (array of four IIOCounterState elements) void getIIOCounterStates(int socket, int IIOStack, IIOCounterState * result); //! \brief Get the state of IDX accel counter //! \param accel ip index //! \param dev device index //! \param counter_id perf counter index //! \return State of IDX counter IDXCounterState getIDXAccelCounterState(uint32 accel, uint32 dev, uint32 counter_id); uint64 extractCoreGenCounterValue(uint64 val); uint64 extractCoreFixedCounterValue(uint64 val); uint64 extractUncoreGenCounterValue(uint64 val); uint64 extractUncoreFixedCounterValue(uint64 val); uint64 extractQOSMonitoring(uint64 val); //! \brief Get a string describing the codename of the processor microarchitecture //! \param cpu_model_ cpu model (if no parameter provided the codename of the detected CPU is returned) const char * getUArchCodename(const int32 cpu_model_ = -1) const; //! \brief Get Brand string of processor static std::string getCPUBrandString(); std::string getCPUFamilyModelString(); //! \brief Enables "force all RTM transaction abort" mode also enabling 4+ programmable counters on Skylake generation processors void enableForceRTMAbortMode(const bool silent = false); //! \brief queries status of "force all RTM transaction abort" mode bool isForceRTMAbortModeEnabled() const; //! \brief Disables "force all RTM transaction abort" mode restricting the number of programmable counters on Skylake generation processors to 3 void disableForceRTMAbortMode(const bool silent = false); //! \brief queries availability of "force all RTM transaction abort" mode bool isForceRTMAbortModeAvailable() const; //! \brief Get microcode level (returns -1 if retrieval not supported due to some restrictions) int64 getCPUMicrocodeLevel() const { return cpu_microcode_level; } //! \brief returns true if CPU model is Atom-based static bool isAtom(const int32 cpu_model_) { return cpu_model_ == ATOM || cpu_model_ == ATOM_2 || cpu_model_ == CENTERTON || cpu_model_ == BAYTRAIL || cpu_model_ == AVOTON || cpu_model_ == CHERRYTRAIL || cpu_model_ == APOLLO_LAKE || cpu_model_ == GEMINI_LAKE || cpu_model_ == DENVERTON // || cpu_model_ == SNOWRIDGE do not use Atom code for SNOWRIDGE ; } //! \brief returns true if CPU is Atom-based bool isAtom() const { return isAtom(cpu_model); } // From commit message: https://github.com/torvalds/linux/commit/e979121b1b1556e184492e6fc149bbe188fc83e6 bool memoryEventErrata() const { switch (cpu_model) { case SANDY_BRIDGE: case JAKETOWN: case IVYTOWN: case IVY_BRIDGE: case HASWELL: case HASWELLX: return true; } return false; } bool packageEnergyMetricsAvailable() const { return ( cpu_model == PCM::JAKETOWN || cpu_model == PCM::IVYTOWN || cpu_model == PCM::SANDY_BRIDGE || cpu_model == PCM::IVY_BRIDGE || cpu_model == PCM::HASWELL || cpu_model == PCM::AVOTON || cpu_model == PCM::CHERRYTRAIL || cpu_model == PCM::BAYTRAIL || cpu_model == PCM::APOLLO_LAKE || cpu_model == PCM::GEMINI_LAKE || cpu_model == PCM::DENVERTON || cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::HASWELLX || cpu_model == PCM::BROADWELL || cpu_model == PCM::BDX_DE || cpu_model == PCM::BDX || cpu_model == PCM::KNL || useSKLPath() || cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::ADL || cpu_model == PCM::RPL || cpu_model == PCM::SPR ); } bool dramEnergyMetricsAvailable() const { return ( cpu_model == PCM::JAKETOWN || cpu_model == PCM::IVYTOWN || cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX_DE || cpu_model == PCM::BDX || cpu_model == PCM::KNL || cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SPR ); } bool packageThermalMetricsAvailable() const { return packageEnergyMetricsAvailable(); } bool outgoingQPITrafficMetricsAvailable() const { return getQPILinksPerSocket() > 0 && ( cpu_model == PCM::NEHALEM_EX || cpu_model == PCM::WESTMERE_EX || cpu_model == PCM::JAKETOWN || cpu_model == PCM::IVYTOWN || cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX || cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SPR ); } bool incomingQPITrafficMetricsAvailable() const { return getQPILinksPerSocket() > 0 && ( cpu_model == PCM::NEHALEM_EX || cpu_model == PCM::WESTMERE_EX || cpu_model == PCM::JAKETOWN || cpu_model == PCM::IVYTOWN || (cpu_model == PCM::SKX && cpu_stepping > 1) || cpu_model == PCM::ICX || cpu_model == PCM::SPR ); } bool localMemoryRequestRatioMetricAvailable() const { return cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX || cpu_model == PCM::SKX || cpu_model == PCM::ICX ; } bool qpiUtilizationMetricsAvailable() const { return outgoingQPITrafficMetricsAvailable(); } bool memoryTrafficMetricsAvailable() const { return (!(isAtom() || cpu_model == PCM::CLARKDALE)) ; } bool HBMmemoryTrafficMetricsAvailable() const { return serverUncorePMUs.empty() == false && serverUncorePMUs[0].get() != nullptr && serverUncorePMUs[0]->HBMAvailable(); } size_t getHBMCASTransferSize() const { return (SPR == cpu_model) ? 32ULL : 64ULL; } bool memoryIOTrafficMetricAvailable() const { if (cpu_model == TGL) return false; return ( cpu_model == PCM::SANDY_BRIDGE || cpu_model == PCM::IVY_BRIDGE || cpu_model == PCM::HASWELL || cpu_model == PCM::BROADWELL || useSKLPath() ); } bool IIOEventsAvailable() const { return ( cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::SPR ); } bool uncoreFrequencyMetricAvailable() const { return MSR.empty() == false && uboxPMUs.size() == getNumSockets() && getNumCores() == getNumOnlineCores(); } bool LatencyMetricsAvailable() const { return ( cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX || cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SPR || useSKLPath() ); } bool DDRLatencyMetricsAvailable() const { return ( cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SPR ); } bool PMMTrafficMetricsAvailable() const { return ( isCLX() || isCPX() || cpu_model == PCM::ICX || cpu_model == PCM::SNOWRIDGE || cpu_model == SPR ); } bool PMMMemoryModeMetricsAvailable() const { return ( isCLX() || isCPX() || cpu_model == PCM::ICX || cpu_model == PCM::SNOWRIDGE ); } bool PMMMixedModeMetricsAvailable() const { return PMMMemoryModeMetricsAvailable(); } bool LLCReadMissLatencyMetricsAvailable() const { return ( HASWELLX == cpu_model || BDX_DE == cpu_model || BDX == cpu_model || isCLX() || isCPX() #ifdef PCM_ENABLE_LLCRDLAT_SKX_MP || SKX == cpu_model #else || ((SKX == cpu_model) && (num_sockets == 1)) #endif || ICX == cpu_model || SPR == cpu_model || SNOWRIDGE == cpu_model ); } bool hasBecktonUncore() const { return ( cpu_model == PCM::NEHALEM_EX || cpu_model == PCM::WESTMERE_EX ); } bool hasPCICFGUncore() const // has PCICFG uncore PMON { return ( cpu_model == PCM::JAKETOWN || cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::IVYTOWN || cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX_DE || cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SPR || cpu_model == PCM::BDX || cpu_model == PCM::KNL ); } bool isSkxCompatible() const { return ( cpu_model == PCM::SKX ); } static bool hasUPI(const int32 cpu_model_) // Intel(r) Ultra Path Interconnect { return ( cpu_model_ == PCM::SKX || cpu_model_ == PCM::ICX || cpu_model_ == PCM::SPR ); } bool hasUPI() const { return hasUPI(cpu_model); } const char * xPI() const { if (hasUPI()) return "UPI"; return "QPI"; } bool hasCHA() const { return ( cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SPR ); } bool supportsHLE() const; bool supportsRTM() const; bool supportsRDTSCP() const; bool useSkylakeEvents() const { return useSKLPath() || PCM::SKX == cpu_model || PCM::ICX == cpu_model || PCM::SPR == cpu_model ; } bool hasClientMCCounters() const { return cpu_model == SANDY_BRIDGE || cpu_model == IVY_BRIDGE || cpu_model == HASWELL || cpu_model == BROADWELL || cpu_model == ADL || cpu_model == RPL || useSKLPath() ; } static double getBytesPerFlit(int32 cpu_model_) { if (hasUPI(cpu_model_)) { // 172 bits per UPI flit return 172./8.; } // 8 bytes per QPI flit return 8.; } double getBytesPerFlit() const { return getBytesPerFlit(cpu_model); } static double getDataBytesPerFlit(int32 cpu_model_) { if (hasUPI(cpu_model_)) { // 9 UPI flits to transfer 64 bytes return 64./9.; } // 8 bytes per QPI flit return 8.; } double getDataBytesPerFlit() const { return getDataBytesPerFlit(cpu_model); } static double getFlitsPerLinkCycle(int32 cpu_model_) { if (hasUPI(cpu_model_)) { // 5 UPI flits sent every 6 link cycles return 5./6.; } return 2.; } static double getBytesPerLinkCycle(int32 cpu_model_) { return getBytesPerFlit(cpu_model_) * getFlitsPerLinkCycle(cpu_model_); } double getBytesPerLinkCycle() const { return getBytesPerLinkCycle(cpu_model); } static double getLinkTransfersPerLinkCycle() { return 8.; } double getBytesPerLinkTransfer() const { return getBytesPerLinkCycle() / getLinkTransfersPerLinkCycle(); } //! \brief Setup ExtendedCustomCoreEventDescription object to read offcore (numa) counters for each processor type //! \param conf conf object to setup offcore MSR values void setupCustomCoreEventsForNuma(PCM::ExtendedCustomCoreEventDescription& conf) const; #define PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(m) bool is##m() const { return m; } PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L2CacheHitRatioAvailable) PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L3CacheHitRatioAvailable) PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L3CacheMissesAvailable) PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L2CacheMissesAvailable) PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L2CacheHitsAvailable) PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L3CacheHitsNoSnoopAvailable) PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L3CacheHitsSnoopAvailable) PCM_GENERATE_METRIC_AVAILABLE_FUNCTION(L3CacheHitsAvailable) #undef PCM_GEN_METRIC_AVAILABLE_FUNCTION bool isActiveRelativeFrequencyAvailable() const { return !isAtom(); } ~PCM(); }; //! \brief Basic core counter state //! //! Intended only for derivation, but not for the direct use class BasicCounterState { friend class PCM; friend class JSONPrinter; template friend double getExecUsage(const CounterStateType & before, const CounterStateType & after); template friend double getIPC(const CounterStateType & before, const CounterStateType & after); template friend double getAverageFrequency(const CounterStateType & before, const CounterStateType & after); template friend double getAverageFrequencyFromClocks(const int64 clocks, const CounterStateType& before, const CounterStateType& after); template friend double getActiveAverageFrequency(const CounterStateType & before, const CounterStateType & after); template friend double getRelativeFrequency(const CounterStateType & before, const CounterStateType & after); template friend double getActiveRelativeFrequency(const CounterStateType & before, const CounterStateType & after); template friend double getL2CacheHitRatio(const CounterStateType & before, const CounterStateType & after); template friend double getL3CacheHitRatio(const CounterStateType & before, const CounterStateType & after); template friend uint64 getL3CacheMisses(const CounterStateType & before, const CounterStateType & after); template friend uint64 getL2CacheMisses(const CounterStateType & before, const CounterStateType & after); template friend uint64 getL2CacheHits(const CounterStateType & before, const CounterStateType & after); template friend uint64 getL3CacheHitsNoSnoop(const CounterStateType & before, const CounterStateType & after); template friend uint64 getL3CacheHitsSnoop(const CounterStateType & before, const CounterStateType & after); template friend uint64 getL3CacheHits(const CounterStateType & before, const CounterStateType & after); template friend uint64 getL3CacheOccupancy(const CounterStateType & now); template friend uint64 getLocalMemoryBW(const CounterStateType & before, const CounterStateType & after); template friend uint64 getRemoteMemoryBW(const CounterStateType & before, const CounterStateType & after); template friend uint64 getCycles(const CounterStateType & before, const CounterStateType & after); template friend uint64 getInstructionsRetired(const CounterStateType & before, const CounterStateType & after); template friend uint64 getCycles(const CounterStateType & now); template friend uint64 getInstructionsRetired(const CounterStateType & now); template friend uint64 getNumberOfCustomEvents(int32 eventCounterNr, const CounterStateType & before, const CounterStateType & after); template friend uint64 getInvariantTSC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getRefCycles(const CounterStateType & before, const CounterStateType & after); template friend double getCoreCStateResidency(int state, const CounterStateType & before, const CounterStateType & after); template friend uint64 getCoreCStateResidency(int state, const CounterStateType& now); template friend uint64 getSMICount(const CounterStateType & before, const CounterStateType & after); template friend uint64 getAllSlotsRaw(const CounterStateType& before, const CounterStateType& after); template friend uint64 getAllSlots(const CounterStateType & before, const CounterStateType & after); template friend double getBackendBound(const CounterStateType & before, const CounterStateType & after); template friend double getFrontendBound(const CounterStateType & before, const CounterStateType & after); template friend double getBadSpeculation(const CounterStateType & before, const CounterStateType & after); template friend double getRetiring(const CounterStateType & before, const CounterStateType & after); template friend uint64 getMSREvent(const uint64 & index, const PCM::MSRType & type, const CounterStateType& before, const CounterStateType& after); protected: checked_uint64 InstRetiredAny{}; checked_uint64 CpuClkUnhaltedThread{}; checked_uint64 CpuClkUnhaltedRef{}; checked_uint64 Event[PERF_MAX_CUSTOM_COUNTERS]; enum { L3MissPos = 0, ArchLLCMissPos = 0, L3UnsharedHitPos = 1, ArchLLCRefPos = 1, SKLL3HitPos = 1, L2HitMPos = 2, SKLL2MissPos = 2, HSXL2MissPos = 2, L2HitPos = 3, HSXL2RefPos = 3 }; uint64 InvariantTSC; // invariant time stamp counter uint64 CStateResidency[PCM::MAX_C_STATE + 1]; int32 ThermalHeadroom; uint64 L3Occupancy; uint64 MemoryBWLocal; uint64 MemoryBWTotal; uint64 SMICount; uint64 FrontendBoundSlots, BadSpeculationSlots, BackendBoundSlots, RetiringSlots, AllSlotsRaw; std::unordered_map MSRValues; public: BasicCounterState() : InvariantTSC(0), ThermalHeadroom(PCM_INVALID_THERMAL_HEADROOM), L3Occupancy(0), MemoryBWLocal(0), MemoryBWTotal(0), SMICount(0), FrontendBoundSlots(0), BadSpeculationSlots(0), BackendBoundSlots(0), RetiringSlots(0), AllSlotsRaw(0) { std::fill(CStateResidency, CStateResidency + PCM::MAX_C_STATE + 1, 0); } virtual ~BasicCounterState() { } BasicCounterState( const BasicCounterState& ) = default; BasicCounterState( BasicCounterState&& ) = default; BasicCounterState & operator = ( BasicCounterState&& ) = default; BasicCounterState & operator += (const BasicCounterState & o) { InstRetiredAny += o.InstRetiredAny; CpuClkUnhaltedThread += o.CpuClkUnhaltedThread; CpuClkUnhaltedRef += o.CpuClkUnhaltedRef; for (int i = 0; i < PERF_MAX_CUSTOM_COUNTERS; ++i) { Event[i] += o.Event[i]; } InvariantTSC += o.InvariantTSC; for (int i = 0; i <= (int)PCM::MAX_C_STATE; ++i) CStateResidency[i] += o.CStateResidency[i]; // ThermalHeadroom is not accumulative L3Occupancy += o.L3Occupancy; MemoryBWLocal += o.MemoryBWLocal; MemoryBWTotal += o.MemoryBWTotal; SMICount += o.SMICount; // std::cout << "before PCM debug aggregate "<< FrontendBoundSlots << " " << BadSpeculationSlots << " " << BackendBoundSlots << " " <= old.FrontendBoundSlots); assert(BadSpeculationSlots >= old.BadSpeculationSlots); assert(BackendBoundSlots >= old.BackendBoundSlots); assert(RetiringSlots >= old.RetiringSlots); return *this; } void readAndAggregate(std::shared_ptr); void readAndAggregateTSC(std::shared_ptr); //! Returns current thermal headroom below TjMax int32 getThermalHeadroom() const { return ThermalHeadroom; } }; inline uint64 RDTSC() { uint64 result = 0; #ifdef _MSC_VER // Windows #if _MSC_VER>= 1600 result = static_cast(__rdtsc()); #endif #else // Linux uint32 high = 0, low = 0; asm volatile("rdtsc" : "=a" (low), "=d" (high)); result = low + (uint64(high)<<32ULL); #endif return result; } inline uint64 RDTSCP() { uint64 result = 0; #ifdef _MSC_VER // Windows #if _MSC_VER>= 1600 unsigned int Aux; result = __rdtscp(&Aux); #endif #else // Linux and OS X uint32 high = 0, low = 0; asm volatile ( "rdtscp\n\t" "mov %%edx, %0\n\t" "mov %%eax, %1\n\t": "=r" (high), "=r" (low) :: "%rax", "%rcx", "%rdx"); result = low + (uint64(high)<<32ULL); #endif return result; } template int32 getThermalHeadroom(const CounterStateType & /* before */, const CounterStateType & after) { return after.getThermalHeadroom(); } /*! \brief Returns the ratio of QPI cycles in power saving half-lane mode \param port QPI port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return 0..1 - ratio of QPI cycles in power saving half-lane mode */ template double getNormalizedQPIL0pTxCycles(uint32 port, const CounterStateType & before, const CounterStateType & after) { return double(getQPIL0pTxCycles(port, before, after)) / double(getQPIClocks(port, before, after)); } /*! \brief Returns the ratio of QPI cycles in power saving shutdown mode \param port QPI port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return 0..1 - ratio of QPI cycles in power saving shutdown mode */ template double getNormalizedQPIL1Cycles(uint32 port, const CounterStateType & before, const CounterStateType & after) { return double(getQPIL1Cycles(port, before, after)) / double(getQPIClocks(port, before, after)); } /*! \brief Returns DRAM clock ticks \param channel DRAM channel number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getDRAMClocks(uint32 channel, const CounterStateType & before, const CounterStateType & after) { const auto clk = after.DRAMClocks[channel] - before.DRAMClocks[channel]; const auto cpu_model = PCM::getInstance()->getCPUModel(); if (cpu_model == PCM::ICX || cpu_model == PCM::SNOWRIDGE) { return 2 * clk; } return clk; } /*! \brief Returns HBM clock ticks \param channel HBM channel number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getHBMClocks(uint32 channel, const CounterStateType & before, const CounterStateType & after) { return after.HBMClocks[channel] - before.HBMClocks[channel]; } /*! \brief Direct read of memory controller PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param channel channel number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getMCCounter(uint32 channel, uint32 counter, const CounterStateType & before, const CounterStateType & after) { return after.MCCounter[channel][counter] - before.MCCounter[channel][counter]; } /*! \brief Direct read of CXLCM PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param port port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getCXLCMCounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.CXLCMCounter[port][counter] - before.CXLCMCounter[port][counter]; } /*! \brief Direct read of CXLDP PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param port port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getCXLDPCounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.CXLDPCounter[port][counter] - before.CXLDPCounter[port][counter]; } /*! \brief Direct read of M3UPI PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param port UPI port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getM3UPICounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.M3UPICounter[port][counter] - before.M3UPICounter[port][counter]; } /*! \brief Direct read of CHA or CBO PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param cbo cbo or cha number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getCBOCounter(uint32 cbo, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.CBOCounter[cbo][counter] - before.CBOCounter[cbo][counter]; } /*! \brief Direct read of MDF PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param mdf mdf number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getMDFCounter(uint32 mdf, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.MDFCounter[mdf][counter] - before.MDFCounter[mdf][counter]; } /*! \brief Direct read of UBOX PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getUBOXCounter(uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.UBOXCounter[counter] - before.UBOXCounter[counter]; } /*! \brief Direct read of IIO PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param stack IIO stack number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getIIOCounter(uint32 stack, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.IIOCounter[stack][counter] - before.IIOCounter[stack][counter]; } /*! \brief Direct read of IRP PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param stack IIO stack number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getIRPCounter(uint32 stack, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.IRPCounter[stack][counter] - before.IRPCounter[stack][counter]; } /*! \brief Direct read of UPI or QPI PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param port UPI/QPI port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getXPICounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after) { return after.xPICounter[port][counter] - before.xPICounter[port][counter]; } /*! \brief Direct read of Memory2Mesh controller PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param controller controller number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getM2MCounter(uint32 controller, uint32 counter, const CounterStateType & before, const CounterStateType & after) { return after.M2MCounter[controller][counter] - before.M2MCounter[controller][counter]; } /*! \brief Direct read of embedded DRAM memory controller counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param channel channel number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getEDCCounter(uint32 channel, uint32 counter, const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->HBMmemoryTrafficMetricsAvailable()) return after.EDCCounter[channel][counter] - before.EDCCounter[channel][counter]; return 0ULL; } /*! \brief Direct read of power control unit PMU counter (counter meaning depends on the programming: power/performance/etc) \param counter counter number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getPCUCounter(uint32 counter, const CounterStateType & before, const CounterStateType & after) { return after.PCUCounter[counter] - before.PCUCounter[counter]; } /*! \brief Returns clock ticks of power control unit \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getPCUClocks(const CounterStateType & before, const CounterStateType & after) { return getPCUCounter(0, before, after); } /*! \brief Returns energy consumed by processor, excluding DRAM (measured in internal units) \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getConsumedEnergy(const CounterStateType & before, const CounterStateType & after) { return after.PackageEnergyStatus - before.PackageEnergyStatus; } /*! \brief Returns energy consumed by DRAM (measured in internal units) \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getDRAMConsumedEnergy(const CounterStateType & before, const CounterStateType & after) { return after.DRAMEnergyStatus - before.DRAMEnergyStatus; } /*! \brief Returns free running counter if it exists, -1 otherwise * \param counter name of the counter * \param before CPU counter state before the experiment * \param after CPU counter state after the experiment */ template int64 getFreeRunningCounter(const typename CounterStateType::FreeRunningCounterID & counter, const CounterStateType & before, const CounterStateType & after) { const auto beforeIt = before.freeRunningCounter.find(counter); const auto afterIt = after.freeRunningCounter.find(counter); if (beforeIt != before.freeRunningCounter.end() && afterIt != after.freeRunningCounter.end()) { return afterIt->second - beforeIt->second; } return -1; } /*! \brief Returns uncore clock ticks \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getUncoreClocks(const CounterStateType& before, const CounterStateType& after) { return after.UncClocks - before.UncClocks; } /*! \brief Returns Joules consumed by processor (excluding DRAM) \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template double getConsumedJoules(const CounterStateType & before, const CounterStateType & after) { PCM * m = PCM::getInstance(); if (!m) return -1.; return double(getConsumedEnergy(before, after)) * m->getJoulesPerEnergyUnit(); } /*! \brief Returns Joules consumed by DRAM \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template double getDRAMConsumedJoules(const CounterStateType & before, const CounterStateType & after) { PCM * m = PCM::getInstance(); if (!m) return -1.; double dram_joules_per_energy_unit = 0.; const auto cpu_model = m->getCPUModel(); if (PCM::HASWELLX == cpu_model || PCM::BDX_DE == cpu_model || PCM::BDX == cpu_model || PCM::SKX == cpu_model || PCM::ICX == cpu_model || PCM::KNL == cpu_model ) { /* as described in sections 5.3.2 (DRAM_POWER_INFO) and 5.3.3 (DRAM_ENERGY_STATUS) of * Volume 2 (Registers) of * Intel Xeon E5-1600 v3 and Intel Xeon E5-2600 v3 (Haswell-EP) Datasheet (Ref 330784-001, Sept.2014) * ENERGY_UNIT for DRAM domain is fixed to 15.3 uJ for server HSX, BDW and KNL processors. */ dram_joules_per_energy_unit = 0.0000153; } else { /* for all other processors (including Haswell client/mobile SKUs) the ENERGY_UNIT for DRAM domain * should be read from PACKAGE_POWER_SKU register (usually value around ~61uJ) */ dram_joules_per_energy_unit = m->getJoulesPerEnergyUnit(); } return double(getDRAMConsumedEnergy(before, after)) * dram_joules_per_energy_unit; } //! \brief Basic uncore counter state //! //! Intended only for derivation, but not for the direct use class UncoreCounterState { friend class PCM; friend class JSONPrinter; template friend uint64 getBytesReadFromMC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getBytesWrittenToMC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getBytesReadFromPMM(const CounterStateType & before, const CounterStateType & after); template friend uint64 getBytesWrittenToPMM(const CounterStateType & before, const CounterStateType & after); template friend uint64 getBytesReadFromEDC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getBytesWrittenToEDC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getGTRequestBytesFromMC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getIARequestBytesFromMC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getIORequestBytesFromMC(const CounterStateType & before, const CounterStateType & after); template friend uint64 getConsumedEnergy(const CounterStateType & before, const CounterStateType & after); template friend uint64 getDRAMConsumedEnergy(const CounterStateType & before, const CounterStateType & after); template friend uint64 getUncoreClocks(const CounterStateType& before, const CounterStateType& after); template friend double getPackageCStateResidency(int state, const CounterStateType & before, const CounterStateType & after); template friend uint64 getPackageCStateResidency(int state, const CounterStateType& now); template friend double getLLCReadMissLatency(const CounterStateType & before, const CounterStateType & after); template friend double getLocalMemoryRequestRatio(const CounterStateType & before, const CounterStateType & after); template friend double getAverageUncoreFrequency(const CounterStateType& before, const CounterStateType& after); template friend double getAverageFrequencyFromClocks(const int64 clocks, const CounterStateType& before, const CounterStateType& after); protected: uint64 UncMCFullWrites; uint64 UncMCNormalReads; uint64 UncHARequests; uint64 UncHALocalRequests; uint64 UncPMMWrites; uint64 UncPMMReads; uint64 UncEDCFullWrites; uint64 UncEDCNormalReads; uint64 UncMCGTRequests; uint64 UncMCIARequests; uint64 UncMCIORequests; uint64 PackageEnergyStatus; uint64 DRAMEnergyStatus; uint64 TOROccupancyIAMiss; uint64 TORInsertsIAMiss; uint64 UncClocks; uint64 CStateResidency[PCM::MAX_C_STATE + 1]; void readAndAggregate(std::shared_ptr); public: UncoreCounterState() : UncMCFullWrites(0), UncMCNormalReads(0), UncHARequests(0), UncHALocalRequests(0), UncPMMWrites(0), UncPMMReads(0), UncEDCFullWrites(0), UncEDCNormalReads(0), UncMCGTRequests(0), UncMCIARequests(0), UncMCIORequests(0), PackageEnergyStatus(0), DRAMEnergyStatus(0), TOROccupancyIAMiss(0), TORInsertsIAMiss(0), UncClocks(0) { std::fill(CStateResidency, CStateResidency + PCM::MAX_C_STATE + 1, 0); } virtual ~UncoreCounterState() { } UncoreCounterState( const UncoreCounterState& ) = default; UncoreCounterState( UncoreCounterState&& ) = default; UncoreCounterState & operator = ( UncoreCounterState&& ) = default; UncoreCounterState & operator += (const UncoreCounterState & o) { UncMCFullWrites += o.UncMCFullWrites; UncMCNormalReads += o.UncMCNormalReads; UncHARequests += o.UncHARequests; UncHALocalRequests += o.UncHALocalRequests; UncPMMReads += o.UncPMMReads; UncPMMWrites += o.UncPMMWrites; UncEDCFullWrites += o.UncEDCFullWrites; UncEDCNormalReads += o.UncEDCNormalReads; UncMCGTRequests += o.UncMCGTRequests; UncMCIARequests += o.UncMCIARequests; UncMCIORequests += o.UncMCIORequests; PackageEnergyStatus += o.PackageEnergyStatus; DRAMEnergyStatus += o.DRAMEnergyStatus; TOROccupancyIAMiss += o.TOROccupancyIAMiss; TORInsertsIAMiss += o.TORInsertsIAMiss; UncClocks += o.UncClocks; for (int i = 0; i <= (int)PCM::MAX_C_STATE; ++i) CStateResidency[i] += o.CStateResidency[i]; return *this; } }; //! \brief Server uncore power counter state //! class ServerUncoreCounterState : public UncoreCounterState { public: enum { maxControllers = 4, maxChannels = 32, maxXPILinks = 6, maxCBOs = 128, maxMDFs = 128, maxIIOStacks = 16, maxCXLPorts = 6, maxCounters = 8 }; enum EventPosition { xPI_TxL0P_POWER_CYCLES = 0, xPI_L1_POWER_CYCLES = 2, xPI_CLOCKTICKS = 3 }; enum FreeRunningCounterID { ImcReads, ImcWrites, PMMReads, PMMWrites }; private: std::array, maxXPILinks> xPICounter; std::array, maxXPILinks> M3UPICounter; std::array, maxCBOs> CBOCounter; std::array, maxMDFs> MDFCounter; std::array, maxIIOStacks> IIOCounter; std::array, maxIIOStacks> IRPCounter; std::array, maxCXLPorts> CXLCMCounter; std::array, maxCXLPorts> CXLDPCounter; std::array UBOXCounter; std::array DRAMClocks; std::array HBMClocks; std::array, maxChannels> MCCounter; // channel X counter std::array, maxControllers> M2MCounter; // M2M/iMC boxes x counter std::array, maxChannels> EDCCounter; // EDC controller X counter std::array PCUCounter; std::unordered_map freeRunningCounter; int32 PackageThermalHeadroom; uint64 InvariantTSC; // invariant time stamp counter friend class PCM; template friend uint64 getDRAMClocks(uint32 channel, const CounterStateType & before, const CounterStateType & after); template friend uint64 getHBMClocks(uint32 channel, const CounterStateType & before, const CounterStateType & after); template friend uint64 getMCCounter(uint32 channel, uint32 counter, const CounterStateType & before, const CounterStateType & after); template friend uint64 getCXLCMCounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getCXLDPCounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getM3UPICounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getCBOCounter(uint32 cbo, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getMDFCounter(uint32 mdf, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getUBOXCounter(uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getIIOCounter(uint32 stack, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getIRPCounter(uint32 stack, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getXPICounter(uint32 port, uint32 counter, const CounterStateType& before, const CounterStateType& after); template friend uint64 getM2MCounter(uint32 controller, uint32 counter, const CounterStateType & before, const CounterStateType & after); template friend uint64 getEDCCounter(uint32 channel, uint32 counter, const CounterStateType & before, const CounterStateType & after); template friend uint64 getPCUCounter(uint32 counter, const CounterStateType & before, const CounterStateType & after); template friend uint64 getConsumedEnergy(const CounterStateType & before, const CounterStateType & after); template friend uint64 getDRAMConsumedEnergy(const CounterStateType & before, const CounterStateType & after); template friend uint64 getInvariantTSC(const CounterStateType & before, const CounterStateType & after); template friend int64 getFreeRunningCounter(const typename CounterStateType::FreeRunningCounterID &, const CounterStateType & before, const CounterStateType & after); template friend double getAverageFrequencyFromClocks(const int64 clocks, const CounterStateType& before, const CounterStateType& after); public: //! Returns current thermal headroom below TjMax int32 getPackageThermalHeadroom() const { return PackageThermalHeadroom; } ServerUncoreCounterState() : xPICounter{{}}, M3UPICounter{{}}, CBOCounter{{}}, MDFCounter{{}}, IIOCounter{{}}, IRPCounter{{}}, CXLCMCounter{{}}, CXLDPCounter{{}}, UBOXCounter{{}}, DRAMClocks{{}}, HBMClocks{{}}, MCCounter{{}}, M2MCounter{{}}, EDCCounter{{}}, PCUCounter{{}}, PackageThermalHeadroom(0), InvariantTSC(0) { } }; /*! \brief Returns QPI LL clock ticks \param port QPI port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getQPIClocks(uint32 port, const CounterStateType& before, const CounterStateType& after) { return getXPICounter(port, ServerUncoreCounterState::EventPosition::xPI_CLOCKTICKS, before, after); } /*! \brief Returns the number of QPI cycles in power saving half-lane mode \param port QPI port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getQPIL0pTxCycles(uint32 port, const CounterStateType& before, const CounterStateType& after) { return getXPICounter(port, ServerUncoreCounterState::EventPosition::xPI_TxL0P_POWER_CYCLES, before, after); } /*! \brief Returns the number of QPI cycles in power saving shutdown mode \param port QPI port number \param before CPU counter state before the experiment \param after CPU counter state after the experiment */ template uint64 getQPIL1Cycles(uint32 port, const CounterStateType& before, const CounterStateType& after) { return getXPICounter(port, ServerUncoreCounterState::EventPosition::xPI_L1_POWER_CYCLES, before, after); } //! \brief (Logical) core-wide counter state class CoreCounterState : public BasicCounterState { friend class PCM; public: CoreCounterState() = default; CoreCounterState( const CoreCounterState& ) = default; CoreCounterState( CoreCounterState&& ) = default; CoreCounterState & operator= ( CoreCounterState&& ) = default; virtual ~ CoreCounterState() {} }; //! \brief Socket-wide counter state class SocketCounterState : public BasicCounterState, public UncoreCounterState { friend class PCM; protected: void readAndAggregate(std::shared_ptr handle) { BasicCounterState::readAndAggregate(handle); UncoreCounterState::readAndAggregate(handle); } public: SocketCounterState& operator += ( const BasicCounterState& ccs ) { BasicCounterState::operator += ( ccs ); return *this; } SocketCounterState& operator += ( const UncoreCounterState& ucs ) { UncoreCounterState::operator += ( ucs ); return *this; } SocketCounterState() = default; SocketCounterState( const SocketCounterState& ) = default; SocketCounterState( SocketCounterState&& ) = default; SocketCounterState & operator = ( SocketCounterState&& ) = default; SocketCounterState & operator = ( UncoreCounterState&& ucs ) { UncoreCounterState::operator = ( std::move(ucs) ); return *this; } virtual ~ SocketCounterState() {} }; //! \brief System-wide counter state class SystemCounterState : public SocketCounterState { friend class PCM; friend std::vector getPCICFGEvent(const PCM::RawEventEncoding& eventEnc, const SystemCounterState& before, const SystemCounterState& after); std::vector > incomingQPIPackets; // each 64 byte std::vector > outgoingQPIFlits; // idle or data/non-data flits depending on the architecture std::vector > TxL0Cycles; uint64 uncoreTSC; std::unordered_map , PCM::PCICFGRegisterEncodingHash, PCM::PCICFGRegisterEncodingCmp> PCICFGValues{}; protected: void readAndAggregate(std::shared_ptr handle) { BasicCounterState::readAndAggregate(handle); UncoreCounterState::readAndAggregate(handle); } public: std::vector CXLWriteMem,CXLWriteCache; friend uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after); friend uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & now); friend double getOutgoingQPILinkUtilization(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after); friend uint64 getOutgoingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after); friend uint64 getOutgoingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & now); SystemCounterState() : uncoreTSC(0) { PCM * m = PCM::getInstance(); CXLWriteMem.resize(m->getNumSockets(),0); CXLWriteCache.resize(m->getNumSockets(),0); incomingQPIPackets.resize(m->getNumSockets(), std::vector((uint32)m->getQPILinksPerSocket(), 0)); outgoingQPIFlits.resize(m->getNumSockets(), std::vector((uint32)m->getQPILinksPerSocket(), 0)); TxL0Cycles.resize(m->getNumSockets(), std::vector((uint32)m->getQPILinksPerSocket(), 0)); } SystemCounterState( const SystemCounterState& ) = default; SystemCounterState( SystemCounterState&& ) = default; SystemCounterState & operator = ( SystemCounterState&& ) = default; SystemCounterState & operator += ( const SocketCounterState& scs ) { BasicCounterState::operator += ( scs ); UncoreCounterState::operator += ( scs ); return *this; } SystemCounterState & operator += ( const UncoreCounterState& ucs ) { UncoreCounterState::operator += ( ucs ); return *this; } virtual ~ SystemCounterState() {} }; /*! \brief Reads the counter state of the system Helper function. Uses PCM object to access counters. System consists of several sockets (CPUs). Socket has a CPU in it. Socket (CPU) consists of several (logical) cores. \return State of counters in the entire system */ PCM_API SystemCounterState getSystemCounterState(); /*! \brief Reads the counter state of a socket Helper function. Uses PCM object to access counters. \param socket socket id \return State of counters in the socket */ PCM_API SocketCounterState getSocketCounterState(uint32 socket); /*! \brief Reads the counter state of a (logical) core Helper function. Uses PCM object to access counters. \param core core id \return State of counters in the core */ PCM_API CoreCounterState getCoreCounterState(uint32 core); /*! \brief Computes average number of retired instructions per core cycle (IPC) \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return IPC */ template double getIPC(const CounterStateType & before, const CounterStateType & after) // instructions per cycle { int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; if (clocks != 0) return double(after.InstRetiredAny - before.InstRetiredAny) / double(clocks); return -1; } /*! \brief Computes the number of retired instructions \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return number of retired instructions */ template uint64 getInstructionsRetired(const CounterStateType & before, const CounterStateType & after) // instructions { return after.InstRetiredAny - before.InstRetiredAny; } /*! \brief Computes average number of retired instructions per time interval \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return usage */ template double getExecUsage(const CounterStateType & before, const CounterStateType & after) // usage { int64 timer_clocks = after.InvariantTSC - before.InvariantTSC; if (timer_clocks != 0) return double(after.InstRetiredAny - before.InstRetiredAny) / double(timer_clocks); return -1; } /*! \brief Computes the number of retired instructions \param now Current CPU counter state \return number of retired instructions */ template uint64 getInstructionsRetired(const CounterStateType & now) // instructions { return now.InstRetiredAny.getRawData_NoOverflowProtection(); } /*! \brief Computes the number core clock cycles when signal on a specific core is running (not halted) Returns number of used cycles (halted cyles are not counted). The counter does not advance in the following conditions: - an ACPI C-state is other than C0 for normal operation - HLT - STPCLK+ pin is asserted - being throttled by TM1 - during the frequency switching phase of a performance state transition The performance counter for this event counts across performance state transitions using different core clock frequencies \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return number core clock cycles */ template uint64 getCycles(const CounterStateType & before, const CounterStateType & after) // clocks { return after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; } /*! \brief Computes the number of reference clock cycles while clock signal on the core is running The reference clock operates at a fixed frequency, irrespective of core frequency changes due to performance state transitions. See Intel(r) Software Developer's Manual for more details \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return number core clock cycles */ template uint64 getRefCycles(const CounterStateType & before, const CounterStateType & after) // clocks { return after.CpuClkUnhaltedRef - before.CpuClkUnhaltedRef; } /*! \brief Computes the number executed core clock cycles Returns number of used cycles (halted cyles are not counted). \param now Current CPU counter state \return number core clock cycles */ template uint64 getCycles(const CounterStateType & now) // clocks { return now.CpuClkUnhaltedThread.getRawData_NoOverflowProtection(); } /*! \brief Computes average number of retired instructions per core cycle for the entire system combining instruction counts from logical cores to corresponding physical cores Use this metric to evaluate IPC improvement between SMT(Hyperthreading) on and SMT off. \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return IPC */ template inline double getCoreIPC(const CounterStateType & before, const CounterStateType & after) // instructions per cycle { double ipc = getIPC(before, after); PCM * m = PCM::getInstance(); if (ipc >= 0. && m && (m->getNumCores() == m->getNumOnlineCores())) return ipc * double(m->getThreadsPerCore()); return -1; } /*! \brief Computes average number of retired instructions per time interval for the entire system combining instruction counts from logical cores to corresponding physical cores Use this metric to evaluate cores utilization improvement between SMT(Hyperthreading) on and SMT off. \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return usage */ template inline double getTotalExecUsage(const CounterStateType & before, const CounterStateType & after) // usage { double usage = getExecUsage(before, after); PCM * m = PCM::getInstance(); if (usage >= 0. && m && (m->getNumCores() == m->getNumOnlineCores())) return usage * double(m->getThreadsPerCore()); return -1; } template double getAverageFrequencyFromClocks(const int64 clocks, const StateType& before, const StateType& after) // in Hz { const int64 timer_clocks = after.InvariantTSC - before.InvariantTSC; PCM* m = PCM::getInstance(); if (timer_clocks != 0 && m) return double(m->getNominalFrequency()) * double(clocks) / double(timer_clocks); return -1; } /*! \brief Computes average core frequency also taking Intel Turbo Boost technology into account \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return frequency in Hz */ template double getAverageFrequency(const CounterStateType & before, const CounterStateType & after) // in Hz { return getAverageFrequencyFromClocks(after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread, before, after); } /*! \brief Computes average uncore frequency \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return frequency in Hz */ template double getAverageUncoreFrequency(const UncoreStateType& before, const UncoreStateType & after) // in Hz { auto m = PCM::getInstance(); assert(m); return double(m->getNumOnlineCores()) * getAverageFrequencyFromClocks(after.UncClocks - before.UncClocks, before, after) / double(m->getNumOnlineSockets()); } /*! \brief Computes average core frequency when not in powersaving C0-state (also taking Intel Turbo Boost technology into account) \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return frequency in Hz */ template double getActiveAverageFrequency(const CounterStateType & before, const CounterStateType & after) // in Hz { int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; int64 ref_clocks = after.CpuClkUnhaltedRef - before.CpuClkUnhaltedRef; PCM * m = PCM::getInstance(); if (ref_clocks != 0 && m) return double(m->getNominalFrequency()) * double(clocks) / double(ref_clocks); return -1; } /*! \brief Computes average core frequency also taking Intel Turbo Boost technology into account \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Fraction of nominal frequency */ template double getRelativeFrequency(const CounterStateType & before, const CounterStateType & after) // fraction of nominal frequency { int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; int64 timer_clocks = after.InvariantTSC - before.InvariantTSC; if (timer_clocks != 0) return double(clocks) / double(timer_clocks); return -1; } /*! \brief Computes average core frequency when not in powersaving C0-state (also taking Intel Turbo Boost technology into account) \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Fraction of nominal frequency (if >1.0 then Turbo was working during the measurement) */ template double getActiveRelativeFrequency(const CounterStateType & before, const CounterStateType & after) // fraction of nominal frequency { if (!PCM::getInstance()->isActiveRelativeFrequencyAvailable()) return -1.; int64 clocks = after.CpuClkUnhaltedThread - before.CpuClkUnhaltedThread; int64 ref_clocks = after.CpuClkUnhaltedRef - before.CpuClkUnhaltedRef; if (ref_clocks != 0) return double(clocks) / double(ref_clocks); return -1; } /*! \brief Computes L2 cache hit ratio \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return value between 0 and 1 */ template double getL2CacheHitRatio(const CounterStateType& before, const CounterStateType& after) // 0.0 - 1.0 { auto* pcm = PCM::getInstance(); if (!pcm->isL2CacheHitRatioAvailable()) return 0; const auto hits = getL2CacheHits(before, after); if (pcm->memoryEventErrata()) { const auto all = after.Event[BasicCounterState::HSXL2RefPos] - before.Event[BasicCounterState::HSXL2RefPos]; if (all == 0ULL) return 0.; return double(hits) / double(all); } const auto misses = getL2CacheMisses(before, after); const auto all = double(hits + misses); if (all == 0.0) return 0.; return double(hits) / all; } /*! \brief Computes L3 cache hit ratio \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return value between 0 and 1 */ template double getL3CacheHitRatio(const CounterStateType& before, const CounterStateType& after) // 0.0 - 1.0 { if (!PCM::getInstance()->isL3CacheHitRatioAvailable()) return 0; const auto hits = getL3CacheHits(before, after); const auto misses = getL3CacheMisses(before, after); const auto all = double(hits + misses); if (all == 0.0) return 0.; return double(hits) / all; } /*! \brief Computes number of L3 cache misses \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return number of misses */ template uint64 getL3CacheMisses(const CounterStateType & before, const CounterStateType & after) { if (!PCM::getInstance()->isL3CacheMissesAvailable()) return 0; return after.Event[BasicCounterState::L3MissPos] - before.Event[BasicCounterState::L3MissPos]; } /*! \brief Computes number of L2 cache misses \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return number of misses */ template uint64 getL2CacheMisses(const CounterStateType & before, const CounterStateType & after) { auto pcm = PCM::getInstance(); if (pcm->isL2CacheMissesAvailable() == false) return 0ULL; const auto cpu_model = pcm->getCPUModel(); if (pcm->useSkylakeEvents() || cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::ADL || cpu_model == PCM::RPL) { return after.Event[BasicCounterState::SKLL2MissPos] - before.Event[BasicCounterState::SKLL2MissPos]; } else if (pcm->isAtom() || cpu_model == PCM::KNL) { return after.Event[BasicCounterState::ArchLLCMissPos] - before.Event[BasicCounterState::ArchLLCMissPos]; } else if (pcm->memoryEventErrata()) { return after.Event[BasicCounterState::ArchLLCRefPos] - before.Event[BasicCounterState::ArchLLCRefPos]; } uint64 L3Miss = after.Event[BasicCounterState::L3MissPos] - before.Event[BasicCounterState::L3MissPos]; uint64 L3UnsharedHit = after.Event[BasicCounterState::L3UnsharedHitPos] - before.Event[BasicCounterState::L3UnsharedHitPos]; uint64 L2HitM = after.Event[BasicCounterState::L2HitMPos] - before.Event[BasicCounterState::L2HitMPos]; return L2HitM + L3UnsharedHit + L3Miss; } /*! \brief Computes number of L2 cache hits \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return number of hits */ template uint64 getL2CacheHits(const CounterStateType & before, const CounterStateType & after) { auto pcm = PCM::getInstance(); if (pcm->isL2CacheHitsAvailable() == false) return 0ULL; if (pcm->isAtom() || pcm->getCPUModel() == PCM::KNL) { uint64 L2Miss = after.Event[BasicCounterState::ArchLLCMissPos] - before.Event[BasicCounterState::ArchLLCMissPos]; uint64 L2Ref = after.Event[BasicCounterState::ArchLLCRefPos] - before.Event[BasicCounterState::ArchLLCRefPos]; return L2Ref - L2Miss; } else if (pcm->memoryEventErrata()) { const auto all = after.Event[BasicCounterState::HSXL2RefPos] - before.Event[BasicCounterState::HSXL2RefPos]; const auto misses = after.Event[BasicCounterState::HSXL2MissPos] - before.Event[BasicCounterState::HSXL2MissPos]; const auto hits = (all > misses) ? (all - misses) : 0ULL; return hits; } return after.Event[BasicCounterState::L2HitPos] - before.Event[BasicCounterState::L2HitPos]; } /*! \brief Computes L3 Cache Occupancy */ template uint64 getL3CacheOccupancy(const CounterStateType & now) { if (PCM::getInstance()->L3CacheOccupancyMetricAvailable() == false) return 0ULL; return now.L3Occupancy; } /*! \brief Computes Local Memory Bandwidth */ template uint64 getLocalMemoryBW(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->CoreLocalMemoryBWMetricAvailable() == false) return 0ULL; return after.MemoryBWLocal - before.MemoryBWLocal; } /*! \brief Computes Remote Memory Bandwidth */ template uint64 getRemoteMemoryBW(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->CoreRemoteMemoryBWMetricAvailable() == false) return 0ULL; const uint64 total = after.MemoryBWTotal - before.MemoryBWTotal; const uint64 local = getLocalMemoryBW(before, after); if (total > local) return total - local; return 0; } /*! \brief Computes number of L3 cache hits where no snooping in sibling L2 caches had to be done \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return number of hits */ template uint64 getL3CacheHitsNoSnoop(const CounterStateType & before, const CounterStateType & after) { if (!PCM::getInstance()->isL3CacheHitsNoSnoopAvailable()) return 0; return after.Event[BasicCounterState::L3UnsharedHitPos] - before.Event[BasicCounterState::L3UnsharedHitPos]; } /*! \brief Computes number of L3 cache hits where snooping in sibling L2 caches had to be done \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return number of hits */ template uint64 getL3CacheHitsSnoop(const CounterStateType & before, const CounterStateType & after) { auto pcm = PCM::getInstance(); if (!pcm->isL3CacheHitsSnoopAvailable()) return 0; const auto cpu_model = pcm->getCPUModel(); if (cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::ADL || cpu_model == PCM::RPL) { const int64 misses = getL3CacheMisses(before, after); const int64 refs = after.Event[BasicCounterState::ArchLLCRefPos] - before.Event[BasicCounterState::ArchLLCRefPos]; const int64 hits = refs - misses; return (hits > 0)? hits : 0; } if (pcm->useSkylakeEvents()) { return after.Event[BasicCounterState::SKLL3HitPos] - before.Event[BasicCounterState::SKLL3HitPos]; } return after.Event[BasicCounterState::L2HitMPos] - before.Event[BasicCounterState::L2HitMPos]; } /*! \brief Computes total number of L3 cache hits \param before CPU counter state before the experiment \param after CPU counter state after the experiment \warning Works only in the DEFAULT_EVENTS programming mode (see program() method) \return number of hits */ template uint64 getL3CacheHits(const CounterStateType & before, const CounterStateType & after) { auto * pcm = PCM::getInstance(); assert(pcm); if (!pcm->isL3CacheHitsAvailable()) return 0; else if (pcm->memoryEventErrata()) { uint64 LLCMiss = after.Event[BasicCounterState::ArchLLCMissPos] - before.Event[BasicCounterState::ArchLLCMissPos]; uint64 LLCRef = after.Event[BasicCounterState::ArchLLCRefPos] - before.Event[BasicCounterState::ArchLLCRefPos]; return (LLCRef > LLCMiss) ? (LLCRef - LLCMiss) : 0ULL; } return getL3CacheHitsSnoop(before, after) + getL3CacheHitsNoSnoop(before, after); } /*! \brief Computes number of invariant time stamp counter ticks This counter counts irrespectively of C-, P- or T-states \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return number of time stamp counter ticks */ template uint64 getInvariantTSC(const CounterStateType & before, const CounterStateType & after) { return after.InvariantTSC - before.InvariantTSC; } /*! \brief Computes residency in the core C-state \param state C-state \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return residence ratio (0..1): 0 - 0%, 1.0 - 100% */ template inline double getCoreCStateResidency(int state, const CounterStateType & before, const CounterStateType & after) { const double tsc = double(getInvariantTSC(before, after)); if (state == 0) return double(getRefCycles(before, after)) / tsc; if (state == 1) { PCM * m = PCM::getInstance(); double result = 1.0 - double(getRefCycles(before, after)) / tsc; // 1.0 - cC0 for (int i = 2; i <= PCM::MAX_C_STATE; ++i) if (m->isCoreCStateResidencySupported(state)) result -= (after.BasicCounterState::CStateResidency[i] - before.BasicCounterState::CStateResidency[i]) / tsc; if (result < 0.) result = 0.; // fix counter dissynchronization else if (result > 1.) result = 1.; // fix counter dissynchronization return result; } return (after.BasicCounterState::CStateResidency[state] - before.BasicCounterState::CStateResidency[state]) / tsc; } /*! \brief Reads raw residency counter for the core C-state \param state C-state # \param now CPU counter state \return raw residency value */ template inline uint64 getCoreCStateResidency(int state, const CounterStateType& now) { if (state == 0) return now.CpuClkUnhaltedRef.getRawData_NoOverflowProtection(); return now.BasicCounterState::CStateResidency[state]; } /*! \brief Computes residency in the package C-state \param state C-state \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return residence ratio (0..1): 0 - 0%, 1.0 - 100% */ template inline double getPackageCStateResidency(int state, const CounterStateType & before, const CounterStateType & after) { const double tsc = double(getInvariantTSC(before, after)); if (state == 0) { PCM * m = PCM::getInstance(); double result = 1.0; for (int i = 1; i <= PCM::MAX_C_STATE; ++i) if (m->isPackageCStateResidencySupported(state)) result -= (after.UncoreCounterState::CStateResidency[i] - before.UncoreCounterState::CStateResidency[i]) / tsc; if (result < 0.) result = 0.; // fix counter dissynchronization else if (result > 1.) result = 1.; // fix counter dissynchronization return result; } return double(after.UncoreCounterState::CStateResidency[state] - before.UncoreCounterState::CStateResidency[state]) / tsc; } /*! \brief Reads raw residency counter for the package C-state \param state C-state # \param now CPU counter state \return raw residency value */ template inline uint64 getPackageCStateResidency(int state, const CounterStateType& now) { return now.UncoreCounterState::CStateResidency[state]; } /*! \brief Computes number of bytes read from DRAM memory controllers \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getBytesReadFromMC(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->memoryTrafficMetricsAvailable()) return (after.UncMCNormalReads - before.UncMCNormalReads) * 64; return 0ULL; } /*! \brief Computes number of bytes written to DRAM memory controllers \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getBytesWrittenToMC(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->memoryTrafficMetricsAvailable()) return (after.UncMCFullWrites - before.UncMCFullWrites) * 64; return 0ULL; } /*! \brief Computes number of bytes read from PMM memory \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getBytesReadFromPMM(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->PMMTrafficMetricsAvailable()) return (after.UncPMMReads - before.UncPMMReads) * 64; return 0ULL; } /*! \brief Computes number of bytes written to PMM memory \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getBytesWrittenToPMM(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->PMMTrafficMetricsAvailable()) return (after.UncPMMWrites - before.UncPMMWrites) * 64; return 0ULL; } /*! \brief Computes number of bytes read from HBM memory controllers \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getBytesReadFromEDC(const CounterStateType & before, const CounterStateType & after) { auto m = PCM::getInstance(); assert(m); if (m->HBMmemoryTrafficMetricsAvailable()) return (after.UncEDCNormalReads - before.UncEDCNormalReads) * m->getHBMCASTransferSize(); return 0ULL; } /*! \brief Computes number of bytes written to HBM memory controllers \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getBytesWrittenToEDC(const CounterStateType & before, const CounterStateType & after) { auto m = PCM::getInstance(); assert(m); if (m->HBMmemoryTrafficMetricsAvailable()) return (after.UncEDCFullWrites - before.UncEDCFullWrites) * m->getHBMCASTransferSize(); return 0ULL; } /*! \brief Computes number of bytes of read/write requests from GT engine \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getGTRequestBytesFromMC(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->memoryIOTrafficMetricAvailable()) return (after.UncMCGTRequests - before.UncMCGTRequests) * 64; return 0ULL; } /*! \brief Computes number of bytes of read/write requests from all IA \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getIARequestBytesFromMC(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->memoryIOTrafficMetricAvailable()) return (after.UncMCIARequests - before.UncMCIARequests) * 64; return 0ULL; } /*! \brief Computes number of bytes of read/write requests from all IO sources \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getIORequestBytesFromMC(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->memoryIOTrafficMetricAvailable()) return (after.UncMCIORequests - before.UncMCIORequests) * 64; return 0ULL; } /*! \brief Returns the number of occurred system management interrupts \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of SMIs (system manegement interrupts) */ template uint64 getSMICount(const CounterStateType & before, const CounterStateType & after) { return after.SMICount - before.SMICount; } /*! \brief Returns the number of occurred custom core events Read number of events programmed with the \c CUSTOM_CORE_EVENTS \param eventCounterNr Event/counter number (value from 0 to 3) \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ template uint64 getNumberOfCustomEvents(int32 eventCounterNr, const CounterStateType & before, const CounterStateType & after) { return after.Event[eventCounterNr] - before.Event[eventCounterNr]; } /*! \brief Computes number of bytes Writen from CXL Cache \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ //template inline uint64 getCXLWriteCacheBytes(uint32 socket,const SystemCounterState & before,const SystemCounterState & after) { return (after.CXLWriteCache[socket] - before.CXLWriteCache[socket]) * 64; } /*! \brief Computes number of bytes Writen from CXL Memory \param before CPU counter state before the experiment \param after CPU counter state after the experiment \return Number of bytes */ //template inline uint64 getCXLWriteMemBytes(uint32 socket, const SystemCounterState & before,const SystemCounterState & after) { return (after.CXLWriteMem[socket] - before.CXLWriteMem[socket]) * 64; } /*! \brief Get estimation of QPI data traffic per incoming QPI link Returns an estimation of number of data bytes transferred to a socket over Intel(r) Quick Path Interconnect \param socketNr socket identifier \param linkNr linkNr \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return Number of bytes */ inline uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) { if (!PCM::getInstance()->incomingQPITrafficMetricsAvailable()) return 0ULL; uint64 b = before.incomingQPIPackets[socketNr][linkNr]; uint64 a = after.incomingQPIPackets[socketNr][linkNr]; // prevent overflows due to counter dissynchronisation return (a > b) ? (64 * (a - b)) : 0; } /*! \brief Get data utilization of incoming QPI link (0..1) Returns an estimation of utilization of QPI link by data traffic transferred to a socket over Intel(r) Quick Path Interconnect \param socketNr socket identifier \param linkNr linkNr \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return utilization (0..1) */ inline double getIncomingQPILinkUtilization(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) { PCM * m = PCM::getInstance(); if (!(m->qpiUtilizationMetricsAvailable())) return 0.; const double bytes = (double)getIncomingQPILinkBytes(socketNr, linkNr, before, after); const uint64 max_speed = m->getQPILinkSpeed(socketNr, linkNr); const double max_bytes = (double)(double(max_speed) * double(getInvariantTSC(before, after) / double(m->getNumOnlineCores())) / double(m->getNominalFrequency())); return bytes / max_bytes; } /*! \brief Get utilization of outgoing QPI link (0..1) Returns an estimation of utilization of QPI link by (data+nondata) traffic transferred from a socket over Intel(r) Quick Path Interconnect \param socketNr socket identifier \param linkNr linkNr \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return utilization (0..1) */ inline double getOutgoingQPILinkUtilization(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) { PCM * m = PCM::getInstance(); if (m->outgoingQPITrafficMetricsAvailable() == false) return 0.; if (m->hasBecktonUncore()) { const uint64 b = before.outgoingQPIFlits[socketNr][linkNr]; // idle flits const uint64 a = after.outgoingQPIFlits[socketNr][linkNr]; // idle flits // prevent overflows due to counter dissynchronisation const double idle_flits = (double)((a > b) ? (a - b) : 0); const uint64 bTSC = before.uncoreTSC; const uint64 aTSC = after.uncoreTSC; const double tsc = (double)((aTSC > bTSC) ? (aTSC - bTSC) : 0); if (idle_flits >= tsc) return 0.; // prevent overflows due to potential counter dissynchronization return (1. - (idle_flits / tsc)); } else if (m->hasPCICFGUncore()) { const uint64 b = before.outgoingQPIFlits[socketNr][linkNr]; // data + non-data flits or idle (null) flits const uint64 a = after.outgoingQPIFlits[socketNr][linkNr]; // data + non-data flits or idle (null) flits // prevent overflows due to counter dissynchronisation double flits = (double)((a > b) ? (a - b) : 0); const double max_flits = ((double(getInvariantTSC(before, after)) * double(m->getQPILinkSpeed(socketNr, linkNr)) / m->getBytesPerFlit()) / double(m->getNominalFrequency())) / double(m->getNumOnlineCores()); if(m->hasUPI()) { flits = flits/3.; } if (flits > max_flits) return 1.; // prevent overflows due to potential counter dissynchronization return (flits / max_flits); } return 0; } /*! \brief Get estimation of QPI (data+nondata) traffic per outgoing QPI link Returns an estimation of number of data bytes transferred from a socket over Intel(r) Quick Path Interconnect \param socketNr socket identifier \param linkNr linkNr \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return Number of bytes */ inline uint64 getOutgoingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & before, const SystemCounterState & after) { PCM * m = PCM::getInstance(); if (!(m->outgoingQPITrafficMetricsAvailable())) return 0ULL; const double util = getOutgoingQPILinkUtilization(socketNr, linkNr, before, after); const double max_bytes = (double(m->getQPILinkSpeed(socketNr, linkNr)) * double(getInvariantTSC(before, after) / double(m->getNumOnlineCores())) / double(m->getNominalFrequency())); return (uint64)(max_bytes * util); } /*! \brief Get estimation of total QPI data traffic Returns an estimation of number of data bytes transferred to all sockets over all Intel(r) Quick Path Interconnect links \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return Number of bytes */ inline uint64 getAllIncomingQPILinkBytes(const SystemCounterState & before, const SystemCounterState & after) { PCM * m = PCM::getInstance(); const uint32 ns = m->getNumSockets(); const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); uint64 sum = 0; for (uint32 s = 0; s < ns; ++s) for (uint32 q = 0; q < qpiLinks; ++q) sum += getIncomingQPILinkBytes(s, q, before, after); return sum; } /*! \brief Get estimation of total QPI data+nondata traffic Returns an estimation of number of data and non-data bytes transferred from all sockets over all Intel(r) Quick Path Interconnect links \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return Number of bytes */ inline uint64 getAllOutgoingQPILinkBytes(const SystemCounterState & before, const SystemCounterState & after) { PCM * m = PCM::getInstance(); const uint32 ns = m->getNumSockets(); const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); uint64 sum = 0; for (uint32 s = 0; s < ns; ++s) for (uint32 q = 0; q < qpiLinks; ++q) sum += getOutgoingQPILinkBytes(s, q, before, after); return sum; } /*! \brief Return current value of the counter of QPI data traffic per incoming QPI link Returns the number of incoming data bytes to a socket over Intel(r) Quick Path Interconnect \param socketNr socket identifier \param linkNr linkNr \param now Current System CPU counter state \return Number of bytes */ inline uint64 getIncomingQPILinkBytes(uint32 socketNr, uint32 linkNr, const SystemCounterState & now) { if (PCM::getInstance()->incomingQPITrafficMetricsAvailable()) return 64 * now.incomingQPIPackets[socketNr][linkNr]; return 0ULL; } /*! \brief Get estimation of total QPI data traffic for this socket Returns an estimation of number of bytes transferred to this sockets over all Intel(r) Quick Path Interconnect links on this socket \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return Number of bytes */ inline uint64 getSocketIncomingQPILinkBytes(uint32 socketNr, const SystemCounterState & now) { PCM * m = PCM::getInstance(); const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); uint64 sum = 0; for (uint32 q = 0; q < qpiLinks; ++q) sum += getIncomingQPILinkBytes(socketNr, q, now); return sum; } /*! \brief Get estimation of Socket QPI data traffic Returns an estimation of number of data bytes transferred to all sockets over all Intel(r) Quick Path Interconnect links \param now System CPU counter state \return Number of bytes */ inline uint64 getAllIncomingQPILinkBytes(const SystemCounterState & now) { PCM * m = PCM::getInstance(); const uint32 ns = m->getNumSockets(); uint64 sum = 0; for (uint32 s = 0; s < ns; ++s) sum += getSocketIncomingQPILinkBytes(s, now); return sum; } /*! \brief Get QPI data to Memory Controller traffic ratio Ideally for NUMA-optmized programs the ratio should be close to 0. \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return Ratio */ inline double getQPItoMCTrafficRatio(const SystemCounterState & before, const SystemCounterState & after) { const uint64 totalQPI = getAllIncomingQPILinkBytes(before, after); uint64 memTraffic = getBytesReadFromMC(before, after) + getBytesWrittenToMC(before, after); if (PCM::getInstance()->PMMTrafficMetricsAvailable()) { memTraffic += getBytesReadFromPMM(before, after) + getBytesWrittenToPMM(before, after); } if (memTraffic == 0) return -1.; return double(totalQPI) / double(memTraffic); } /*! \brief Get local memory access ration measured in home agent \param before System CPU counter state before the experiment \param after System CPU counter state after the experiment \return Ratio */ template inline double getLocalMemoryRequestRatio(const CounterStateType & before, const CounterStateType & after) { if (PCM::getInstance()->localMemoryRequestRatioMetricAvailable() == false) return -1.; const auto all = after.UncHARequests - before.UncHARequests; const auto local = after.UncHALocalRequests - before.UncHALocalRequests; // std::cout << "PCM DEBUG "<< 64*all/1e6 << " " << 64*local/1e6 << "\n"; return double(local)/double(all); } //! \brief Returns the raw count of events //! \param before counter state before the experiment //! \param after counter state after the experiment template inline uint64 getNumberOfEvents(const CounterType & before, const CounterType & after) { return after.data - before.data; } //! \brief Returns average last level cache read+prefetch miss latency in ns template inline double getLLCReadMissLatency(const CounterStateType & before, const CounterStateType & after) { auto * m = PCM::getInstance(); if (m->LLCReadMissLatencyMetricsAvailable() == false) return -1.; const double occupancy = double(after.TOROccupancyIAMiss) - double(before.TOROccupancyIAMiss); const double inserts = double(after.TORInsertsIAMiss) - double(before.TORInsertsIAMiss); const double unc_clocks = double(after.UncClocks) - double(before.UncClocks); const double seconds = double(getInvariantTSC(before, after)) / double(m->getNumOnlineCores()/m->getNumSockets()) / double(m->getNominalFrequency()); return 1e9*seconds*(occupancy/inserts)/unc_clocks; } template inline uint64 getAllSlots(const CounterStateType & before, const CounterStateType & after) { const int64 a = after.BackendBoundSlots - before.BackendBoundSlots; const int64 b = after.FrontendBoundSlots - before.FrontendBoundSlots; const int64 c = after.BadSpeculationSlots - before.BadSpeculationSlots; const int64 d = after.RetiringSlots - before.RetiringSlots; // std::cout << "before DEBUG: " << before.FrontendBoundSlots << " " << before.BadSpeculationSlots << " "<< before.BackendBoundSlots << " " << before.RetiringSlots << std::endl; // std::cout << "after DEBUG: " << after.FrontendBoundSlots << " " << after.BadSpeculationSlots << " " << after.BackendBoundSlots << " " << after.RetiringSlots << std::endl; assert(a >= 0); assert(b >= 0); assert(c >= 0); assert(d >= 0); return a + b + c + d; } template inline uint64 getAllSlotsRaw(const CounterStateType& before, const CounterStateType& after) { return after.AllSlotsRaw - before.AllSlotsRaw; } //! \brief Returns unutilized pipeline slots where no uop was delivered due to lack of back-end resources as range 0..1 template inline double getBackendBound(const CounterStateType & before, const CounterStateType & after) { // std::cout << "DEBUG: "<< after.BackendBoundSlots - before.BackendBoundSlots << " " << getAllSlots(before, after) << std::endl; if (PCM::getInstance()->isHWTMAL1Supported()) return double(after.BackendBoundSlots - before.BackendBoundSlots)/double(getAllSlots(before, after)); return 0.; } //! \brief Returns unutilized pipeline slots where Front-end did not deliver a uop while back-end is ready as range 0..1 template inline double getFrontendBound(const CounterStateType & before, const CounterStateType & after) { // std::cout << "DEBUG: "<< after.FrontendBoundSlots - before.FrontendBoundSlots << " " << getAllSlots(before, after) << std::endl; if (PCM::getInstance()->isHWTMAL1Supported()) return double(after.FrontendBoundSlots - before.FrontendBoundSlots)/double(getAllSlots(before, after)); return 0.; } //! \brief Returns wasted pipeline slots due to incorrect speculation, covering whole penalty: Utilized by uops that do not retire, or Recovery Bubbles (unutilized slots) as range 0..1 template inline double getBadSpeculation(const CounterStateType & before, const CounterStateType & after) { // std::cout << "DEBUG: "<< after.BadSpeculationSlots - before.BadSpeculationSlots << " " << getAllSlots(before, after) << std::endl; if (PCM::getInstance()->isHWTMAL1Supported()) return double(after.BadSpeculationSlots - before.BadSpeculationSlots)/double(getAllSlots(before, after)); return 0.; } //! \brief Returns pipeline slots utilized by uops that eventually retire (commit) template inline double getRetiring(const CounterStateType & before, const CounterStateType & after) { // std::cout << "DEBUG: "<< after.RetiringSlots - before.RetiringSlots << " " << getAllSlots(before, after) << std::endl; if (PCM::getInstance()->isHWTMAL1Supported()) return double(after.RetiringSlots - before.RetiringSlots)/double(getAllSlots(before, after)); return 0.; } inline std::vector getPCICFGEvent(const PCM::RawEventEncoding & eventEnc, const SystemCounterState& before, const SystemCounterState& after) { std::vector result{}; auto beforeIter = before.PCICFGValues.find(eventEnc); auto afterIter = after.PCICFGValues.find(eventEnc); if (beforeIter != before.PCICFGValues.end() && afterIter != after.PCICFGValues.end()) { const auto& beforeValues = beforeIter->second; const auto& afterValues = afterIter->second; assert(beforeValues.size() == afterValues.size()); const size_t sz = beforeValues.size(); for (size_t i = 0; i < sz; ++i) { switch (eventEnc[PCM::PCICFGEventPosition::type]) { case PCM::MSRType::Freerun: result.push_back(afterValues[i] - beforeValues[i]); break; case PCM::MSRType::Static: result.push_back(afterValues[i]); break; } } } return result; } template uint64 getMSREvent(const uint64& index, const PCM::MSRType& type, const CounterStateType& before, const CounterStateType& after) { switch (type) { case PCM::MSRType::Freerun: { const auto beforeIt = before.MSRValues.find(index); const auto afterIt = after.MSRValues.find(index); if (beforeIt != before.MSRValues.end() && afterIt != after.MSRValues.end()) { return afterIt->second - beforeIt->second; } break; } case PCM::MSRType::Static: { const auto result = after.MSRValues.find(index); if (result != after.MSRValues.end()) { return result->second; } break; } } return 0ULL; } } // namespace pcm #endif pcm-202307/src/daemon/000077500000000000000000000000001445420033100144165ustar00rootroot00000000000000pcm-202307/src/daemon/.cproject000066400000000000000000000274101445420033100162340ustar00rootroot00000000000000 pcm-202307/src/daemon/.gitignore000066400000000000000000000002031445420033100164010ustar00rootroot00000000000000.project client/.project client/.cproject client/Debug/client daemon/.project daemon/.cproject daemon/Debug/daemon daemon/test/testpcm-202307/src/daemon/common.h000066400000000000000000000237431445420033100160700ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2018,2022 Intel Corporation // written by Steven Briscoe #ifndef COMMON_H_ #define COMMON_H_ #include #include static const char DEFAULT_SHM_ID_LOCATION[] = "/tmp/opcm-daemon-shm-id"; static const char VERSION[] = "2.0.0"; #define MAX_CPU_CORES 4096 #define MAX_SOCKETS 256 #define MEMORY_MAX_IMC_CHANNELS (12) #define MEMORY_READ 0 #define MEMORY_WRITE 1 #define QPI_MAX_LINKS (MAX_SOCKETS * 4) #define VERSION_SIZE 12 #define ALIGNMENT 64 #define ALIGN(x) __attribute__((aligned((x)))) namespace PCMDaemon { typedef int int32; typedef long int64; typedef unsigned int uint32; typedef unsigned long uint64; struct PCMSystem { uint32 numOfCores; // the number of logical cores in the system uint32 numOfOnlineCores; // the number of online logical cores in the system uint32 numOfSockets; // the number of CPU sockets in the system uint32 numOfOnlineSockets; // the number of online CPU sockets in the system uint32 numOfQPILinksPerSocket; // the number of QPI or UPI (xPI) links per socket public: PCMSystem() : numOfCores(0), numOfOnlineCores(0), numOfSockets(0), numOfOnlineSockets(0), numOfQPILinksPerSocket(0) {} } ALIGN(ALIGNMENT); typedef struct PCMSystem PCMSystem; struct PCMCoreCounter { uint64 coreId = 0; // core ID int32 socketId = 0; // socket ID double instructionsPerCycle = 0.; // instructions per cycle metric uint64 cycles = 0; // cpu cycle metric uint64 instructionsRetired = 0; // number of retired instructions metric double execUsage = 0.; // instructions per nominal CPU cycle, i.e. in respect to the CPU frequency ignoring turbo and power saving double relativeFrequency = 0.; // frequency relative to nominal CPU frequency (“clockticksâ€/â€invariant timer ticksâ€) double activeRelativeFrequency = 0.; // frequency relative to nominal CPU frequency excluding the time when the CPU is sleeping uint64 l3CacheMisses = 0; // L3 cache line misses uint64 l3CacheReference = 0; // L3 cache line references (accesses) uint64 l2CacheMisses = 0; // L2 cache line misses double l3CacheHitRatio = 0.; // L3 cache hit ratio double l2CacheHitRatio = 0.; // L2 cachhe hit ratio double l3CacheMPI = 0.; // number of L3 cache misses per retired instruction double l2CacheMPI = 0.; // number of L2 cache misses per retired instruction bool l3CacheOccupancyAvailable; // true if L3 cache occupancy metric is available uint64 l3CacheOccupancy; // L3 cache occupancy in KBytes bool localMemoryBWAvailable; // true if local memory bandwidth metric (L3 cache external bandwidth satisfied by local memory) is available uint64 localMemoryBW; // L3 cache external bandwidth satisfied by local memory (in MBytes) bool remoteMemoryBWAvailable; // true if remote memory bandwidth metric (L3 cache external bandwidth satisfied by remote memory) is available uint64 remoteMemoryBW; // L3 cache external bandwidth satisfied by remote memory (in MBytes) uint64 localMemoryAccesses = 0; // the number of local DRAM memory accesses uint64 remoteMemoryAccesses = 0; // the number of remote DRAM memory accesses int32 thermalHeadroom = 0; // thermal headroom in Kelvin (max design temperature – current temperature) public: PCMCoreCounter() : l3CacheOccupancyAvailable(false), l3CacheOccupancy(0), localMemoryBWAvailable(false), localMemoryBW(0), remoteMemoryBWAvailable(false), remoteMemoryBW(0) {} } ALIGN(ALIGNMENT); typedef struct PCMCoreCounter PCMCoreCounter; struct PCMCore { PCMCoreCounter cores[MAX_CPU_CORES]; bool packageEnergyMetricsAvailable; // true if CPU package (a.k.a. socket) energy metric is available double energyUsedBySockets[MAX_SOCKETS] ALIGN(ALIGNMENT); // energy consumed/used by CPU (socket) in Joules public: PCMCore() : packageEnergyMetricsAvailable(false) { for (int i = 0; i < MAX_SOCKETS; ++i) { energyUsedBySockets[i] = -1.0; } } } ALIGN(ALIGNMENT); typedef struct PCMCore PCMCore; struct PCMMemoryChannelCounter { float read; // DRAM read traffic in MBytes/sec float write; // DRAM write traffic in MBytes/sec float total; // total traffic in MBytes/sec public: PCMMemoryChannelCounter() : read(-1.0), write(-1.0), total(-1.0) {} } ALIGN(ALIGNMENT); typedef struct PCMMemoryChannelCounter PCMMemoryChannelCounter; struct PCMMemorySocketCounter { uint64 socketId = 0; // socket ID PCMMemoryChannelCounter channels[MEMORY_MAX_IMC_CHANNELS]; uint32 numOfChannels; // number of memory channels in the CPU socket float read; // DRAM read traffic in MBytes/sec float write; // DRAM write traffic in MBytes/sec float pmmRead; // PMM read traffic in MBytes/sec float pmmWrite; // PMM write traffic in MBytes/sec float total; // total traffic in MBytes/sec float pmmMemoryModeHitRate; // PMM memory mode hit rate estimation. Metric value range is [0..1] double dramEnergy; // energy consumed/used by DRAM memory in Joules public: PCMMemorySocketCounter() : numOfChannels(0), read(-1.0), write(-1.0), pmmRead(-1.0), pmmWrite(-1.0), total(-1.0), pmmMemoryModeHitRate(-1.0), dramEnergy(0.0) {} } ALIGN(ALIGNMENT); typedef struct PCMMemorySocketCounter PCMMemorySocketCounter; struct PCMMemorySystemCounter { float read; // DRAM read traffic in MBytes/sec float write; // DRAM write traffic in MBytes/sec float pmmRead; // PMM read traffic in MBytes/sec float pmmWrite; // PMM write traffic in MBytes/sec float total; // total traffic in MBytes/sec public: PCMMemorySystemCounter() : read(-1.0), write(-1.0), pmmRead(-1.0), pmmWrite(-1.0), total(-1.0) {} } ALIGN(ALIGNMENT); typedef struct PCMMemorySystemCounter PCMMemorySystemCounter; struct PCMMemory { PCMMemorySocketCounter sockets[MAX_SOCKETS]; PCMMemorySystemCounter system; bool dramEnergyMetricsAvailable; // true if DRAM energy metrics are available bool pmmMetricsAvailable; // true if PMM metrics are available public: PCMMemory() : dramEnergyMetricsAvailable(false), pmmMetricsAvailable(false) {} } ALIGN(ALIGNMENT); typedef struct PCMMemory PCMMemory; struct PCMQPILinkCounter { uint64 bytes; // bytes of certain traffic class transferred over QPI or UPI link double utilization; // utilization of the link caused by the certain traffic class public: PCMQPILinkCounter() : bytes(0), utilization(-1.0) {} } ALIGN(ALIGNMENT); typedef struct PCMQPILinkCounter PCMQPILinkCounter; struct PCMQPISocketCounter { uint64 socketId = 0; // socket ID PCMQPILinkCounter links[QPI_MAX_LINKS]; uint64 total; // total number of transferred bytes of a certain traffic class public: PCMQPISocketCounter() : total(0) {} } ALIGN(ALIGNMENT); typedef struct PCMQPISocketCounter PCMQPISocketCounter; struct PCMQPI { PCMQPISocketCounter incoming[MAX_SOCKETS]; // incoming data traffic class statistics uint64 incomingTotal; // incoming data traffic total bytes PCMQPISocketCounter outgoing[MAX_SOCKETS]; // outgoing data+"non-data" traffic class statistics uint64 outgoingTotal; // outgoing data+"non-data" traffic total bytes bool incomingQPITrafficMetricsAvailable; // true if incoming data traffic class statistics metrics are available bool outgoingQPITrafficMetricsAvailable; // true if outgoing data+"non-data" class statistics metrics are available public: PCMQPI() : incomingTotal(0), outgoingTotal(0), incomingQPITrafficMetricsAvailable(false), outgoingQPITrafficMetricsAvailable(false) {} } ALIGN(ALIGNMENT); typedef struct PCMQPI PCMQPI; struct SharedPCMCounters { PCMSystem system; PCMCore core; PCMMemory memory; PCMQPI qpi; } ALIGN(ALIGNMENT); typedef struct SharedPCMCounters SharedPCMCounters; struct SharedPCMState { char version[VERSION_SIZE]; // version (null-terminated string) uint64 lastUpdateTscBegin; // time stamp counter (TSC) obtained via rdtsc instruction *before* the state update uint64 timestamp; // monotonic time since some unspecified starting point in nanoseconds *after* the state update uint64 cyclesToGetPCMState; // time it took to update the state measured in TSC cycles uint32 pollMs; // the poll interval in shared memory in milliseconds SharedPCMCounters pcm; uint64 lastUpdateTscEnd; // time stamp counter (TSC) obtained via rdtsc instruction *after* the state update public: SharedPCMState() : lastUpdateTscBegin(0), timestamp(0), cyclesToGetPCMState(0), pollMs(-1), lastUpdateTscEnd(0) { std::fill(this->version, this->version + VERSION_SIZE, 0); } } ALIGN(ALIGNMENT); typedef struct SharedPCMState SharedPCMState; } #endif /* COMMON_H_ */ pcm-202307/src/daemon/daemon.cpp000066400000000000000000000706771445420033100164060ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2018,2022 Intel Corporation // written by Steven Briscoe #include #include #include #include #include #include #include #include #include #include #include #ifndef CLOCK_MONOTONIC_RAW #define CLOCK_MONOTONIC_RAW (4) /* needed for SLES11 */ #endif #include "daemon.h" #include "common.h" #include "pcm.h" namespace PCMDaemon { std::string Daemon::shmIdLocation_; int Daemon::sharedMemoryId_; SharedPCMState* Daemon::sharedPCMState_; Daemon::Daemon(int argc, char* argv[]) : debugMode_(false), pollIntervalMs_(0), groupName_(""), mode_(Mode::DIFFERENCE), pcmInstance_(NULL) { allowedSubscribers_.push_back("core"); allowedSubscribers_.push_back("memory"); allowedSubscribers_.push_back("qpi"); shmIdLocation_ = std::string(DEFAULT_SHM_ID_LOCATION); sharedMemoryId_ = 0; sharedPCMState_ = NULL; readApplicationArguments(argc, argv); setupSharedMemory(); setupPCM(); assert(sharedPCMState_); //Put the poll interval in shared memory so that the client knows sharedPCMState_->pollMs = pollIntervalMs_; collectionTimeAfter_ = 0; updatePCMState(&systemStatesBefore_, &socketStatesBefore_, &coreStatesBefore_, collectionTimeBefore_); systemStatesForQPIBefore_ = SystemCounterState(systemStatesBefore_); serverUncoreCounterStatesBefore_ = new ServerUncoreCounterState[pcmInstance_->getNumSockets()]; serverUncoreCounterStatesAfter_ = new ServerUncoreCounterState[pcmInstance_->getNumSockets()]; } int Daemon::run() { std::cout << "\n**** PCM Daemon Started *****\n"; while (true) { if (debugMode_) { time_t rawtime; struct tm timeinfo; char timeBuffer[200]; time(&rawtime); localtime_r(&rawtime, &timeinfo); snprintf(timeBuffer, 200, "[%02d %02d %04d %02d:%02d:%02d]", timeinfo.tm_mday, timeinfo.tm_mon + 1, timeinfo.tm_year + 1900, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); std::cout << timeBuffer << "\tFetching counters...\n"; } // Here to make sure that any output elsewhere in this class or its callees is flushed before the sleep std::cout << std::flush; usleep(pollIntervalMs_ * 1000); getPCMCounters(); } return EXIT_SUCCESS; } Daemon::~Daemon() { delete[] serverUncoreCounterStatesBefore_; delete[] serverUncoreCounterStatesAfter_; } void Daemon::setupPCM() { pcmInstance_ = PCM::getInstance(); pcmInstance_->setBlocked(false); set_signal_handlers(); set_post_cleanup_callback(&Daemon::cleanup); checkAccessAndProgramPCM(); } void Daemon::checkAccessAndProgramPCM() { PCM::ErrorCode status; if (subscribers_.find("core") != subscribers_.end()) { EventSelectRegister defEventSelectRegister; defEventSelectRegister.value = 0; defEventSelectRegister.fields.usr = 1; defEventSelectRegister.fields.os = 1; defEventSelectRegister.fields.enable = 1; uint32 numOfCustomCounters = 4; EventSelectRegister regs[numOfCustomCounters]; PCM::ExtendedCustomCoreEventDescription conf; conf.nGPCounters = numOfCustomCounters; conf.gpCounterCfg = regs; try { pcmInstance_->setupCustomCoreEventsForNuma(conf); } catch (UnsupportedProcessorException& e) { std::cerr << "\nPCM daemon does not support your processor currently.\n\n"; exit(EXIT_FAILURE); } // Set default values for event select registers for (uint32 i(0); i < numOfCustomCounters; ++i) regs[i] = defEventSelectRegister; regs[0].fields.event_select = 0xB7; // OFFCORE_RESPONSE 0 event regs[0].fields.umask = 0x01; regs[1].fields.event_select = 0xBB; // OFFCORE_RESPONSE 1 event regs[1].fields.umask = 0x01; regs[2].fields.event_select = ARCH_LLC_MISS_EVTNR; regs[2].fields.umask = ARCH_LLC_MISS_UMASK; regs[3].fields.event_select = ARCH_LLC_REFERENCE_EVTNR; regs[3].fields.umask = ARCH_LLC_REFERENCE_UMASK; if (pcmInstance_->getMaxCustomCoreEvents() == 3) { conf.nGPCounters = 2; // drop LLC metrics } status = pcmInstance_->program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf); } else { status = pcmInstance_->program(); } pcmInstance_->checkError(status); } void Daemon::readApplicationArguments(int argc, char* argv[]) { int opt; int counterCount(0); if (argc == 1) { printExampleUsageAndExit(argv); } std::cout << "\n"; while ((opt = getopt(argc, argv, "p:c:dg:m:s:")) != -1) { switch (opt) { case 'p': pollIntervalMs_ = atoi(optarg); std::cout << "Polling every " << pollIntervalMs_ << "ms\n"; break; case 'c': { std::string subscriber(optarg); if (subscriber == "all") { for (std::vector::const_iterator it = allowedSubscribers_.begin(); it != allowedSubscribers_.end(); ++it) { subscribers_.insert(std::pair(*it, 1)); ++counterCount; } } else { if (std::find(allowedSubscribers_.begin(), allowedSubscribers_.end(), subscriber) == allowedSubscribers_.end()) { printExampleUsageAndExit(argv); } subscribers_.insert(std::pair(subscriber, 1)); ++counterCount; } std::cout << "Listening to '" << subscriber << "' counters\n"; } break; case 'd': debugMode_ = true; std::cout << "Debug mode enabled\n"; break; case 'g': { groupName_ = std::string(optarg); std::cout << "Restricting to group: " << groupName_ << "\n"; } break; case 'm': { std::string mode = std::string(optarg); std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower); if (mode == "difference") { mode_ = Mode::DIFFERENCE; } else if (mode == "absolute") { mode_ = Mode::ABSOLUTE; } else { printExampleUsageAndExit(argv); } std::cout << "Operational mode: " << mode_ << " ("; if (mode_ == Mode::DIFFERENCE) std::cout << "difference"; else if (mode_ == Mode::ABSOLUTE) std::cout << "absolute"; std::cout << ")\n"; } break; case 's': { shmIdLocation_ = std::string(optarg); std::cout << "Shared memory ID location: " << shmIdLocation_ << "\n"; } break; default: printExampleUsageAndExit(argv); break; } } if (pollIntervalMs_ <= 0 || counterCount == 0) { printExampleUsageAndExit(argv); } std::cout << "PCM Daemon version: " << VERSION << "\n\n"; } void Daemon::printExampleUsageAndExit(char* argv[]) { std::cerr << "\n"; std::cerr << "-------------------------------------------------------------------\n"; std::cerr << "Example usage: " << argv[0] << " -p 50 -c numa -c memory\n"; std::cerr << "Poll every 50ms. Fetch counters for numa and memory\n\n"; std::cerr << "Example usage: " << argv[0] << " -p 250 -c all -g pcm -m absolute\n"; std::cerr << "Poll every 250ms. Fetch all counters (core, numa & memory).\n"; std::cerr << "Restrict access to user group 'pcm'. Store absolute values on each poll interval\n\n"; std::cerr << "-p for poll frequency\n"; std::cerr << "-c to request specific counters (Allowed counters: all "; for (std::vector::const_iterator it = allowedSubscribers_.begin(); it != allowedSubscribers_.end(); ++it) { std::cerr << *it; if (it + 1 != allowedSubscribers_.end()) { std::cerr << " "; } } std::cerr << ")"; std::cerr << "\n-d flag for debug output [optional]\n"; std::cerr << "-g to restrict access to group [optional]\n"; std::cerr << "-m stores differences or absolute values (Allowed: difference absolute) Default: difference [optional]\n"; std::cerr << "-s to store shared memory ID Default: " << std::string(DEFAULT_SHM_ID_LOCATION) << " [optional]\n"; std::cerr << "\n"; exit(EXIT_FAILURE); } void Daemon::setupSharedMemory() { int mode = 0660; int shmFlag = IPC_CREAT | mode; sharedMemoryId_ = shmget(IPC_PRIVATE, sizeof(SharedPCMState), shmFlag); if (sharedMemoryId_ < 0) { std::cerr << "Failed to allocate shared memory segment (errno=" << errno << ")\n"; exit(EXIT_FAILURE); } //Store shm id in a file (shmIdLocation_) int success = remove(shmIdLocation_.c_str()); if (success != 0) { std::cerr << "Failed to delete shared memory id location: " << shmIdLocation_ << " (errno=" << errno << ")\n"; } FILE* fp = fopen(shmIdLocation_.c_str(), "w"); if (!fp) { std::cerr << "Failed to create/write to shared memory key location: " << shmIdLocation_ << "\n"; exit(EXIT_FAILURE); } fprintf(fp, "%i", sharedMemoryId_); fclose(fp); if (groupName_.size() > 0) { ushort gid = (ushort)resolveGroupName(groupName_); struct shmid_ds shmData; shmData.shm_perm.gid = gid; shmData.shm_perm.mode = mode; success = shmctl(sharedMemoryId_, IPC_SET, &shmData); if (success < 0) { std::cerr << "Failed to IPC_SET (errno=" << errno << ")\n"; exit(EXIT_FAILURE); } //Change group of shared memory ID file uid_t uid = geteuid(); success = chown(shmIdLocation_.c_str(), uid, gid); if (success < 0) { std::cerr << "Failed to change ownership of shared memory key location: " << shmIdLocation_ << "\n"; exit(EXIT_FAILURE); } } sharedPCMState_ = (SharedPCMState*)shmat(sharedMemoryId_, NULL, 0); if (sharedPCMState_ == (void*)-1) { std::cerr << "Failed to attach shared memory segment (errno=" << errno << ")\n"; exit(EXIT_FAILURE); } //Clear out shared memory sharedPCMState_ = new (sharedPCMState_) SharedPCMState(); // use placement new operator } gid_t Daemon::resolveGroupName(const std::string& groupName) { struct group* group = getgrnam(groupName.c_str()); if (group == NULL) { std::cerr << "Failed to resolve group '" << groupName << "'\n"; exit(EXIT_FAILURE); } return group->gr_gid; } void Daemon::getPCMCounters() { std::copy(VERSION, VERSION + sizeof(VERSION), sharedPCMState_->version); sharedPCMState_->version[sizeof(VERSION)] = '\0'; sharedPCMState_->lastUpdateTscBegin = RDTSC(); updatePCMState(&systemStatesAfter_, &socketStatesAfter_, &coreStatesAfter_, collectionTimeAfter_); getPCMSystem(); if (subscribers_.find("core") != subscribers_.end()) { getPCMCore(); } if (subscribers_.find("memory") != subscribers_.end()) { getPCMMemory(); } bool fetchQPICounters = subscribers_.find("qpi") != subscribers_.end(); if (fetchQPICounters) { getPCMQPI(); } const auto lastUpdateTscEnd = RDTSC(); sharedPCMState_->cyclesToGetPCMState = lastUpdateTscEnd - sharedPCMState_->lastUpdateTscBegin; sharedPCMState_->timestamp = getTimestamp(); // As the client polls this timestamp (lastUpdateTsc) // All the data has to be in shm before sharedPCMState_->lastUpdateTscEnd = lastUpdateTscEnd; if (mode_ == Mode::DIFFERENCE) { swapPCMBeforeAfterState(); } if (fetchQPICounters) { systemStatesForQPIBefore_ = SystemCounterState(systemStatesAfter_); } std::swap(collectionTimeBefore_, collectionTimeAfter_); } void Daemon::updatePCMState(SystemCounterState* systemStates, std::vector* socketStates, std::vector* coreStates, uint64& t) { if (subscribers_.find("core") != subscribers_.end()) { pcmInstance_->getAllCounterStates(*systemStates, *socketStates, *coreStates); } else { if (subscribers_.find("memory") != subscribers_.end() || subscribers_.find("qpi") != subscribers_.end()) { pcmInstance_->getUncoreCounterStates(*systemStates, *socketStates); } } t = pcmInstance_->getTickCount(); } void Daemon::swapPCMBeforeAfterState() { //After state now becomes before state (for the next iteration) std::swap(coreStatesBefore_, coreStatesAfter_); std::swap(socketStatesBefore_, socketStatesAfter_); std::swap(systemStatesBefore_, systemStatesAfter_); std::swap(serverUncoreCounterStatesBefore_, serverUncoreCounterStatesAfter_); } void Daemon::getPCMSystem() { PCMSystem& system = sharedPCMState_->pcm.system; system.numOfCores = pcmInstance_->getNumCores(); system.numOfOnlineCores = pcmInstance_->getNumOnlineCores(); system.numOfSockets = pcmInstance_->getNumSockets(); system.numOfOnlineSockets = pcmInstance_->getNumOnlineSockets(); system.numOfQPILinksPerSocket = pcmInstance_->getQPILinksPerSocket(); } void Daemon::getPCMCore() { PCMCore& core = sharedPCMState_->pcm.core; const uint32 numCores = sharedPCMState_->pcm.system.numOfCores; uint32 onlineCoresI(0); for (uint32 coreI(0); coreI < numCores; ++coreI) { if (!pcmInstance_->isCoreOnline(coreI)) continue; PCMCoreCounter& coreCounters = core.cores[onlineCoresI]; int32 socketId = pcmInstance_->getSocketId(coreI); double instructionsPerCycle = getIPC(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); uint64 cycles = getCycles(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); uint64 instructionsRetired = getInstructionsRetired(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); double execUsage = getExecUsage(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); double relativeFrequency = getRelativeFrequency(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); double activeRelativeFrequency = getActiveRelativeFrequency(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); uint64 l3CacheMisses = getNumberOfCustomEvents(2, coreStatesBefore_[coreI], coreStatesAfter_[coreI]); uint64 l3CacheReference = getNumberOfCustomEvents(3, coreStatesBefore_[coreI], coreStatesAfter_[coreI]); uint64 l2CacheMisses = getL2CacheMisses(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); double l3CacheHitRatio = getL3CacheHitRatio(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); double l2CacheHitRatio = getL2CacheHitRatio(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); double l3CacheMPI = double(l3CacheMisses) / instructionsRetired; double l2CacheMPI = double(l2CacheMisses) / instructionsRetired; int32 thermalHeadroom = coreStatesAfter_[coreI].getThermalHeadroom(); coreCounters.coreId = coreI; coreCounters.socketId = socketId; coreCounters.instructionsPerCycle = instructionsPerCycle; coreCounters.cycles = cycles; coreCounters.instructionsRetired = instructionsRetired; coreCounters.execUsage = execUsage; coreCounters.relativeFrequency = relativeFrequency; coreCounters.activeRelativeFrequency = activeRelativeFrequency; coreCounters.l3CacheMisses = l3CacheMisses; coreCounters.l3CacheReference = l3CacheReference; coreCounters.l2CacheMisses = l2CacheMisses; coreCounters.l3CacheHitRatio = l3CacheHitRatio; coreCounters.l2CacheHitRatio = l2CacheHitRatio; coreCounters.l3CacheMPI = l3CacheMPI; coreCounters.l2CacheMPI = l2CacheMPI; coreCounters.thermalHeadroom = thermalHeadroom; coreCounters.l3CacheOccupancyAvailable = pcmInstance_->L3CacheOccupancyMetricAvailable(); if (coreCounters.l3CacheOccupancyAvailable) { uint64 l3CacheOccupancy = getL3CacheOccupancy(coreStatesAfter_[coreI]); coreCounters.l3CacheOccupancy = l3CacheOccupancy; } coreCounters.localMemoryBWAvailable = pcmInstance_->CoreLocalMemoryBWMetricAvailable(); if (coreCounters.localMemoryBWAvailable) { uint64 localMemoryBW = getLocalMemoryBW(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); coreCounters.localMemoryBW = localMemoryBW; } coreCounters.remoteMemoryBWAvailable = pcmInstance_->CoreRemoteMemoryBWMetricAvailable(); if (coreCounters.remoteMemoryBWAvailable) { uint64 remoteMemoryBW = getRemoteMemoryBW(coreStatesBefore_[coreI], coreStatesAfter_[coreI]); coreCounters.remoteMemoryBW = remoteMemoryBW; } coreCounters.localMemoryAccesses = getNumberOfCustomEvents(0, coreStatesBefore_[coreI], coreStatesAfter_[coreI]); coreCounters.remoteMemoryAccesses = getNumberOfCustomEvents(1, coreStatesBefore_[coreI], coreStatesAfter_[coreI]); ++onlineCoresI; } const uint32 numSockets = sharedPCMState_->pcm.system.numOfSockets; core.packageEnergyMetricsAvailable = pcmInstance_->packageEnergyMetricsAvailable(); if (core.packageEnergyMetricsAvailable) { for (uint32 i(0); i < numSockets; ++i) { core.energyUsedBySockets[i] = getConsumedJoules(socketStatesBefore_[i], socketStatesAfter_[i]); } } } void Daemon::getPCMMemory() { pcmInstance_->disableJKTWorkaround(); PCMMemory& memory = sharedPCMState_->pcm.memory; memory.dramEnergyMetricsAvailable = pcmInstance_->dramEnergyMetricsAvailable(); memory.pmmMetricsAvailable = pcmInstance_->PMMTrafficMetricsAvailable(); const uint32 numSockets = sharedPCMState_->pcm.system.numOfSockets; for (uint32 i(0); i < numSockets; ++i) { serverUncoreCounterStatesAfter_[i] = pcmInstance_->getServerUncoreCounterState(i); } uint64 elapsedTime = collectionTimeAfter_ - collectionTimeBefore_; float iMC_Rd_socket_chan[MAX_SOCKETS][MEMORY_MAX_IMC_CHANNELS]; float iMC_Wr_socket_chan[MAX_SOCKETS][MEMORY_MAX_IMC_CHANNELS]; float iMC_Rd_socket[MAX_SOCKETS]; float iMC_Wr_socket[MAX_SOCKETS]; float iMC_PMM_Rd_socket[MAX_SOCKETS]; float iMC_PMM_Wr_socket[MAX_SOCKETS]; for (uint32 skt(0); skt < numSockets; ++skt) { iMC_Rd_socket[skt] = 0.0; iMC_Wr_socket[skt] = 0.0; iMC_PMM_Rd_socket[skt] = 0.0; iMC_PMM_Wr_socket[skt] = 0.0; auto toBW = [&elapsedTime](const uint64 bytes) { return (float)(bytes / 1000000.0 / (elapsedTime / 1000.0)); }; if (memory.pmmMetricsAvailable) { iMC_PMM_Rd_socket[skt] = toBW(getBytesReadFromPMM(socketStatesBefore_[skt], socketStatesAfter_[skt])); iMC_PMM_Wr_socket[skt] = toBW(getBytesWrittenToPMM(socketStatesBefore_[skt], socketStatesAfter_[skt])); } for (uint32 channel(0); channel < MEMORY_MAX_IMC_CHANNELS; ++channel) { //In case of JKT-EN, there are only three channels. Skip one and continue. const bool memoryReadNotAvailable = getMCCounter(channel, MEMORY_READ, serverUncoreCounterStatesBefore_[skt], serverUncoreCounterStatesAfter_[skt]) == 0; const bool memoryWriteNotAvailable = getMCCounter(channel, MEMORY_WRITE, serverUncoreCounterStatesBefore_[skt], serverUncoreCounterStatesAfter_[skt]) == 0; if (memoryReadNotAvailable && memoryWriteNotAvailable) { iMC_Rd_socket_chan[skt][channel] = -1.0; iMC_Wr_socket_chan[skt][channel] = -1.0; continue; } iMC_Rd_socket_chan[skt][channel] = (float)(toBW(getMCCounter(channel, MEMORY_READ, serverUncoreCounterStatesBefore_[skt], serverUncoreCounterStatesAfter_[skt]) * 64)); iMC_Wr_socket_chan[skt][channel] = (float)(toBW(getMCCounter(channel, MEMORY_WRITE, serverUncoreCounterStatesBefore_[skt], serverUncoreCounterStatesAfter_[skt]) * 64)); iMC_Rd_socket[skt] += iMC_Rd_socket_chan[skt][channel]; iMC_Wr_socket[skt] += iMC_Wr_socket_chan[skt][channel]; } } float systemRead(0.0); float systemWrite(0.0); float systemPMMRead(0.0); float systemPMMWrite(0.0); uint32 onlineSocketsI(0); for (uint32 skt(0); skt < numSockets; ++skt) { if (!pcmInstance_->isSocketOnline(skt)) continue; uint64 currentChannelI(0); for (uint64 channel(0); channel < MEMORY_MAX_IMC_CHANNELS; ++channel) { //If the channel read neg. value, the channel is not working; skip it. if (iMC_Rd_socket_chan[skt][channel] < 0.0 && iMC_Wr_socket_chan[skt][channel] < 0.0) continue; const float socketChannelRead = iMC_Rd_socket_chan[skt][channel]; const float socketChannelWrite = iMC_Wr_socket_chan[skt][channel]; memory.sockets[onlineSocketsI].channels[currentChannelI].read = socketChannelRead; memory.sockets[onlineSocketsI].channels[currentChannelI].write = socketChannelWrite; memory.sockets[onlineSocketsI].channels[currentChannelI].total = socketChannelRead + socketChannelWrite; ++currentChannelI; } memory.sockets[onlineSocketsI].socketId = skt; memory.sockets[onlineSocketsI].numOfChannels = currentChannelI; memory.sockets[onlineSocketsI].read = iMC_Rd_socket[skt]; memory.sockets[onlineSocketsI].write = iMC_Wr_socket[skt]; memory.sockets[onlineSocketsI].pmmRead = iMC_PMM_Rd_socket[skt]; memory.sockets[onlineSocketsI].pmmWrite = iMC_PMM_Wr_socket[skt]; memory.sockets[onlineSocketsI].total = iMC_Rd_socket[skt] + iMC_Wr_socket[skt] + iMC_PMM_Rd_socket[skt] + iMC_PMM_Wr_socket[skt]; const auto all = memory.sockets[onlineSocketsI].total; memory.sockets[onlineSocketsI].pmmMemoryModeHitRate = (all == 0.0) ? -1.0 : ((iMC_Rd_socket[skt] + iMC_Wr_socket[skt]) / all); // simplified approximation if (memory.dramEnergyMetricsAvailable) { memory.sockets[onlineSocketsI].dramEnergy = getDRAMConsumedJoules(socketStatesBefore_[skt], socketStatesAfter_[skt]); } systemRead += iMC_Rd_socket[skt]; systemWrite += iMC_Wr_socket[skt]; systemPMMRead += iMC_PMM_Rd_socket[skt]; systemPMMWrite += iMC_PMM_Wr_socket[skt]; ++onlineSocketsI; } memory.system.read = systemRead; memory.system.write = systemWrite; memory.system.total = systemRead + systemWrite; } void Daemon::getPCMQPI() { PCMQPI& qpi = sharedPCMState_->pcm.qpi; const uint32 numSockets = sharedPCMState_->pcm.system.numOfSockets; const uint32 numLinksPerSocket = sharedPCMState_->pcm.system.numOfQPILinksPerSocket; qpi.incomingQPITrafficMetricsAvailable = pcmInstance_->incomingQPITrafficMetricsAvailable(); if (qpi.incomingQPITrafficMetricsAvailable) { uint32 onlineSocketsI(0); for (uint32 i(0); i < numSockets; ++i) { if (!pcmInstance_->isSocketOnline(i)) continue; qpi.incoming[onlineSocketsI].socketId = i; uint64 total(0); for (uint32 l(0); l < numLinksPerSocket; ++l) { uint64 bytes = getIncomingQPILinkBytes(i, l, systemStatesBefore_, systemStatesAfter_); qpi.incoming[onlineSocketsI].links[l].bytes = bytes; qpi.incoming[onlineSocketsI].links[l].utilization = getIncomingQPILinkUtilization(i, l, systemStatesForQPIBefore_, systemStatesAfter_); total += bytes; } qpi.incoming[i].total = total; ++onlineSocketsI; } qpi.incomingTotal = getAllIncomingQPILinkBytes(systemStatesBefore_, systemStatesAfter_); } qpi.outgoingQPITrafficMetricsAvailable = pcmInstance_->outgoingQPITrafficMetricsAvailable(); if (qpi.outgoingQPITrafficMetricsAvailable) { uint32 onlineSocketsI(0); for (uint32 i(0); i < numSockets; ++i) { if (!pcmInstance_->isSocketOnline(i)) continue; qpi.outgoing[onlineSocketsI].socketId = i; uint64 total(0); for (uint32 l(0); l < numLinksPerSocket; ++l) { uint64 bytes = getOutgoingQPILinkBytes(i, l, systemStatesBefore_, systemStatesAfter_); qpi.outgoing[onlineSocketsI].links[l].bytes = bytes; qpi.outgoing[onlineSocketsI].links[l].utilization = getOutgoingQPILinkUtilization(i, l, systemStatesForQPIBefore_, systemStatesAfter_); total += bytes; } qpi.outgoing[i].total = total; ++onlineSocketsI; } qpi.outgoingTotal = getAllOutgoingQPILinkBytes(systemStatesBefore_, systemStatesAfter_); } } uint64 Daemon::getTimestamp() { struct timespec now; clock_gettime(CLOCK_MONOTONIC_RAW, &now); uint64 epoch = (uint64)now.tv_sec * 1E9; epoch += (uint64)now.tv_nsec; return epoch; } void Daemon::cleanup() { if (sharedPCMState_ != NULL) { //Detach shared memory segment int success = shmdt(sharedPCMState_); if (success != 0) { std::cerr << "Failed to detach the shared memory segment (errno=" << errno << ")\n"; } else { // Delete segment success = shmctl(sharedMemoryId_, IPC_RMID, NULL); if (success != 0) { std::cerr << "Failed to delete the shared memory segment (errno=" << errno << ")\n"; } } //Delete shared memory ID file success = remove(shmIdLocation_.c_str()); if (success != 0) { std::cerr << "Failed to delete shared memory id location: " << shmIdLocation_ << " (errno=" << errno << ")\n"; } } } } pcm-202307/src/daemon/daemon.h000066400000000000000000000036041445420033100160350ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Steven Briscoe #ifndef DAEMON_H_ #define DAEMON_H_ #include #include #include #include #include "common.h" #include "pcm.h" namespace PCMDaemon { enum Mode { DIFFERENCE, ABSOLUTE }; class Daemon { public: Daemon(int argc, char *argv[]); ~Daemon(); int run(); Daemon (const Daemon &) = delete; Daemon & operator = (const Daemon &) = delete; private: void setupPCM(); void checkAccessAndProgramPCM(); void readApplicationArguments(int argc, char *argv[]); void printExampleUsageAndExit(char *argv[]); void setupSharedMemory(); gid_t resolveGroupName(const std::string& groupName); void getPCMCounters(); void updatePCMState(SystemCounterState* systemStates, std::vector* socketStates, std::vector* coreStates, uint64 & t); void swapPCMBeforeAfterState(); void getPCMSystem(); void getPCMCore(); void getPCMMemory(); void getPCMQPI(); uint64 getTimestamp(); static void cleanup(); bool debugMode_; uint32 pollIntervalMs_; std::string groupName_; Mode mode_; static std::string shmIdLocation_; static int sharedMemoryId_; static SharedPCMState* sharedPCMState_; PCM* pcmInstance_; std::map subscribers_; std::vector allowedSubscribers_; //Data for core, socket and system state uint64 collectionTimeBefore_{0ULL}, collectionTimeAfter_{0ULL}; std::vector coreStatesBefore_, coreStatesAfter_; std::vector socketStatesBefore_, socketStatesAfter_; SystemCounterState systemStatesBefore_, systemStatesForQPIBefore_, systemStatesAfter_; ServerUncoreCounterState* serverUncoreCounterStatesBefore_; ServerUncoreCounterState* serverUncoreCounterStatesAfter_; }; } #endif /* DAEMON_H_ */ pcm-202307/src/daemon/main.cpp000066400000000000000000000004071445420033100160470ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Steven Briscoe #include "daemon.h" PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { PCMDaemon::Daemon daemon(argc, argv); return daemon.run(); } pcm-202307/src/daemon/pcm.h000066400000000000000000000007411445420033100153500ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Steven Briscoe #ifndef PCM_H_ #define PCM_H_ #ifdef _MSC_VER #include #include "../../../PCM_Win/windriver.h" #else #include #include #include // for gettimeofday() #endif #include "../cpucounters.h" #include "../utils.h" #ifdef _MSC_VER #include "../../freegetopt/getopt.h" #endif using namespace pcm; #endif /* PCM_H_ */ pcm-202307/src/dashboard.cpp000066400000000000000000000635711445420033100156220ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation #include #include #include #include "cpucounters.h" #include "dashboard.h" namespace pcm { class Target { public: virtual std::string operator () (const std::string& refId) const = 0; virtual ~Target() {} }; class InfluxDBTarget : public Target { std::string alias; std::string metric; InfluxDBTarget() = delete; public: InfluxDBTarget(const std::string & alias_, const std::string & metric_) : alias(alias_), metric(metric_) {} std::string operator () (const std::string & refId) const { std::string result; result += R"PCMDELIMITER( { "alias": ")PCMDELIMITER"; result += alias; result += R"PCMDELIMITER(", "groupBy": [ { "params": [ "$__interval" ], "type": "time" }, { "params": [ "null" ], "type": "fill" } ], "measurement": "http", "orderByTime": "ASC", "policy": "default", "query": "SELECT )PCMDELIMITER"; result += metric; result += R"PCMDELIMITER( FROM \"http\" WHERE (\"url\" = '$node') AND $timeFilter GROUP BY time($__interval) fill(null)", "rawQuery": true, "refId": ")PCMDELIMITER"; result += refId; result += R"PCMDELIMITER(", "resultFormat": "time_series", "select": [ [ { "params": [ "value" ], "type": "field" }, { "params": [], "type": "mean" } ] ], "tags": [] })PCMDELIMITER"; return result; } }; class PrometheusTarget : public Target { std::string legend, expr; PrometheusTarget() = delete; public: PrometheusTarget(const std::string& legend_, const std::string& expr_) : legend(legend_), expr(expr_) {} std::string operator () (const std::string& refId) const { std::string result; result += R"PCMDELIMITER( { "expr": ")PCMDELIMITER"; result += expr; result += R"PCMDELIMITER(", "instant": false, "interval": "", "legendFormat": ")PCMDELIMITER"; result += legend; result += R"PCMDELIMITER(", "refId": ")PCMDELIMITER"; result += refId; result += R"PCMDELIMITER(" })PCMDELIMITER"; return result; } }; const char * defaultDataSource = "null"; class Panel { int x, y, w, h; std::string title; std::vector> targets; Panel() = delete; protected: std::string getHeader(const int id) const { std::string result; result += R"PCMDELIMITER( { "datasource": )PCMDELIMITER"; result += defaultDataSource; result += R"PCMDELIMITER(, "interval": "2s", "gridPos": { )PCMDELIMITER"; result += " \"x\": " + std::to_string(x) + ",\n"; result += " \"y\": " + std::to_string(y) + ",\n"; result += " \"w\": " + std::to_string(w) + ",\n"; result += " \"h\": " + std::to_string(h); result += R"PCMDELIMITER( }, "title": ")PCMDELIMITER"; result += title; result += "\",\n \"id\": " + std::to_string(id) + ",\n \"targets\": ["; char refId[] = "A"; for (size_t i = 0; i< targets.size(); ++i, ++(refId[0])) { if (i > 0) { result += ","; } result += targets[i]->operator()(refId); } result += "\n ],\n"; return result; } public: Panel(int x_, int y_, int w_, int h_, const std::string & title_) : x(x_), y(y_), w(w_), h(h_), title(title_) {} void push(const std::shared_ptr & t) { targets.push_back(t); } virtual std::string operator () (const int id) const = 0; virtual ~Panel() {} }; class GaugePanel : public Panel { GaugePanel() = delete; public: GaugePanel(int x_, int y_, int w_, int h_, const std::string & title_) : Panel(x_, y_, w_, h_, title_) {} std::string operator () (const int id) const { std::string result = Panel::getHeader(id); result += R"PCMDELIMITER( "options": { "fieldOptions": { "calcs": [ "lastNotNull" ], "defaults": { "mappings": [], "max": 100, "min": 0, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 70 } ] }, "unit": "%" }, "overrides": [], "values": false }, "orientation": "auto", "showThresholdLabels": false, "showThresholdMarkers": true }, "pluginVersion": "6.7.2", "timeFrom": null, "timeShift": null, "type": "gauge" })PCMDELIMITER"; return result; } }; class BarGaugePanel : public Panel { BarGaugePanel() = delete; public: BarGaugePanel(int x_, int y_, int w_, int h_, const std::string & title_) : Panel(x_, y_, w_, h_, title_) {} std::string operator () (const int id) const { std::string result = Panel::getHeader(id); result += R"PCMDELIMITER( "cacheTimeout": null, "links": [ { "title": "", "url": "" } ], "options": { "displayMode": "lcd", "fieldOptions": { "calcs": [ "lastNotNull" ], "defaults": { "mappings": [ { "$$hashKey": "object:413", "id": 0, "op": "=", "text": "N/A", "type": 1, "value": "null" } ], "nullValueMode": "connected", "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null } ] }, "unit": "none" }, "overrides": [], "values": false }, "orientation": "vertical", "showUnfilled": true }, "pluginVersion": "6.7.2", "timeFrom": null, "timeShift": null, "type": "bargauge" })PCMDELIMITER"; return result; } }; class GraphPanel : public Panel { std::string yAxisLabel; bool stack; GraphPanel() = delete; public: GraphPanel(int x_, int y_, int w_, int h_, const std::string & title_, const std::string & yAxisLabel_, bool stack_) : Panel(x_, y_, w_, h_, title_) , yAxisLabel(yAxisLabel_) , stack(stack_) { } std::string operator () (const int id) const { std::string result = Panel::getHeader(id); result += R"PCMDELIMITER( "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "fill": 1, "fillGradient": 0, "hiddenSeries": false, "legend": { "avg": false, "current": false, "max": false, "min": false, "show": true, "total": false, "values": false }, "lines": true, "linewidth": 1, "links": [ { "title": "", "url": "" } ], "nullPointMode": "null", "options": { "dataLinks": [] }, "percentage": false, "pluginVersion": "6.7.2", "pointradius": 2, "points": false, "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, "stack": )PCMDELIMITER"; result += stack? "true" : "false"; result += R"PCMDELIMITER(, "steppedLine": false, "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, "tooltip": { "shared": true, "sort": 0, "value_type": "individual" }, "type": "graph", "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, "yaxes": [ { "$$hashKey": "object:2758", "format": "none", "label": ")PCMDELIMITER"; result += yAxisLabel; result += R"PCMDELIMITER(", "logBase": 1, "max": null, "min": "0", "show": true }, { "$$hashKey": "object:2759", "format": "none", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ], "yaxis": { "align": false, "alignLevel": null } })PCMDELIMITER"; return result; } }; class Dashboard { std::string title; PCMDashboardType type; std::vector> panels; Dashboard() = delete; public: Dashboard(const std::string & title_,PCMDashboardType type_) : title(title_), type(type_) {} void push(const std::shared_ptr & p) { panels.push_back(p); } std::string operator () () const { std::string result; std::string definition,query; if(type==InfluxDB){ definition = "\"SHOW TAG VALUES WITH KEY = \\\"url\\\"\""; query = "\"SHOW TAG VALUES WITH KEY = \\\"url\\\"\""; } else{ definition = "\"label_values(Number_of_sockets,instance)\""; query = "{\"query\": \"label_values(Number_of_sockets,instance)\",\"refId\": \"StandardVariableQuery\"}"; } result += R"PCMDELIMITER({ "annotations": { "list": [ { "$$hashKey": "object:2661", "builtIn": 1, "datasource": "-- Grafana --", "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "gnetId": null, "graphTooltip": 0, "id": 1, "links": [], "panels": [)PCMDELIMITER"; for (size_t i=0; i < panels.size(); ++i) { if (i > 0) { result += ","; } result += panels[i]->operator()(i + 2); } result += R"PCMDELIMITER( ], "refresh": "5s", "schemaVersion": 22, "style": "dark", "tags": [], "templating": { "list": [ { "current": { "selected": false, "text": "ip addr:port", "value": "ip addr:port" }, "datasource": null, "definition": )PCMDELIMITER"; result +=definition; result += R"PCMDELIMITER(, "hide": 0, "includeAll": false, "label": "Host", "multi": false, "name": "node", "options": [], "query":)PCMDELIMITER"; result+=query; result += R"PCMDELIMITER(, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 0, "type": "query" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "", "title": ")PCMDELIMITER"; result += title; result += R"PCMDELIMITER(", "uid": "A_CvwTCWk", "variables": { "list": [] }, "version": 1 })PCMDELIMITER"; return result; } }; std::string prometheusMetric(const std::string& m) { auto result = m; for (char& c : result) { if (c == ' ' || c == '-') { c = '_'; } } return result; } std::string prometheusSystem() { return "{instance=\\\"$node\\\", aggregate=\\\"system\\\"}"; } std::string prometheusSocket(const std::string& S, const bool aggregate = true) { if (aggregate) return "{instance=\\\"$node\\\", aggregate=\\\"socket\\\", socket=\\\"" + S + "\\\"}"; return "{instance=\\\"$node\\\", socket=\\\"" + S + "\\\"}"; } std::string prometheusSystem(const std::string& S) { return "{instance=\\\"$node\\\", aggregate=\\\"system\\\", socket=\\\"" + S + "\\\"}"; } std::string influxDB_Counters(const std::string& S, const std::string& m, const char * domain) { return std::string("mean(\\\"Sockets_") + S + "_" + domain + "_" + m + "\\\")"; } std::string influxDB_Counters(const std::string& m, const char* domain) { return std::string("mean(\\\"") + domain + "_" + m + "\\\")"; } std::string influxDBCore_Aggregate_Core_Counters(const std::string& S, const std::string& m) { return influxDB_Counters(S, m, "Core Aggregate_Core Counters"); } std::string influxDBCore_Aggregate_Core_Counters(const std::string& m) { return influxDB_Counters(m, "Core Aggregate_Core Counters"); } std::string influxDBUncore_Uncore_Counters(const std::string& S, const std::string& m) { return influxDB_Counters(S, m, "Uncore_Uncore Counters"); } const char* interval = "[4s]"; std::string prometheusCounters(const std::string& S, const std::string& m, const bool aggregate = true) { return std::string("rate(") + prometheusMetric(m) + prometheusSocket(S, aggregate) + interval + ")"; } std::string prometheusCounters(const std::string& m) { return std::string("rate(") + prometheusMetric(m) + prometheusSystem() + interval + ")"; } std::mutex dashboardGenMutex; std::string getPCMDashboardJSON(const PCMDashboardType type, int ns, int nu, int nc) { auto pcm = PCM::getInstance(); std::lock_guard dashboardGenGuard(dashboardGenMutex); const size_t NumSockets = (ns < 0) ? pcm->getNumSockets() : ns; const size_t NumUPILinksPerSocket = (nu < 0) ? pcm->getQPILinksPerSocket() : nu; const size_t maxCState = (nc < 0) ? PCM::MAX_C_STATE : nc; constexpr int height = 5; constexpr int width = 15; constexpr int max_width = 24; int y = 0; if (type == Prometheus_Default) { interval = "[60s]"; defaultDataSource = "\"prometheus\""; } else { interval = "[4s]"; defaultDataSource = "null"; } char buffer[64]; std::string hostname = "unknown hostname"; if (gethostname(buffer, 63) == 0) { hostname = buffer; } Dashboard dashboard("Intel(r) Performance Counter Monitor (Intel(r) PCM) Dashboard - " + hostname,type); auto createTarget = [type](const std::string& title, const std::string& inluxdbMetric, const std::string& prometheusExpr) -> std::shared_ptr { std::shared_ptr t; if (type == InfluxDB) t = std::make_shared(title, inluxdbMetric); else t = std::make_shared(title, prometheusExpr); return t; }; { auto panel = std::make_shared(0, y, width, height, "Memory Bandwidth", "MByte/sec", false); auto panel1 = std::make_shared(width, y, max_width - width, height, "Memory Bandwidth (MByte/sec)"); y += height; auto genAll = [type](const std::string& special) -> std::string { std::string all; for (auto& m : { "DRAM Reads", "DRAM Writes", "Persistent Memory Reads", "Persistent Memory Writes" }) { if (all.size() > 0) { all += " + "; } if (type == InfluxDB) all += special + "_Uncore Counters_" + m + "\\\")/1048576"; else all += std::string("rate(") + prometheusMetric(m) + special + interval + ")/1048576"; } return all; }; for (size_t s = 0; s < NumSockets; ++s) { const auto S = std::to_string(s); auto t = createTarget("Socket" + S, genAll("mean(\\\"Sockets_" + S + "_Uncore"), genAll(prometheusSocket(S, false))); panel->push(t); panel1->push(t); } auto t = createTarget("Total", genAll("mean(\\\"Uncore Aggregate"), genAll(prometheusSystem())); panel->push(t); panel1->push(t); dashboard.push(panel); dashboard.push(panel1); }; for (size_t s = 0; s < NumSockets; ++s) { const auto S = std::to_string(s); auto panel = std::make_shared(0, y, width, height, std::string("Socket") + S + " Memory Bandwidth", "MByte/sec", false); auto panel1 = std::make_shared(width, y, max_width - width, height, std::string("Current Socket") + S + " Memory Bandwidth (MByte/sec)"); y += height; for (auto& m : { "DRAM Reads", "DRAM Writes", "Persistent Memory Reads", "Persistent Memory Writes" }) { auto t = createTarget(m, influxDBUncore_Uncore_Counters(S, m) + "/1048576", prometheusCounters(S, m, false) + "/1048576"); panel->push(t); panel1->push(t); } for (auto& m : {"CXL Write Mem","CXL Write Cache" }){ auto t = createTarget(m, "mean(\\\"QPI/UPI Links_QPI Counters Socket " + S + "_" + m + "\\\")/1048576", prometheusCounters(S, m, false) + "/1048576"); panel->push(t); panel1->push(t); } for (std::string m : { "DRAM ", "Persistent Memory " }) { auto t = createTarget(m + "Total", "(" + influxDBUncore_Uncore_Counters(S, m + "Writes") + "+" + influxDBUncore_Uncore_Counters(S, m + "Reads") + ")/1048576", "(" + prometheusCounters(S, m + "Writes", false) + "+" + prometheusCounters(S, m + "Reads", false) + ")/1048576"); panel->push(t); panel1->push(t); } dashboard.push(panel); dashboard.push(panel1); } auto panel = std::make_shared(0, y, width, height, "PMEM/DRAM Bandwidth Ratio", "PMEM/DRAM", false); auto panel1 = std::make_shared(width, y, max_width - width, height, "PMEM/DRAM Bandwidth Ratio"); y += height; for (size_t s = 0; s < NumSockets; ++s) { const auto S = std::to_string(s); auto t = createTarget("Socket" + S, "(" + influxDBUncore_Uncore_Counters(S, "Persistent Memory Writes") + "+" + influxDBUncore_Uncore_Counters(S, "Persistent Memory Reads") + ")/" + "(" + influxDBUncore_Uncore_Counters(S, "DRAM Writes") + "+" + influxDBUncore_Uncore_Counters(S, "DRAM Reads") + ")", "(" + prometheusCounters(S, "Persistent Memory Writes", false) + "+" + prometheusCounters(S, "Persistent Memory Reads", false) + ")/" + "(" + prometheusCounters(S, "DRAM Writes", false) + "+" + prometheusCounters(S, "DRAM Reads", false) +")"); panel->push(t); panel1->push(t); } dashboard.push(panel); dashboard.push(panel1); auto upi = [&](const std::string & m, const bool utilization) { for (size_t s = 0; s < NumSockets; ++s) { const auto S = std::to_string(s); auto panel = std::make_shared(0, y, width, height, std::string("Socket") + S + " " + pcm->xPI() + " " + m, utilization?"%": "MByte/sec", false); std::shared_ptr panel1; if (utilization) panel1 = std::make_shared(width, y, max_width - width, height, std::string("Current Socket") + S + " UPI " + m + " (%)"); else panel1 = std::make_shared(width, y, max_width - width, height, std::string("Current Socket") + S + " UPI " + m + " (MByte/sec)"); y += height; const auto suffix = utilization ? "*100" : "/1048576"; for (size_t l = 0; l < NumUPILinksPerSocket; ++l) { const auto L = std::to_string(l); auto t = createTarget(pcm->xPI() + std::to_string(l), "mean(\\\"QPI/UPI Links_QPI Counters Socket " + S + "_" + m + " On Link " + L + "\\\")" + suffix, "rate(" + prometheusMetric(m) + "_On_Link_" + L + prometheusSystem(S) + interval + ")" + suffix); panel->push(t); panel1->push(t); } dashboard.push(panel); dashboard.push(panel1); } }; for (auto &m : {"Utilization Outgoing Data And Non-Data Traffic", "Utilization Incoming Data Traffic"}) { upi(m, true); } for (auto & m : {"Outgoing Data And Non-Data Traffic", "Incoming Data Traffic"}) { upi(m, false); } auto cstate = [&] (const char * m, const char * tPrefix, const char * source) { auto my_height = 3 * height / 2; auto panel = std::make_shared(0, y, width, my_height, std::string(m) + " C-state residency", "stacked %", true); auto panel1 = std::make_shared(width, y, max_width - width, my_height, std::string("Current ") + m + " C-state residency (%)"); y += my_height; auto prometheusCStateExpression = [](const std::string& source, const size_t c) -> std::string { auto C = std::to_string(c); return std::string("100 * rate(RawCStateResidency{ instance=\\\"$node\\\", aggregate = \\\"system\\\", index = \\\"") + C + "\\\", source = \\\"" + source + "\\\" }" + interval + ") / ignoring(source, index) rate(Invariant_TSC{ instance=\\\"$node\\\", aggregate = \\\"system\\\" }" + interval + ")"; }; auto prometheusComputedCStateExpression = [&maxCState, &prometheusCStateExpression](const std::string& source, const size_t e) -> std::string { std::string result = "100"; for (size_t c = 0; c < maxCState + 1; ++c) { if (e != c) { result = result + " - (" + prometheusCStateExpression(source, c) + ") "; } } return result; }; for (size_t c = 0; c < maxCState + 1; ++c) { auto C = std::to_string(c); auto pExpr = prometheusCStateExpression(source, c); if ((std::string(source) == "core" && c == 1) || (std::string(source) == "uncore" && c == 0)) { pExpr = prometheusComputedCStateExpression(source, c); } auto t = createTarget("C" + C, std::string("mean(\\\"") + tPrefix + " Counters_CStateResidency[" + C + "]\\\")*100", pExpr); panel->push(t); panel1->push(t); } dashboard.push(panel); dashboard.push(panel1); }; cstate("Core", "Core Aggregate_Energy", "core"); cstate("Package", "Uncore Aggregate_Uncore", "uncore"); auto derived = [&](const std::string & fullName, const std::string & shortName, const std::string & dividend, const std::string & divisor) { auto panel = std::make_shared(0, y, width, height, fullName, shortName, false); auto panel1 = std::make_shared(width, y, max_width - width, height, fullName); y += height; for (size_t s = 0; s < NumSockets; ++s) { const auto S = std::to_string(s); auto t = createTarget("Socket" + S, influxDBCore_Aggregate_Core_Counters(S, dividend) + "/" + influxDBCore_Aggregate_Core_Counters(S, divisor), prometheusCounters(S, dividend) + "/" + prometheusCounters(S, divisor)); panel->push(t); panel1->push(t); } auto t = createTarget("Total", influxDBCore_Aggregate_Core_Counters(dividend) + "/" + influxDBCore_Aggregate_Core_Counters(divisor), prometheusCounters(dividend) + "/" + prometheusCounters(divisor) ); panel->push(t); panel1->push(t); dashboard.push(panel); dashboard.push(panel1); }; derived("Instructions Per Cycle", "IPC", "Instructions Retired Any", "Clock Unhalted Thread"); derived("Active Frequency Ratio", "AFREQ", "Clock Unhalted Thread", "Clock Unhalted Ref"); derived("L3 Cache Misses Per Instruction", "L3 MPI", "L3 Cache Misses", "Instructions Retired Any"); derived("L2 Cache Misses Per Instruction", "L2 MPI", "L2 Cache Misses", "Instructions Retired Any"); for (auto & m : {"Instructions Retired Any", "Clock Unhalted Thread", "L2 Cache Hits", "L2 Cache Misses", "L3 Cache Hits", "L3 Cache Misses"}) { auto panel = std::make_shared(0, y, width, height, std::string(m), "Million", false); auto panel1 = std::make_shared(width, y, max_width - width, height, std::string(m) + " (Million)"); y += height; for (size_t s = 0; s < NumSockets; ++s) { const auto S = std::to_string(s); auto t = createTarget("Socket" + S, influxDBCore_Aggregate_Core_Counters(S, m) + "/1000000", prometheusCounters(S, m) + "/1000000"); panel->push(t); panel1->push(t); } auto t = createTarget("Total", influxDBCore_Aggregate_Core_Counters(m) + "/1000000", prometheusCounters(m) + "/1000000"); panel->push(t); panel1->push(t); dashboard.push(panel); dashboard.push(panel1); } for (size_t s = 0; s < NumSockets; ++s) { const auto S = std::to_string(s); auto panel = std::make_shared(0, y, width, height, std::string("Socket") + S + " Energy Consumption", "Watt", false); auto panel1 = std::make_shared(width, y, max_width - width, height, std::string("Current Socket") + S + " Energy Consumption (Watt)"); y += height; for (auto &m : {"Package Joules Consumed", "DRAM Joules Consumed"}) { auto t = createTarget(m, influxDBUncore_Uncore_Counters(S, m), prometheusCounters(S, m, false)); panel->push(t); panel1->push(t); } dashboard.push(panel); dashboard.push(panel1); } return dashboard(); } } // namespace pcm pcm-202307/src/dashboard.h000066400000000000000000000005061445420033100152540ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation #pragma once #include namespace pcm { enum PCMDashboardType { InfluxDB, Prometheus, Prometheus_Default }; std::string getPCMDashboardJSON(const PCMDashboardType type, int ns = -1, int nu = -1, int nc = -1); } // namespace pcm pcm-202307/src/dashboardtest.cpp000066400000000000000000000003641445420033100165110ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation #include "dashboard.h" #include int main() { std::cout << pcm::getPCMDashboardJSON(pcm::Prometheus, 2, 3, 10) << std::endl; return 0; } pcm-202307/src/debug.cpp000066400000000000000000000004231445420033100147440ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation namespace pcm { namespace debug { int currentDebugLevel = 0; void dyn_debug_level( int debugLevel ) { debug::currentDebugLevel = debugLevel; } } } // namespace pcm pcm-202307/src/debug.h000066400000000000000000000041121445420033100144100ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation #pragma once #include #include #include #ifdef _MSC_VER #include #define ssize_t SSIZE_T #define __PRETTY_FUNCTION__ __FUNCSIG__ #endif namespace pcm { namespace debug { extern int currentDebugLevel; template void dyn_debug_output_helper( std::stringstream& out, T t ) { out << t << "\n"; } template void dyn_debug_output_helper( std::stringstream& out, const T & t, Args... args ) { out << t; dyn_debug_output_helper( out, args... ); } template void dyn_debug_output( std::ostream& out, LVL level, PF pretty_function, F file, L line, Args... args ) { std::stringstream ss; ss << "DBG(" << std::dec << level << "): File '" << file << "', line '" << std::dec << line << "' :\n"; ss << "DBG(" << std::dec << level << "): " << pretty_function << ":\n"; ss << "DBG(" << std::dec << level << "): "; // Next code line will continue printing on this output line dyn_debug_output_helper( ss, args... ); out << ss.str() << std::flush; } template void dyn_hex_table_output( int debugLevel, std::ostream& out, ssize_t len, T* inputBuffer_ ) { std::stringstream ss; if ( debug::currentDebugLevel < debugLevel ) return; for ( ssize_t i = 0; i < len; ++i ) { constexpr int DHTO_CHARS_PER_LINE = 16; ss << std::hex << std::internal << std::setfill('0') << std::setw(2) << std::abs(inputBuffer_[i]) << " "; if ( (i % DHTO_CHARS_PER_LINE) == (DHTO_CHARS_PER_LINE - 1) ) ss << "\n"; } out << ss.str() << std::flush; } void dyn_debug_level( int debugLevel ); } #define DBG( level, ... ) \ if ( debug::currentDebugLevel >= level ) \ debug::dyn_debug_output( std::cout, level, __PRETTY_FUNCTION__, __FILE__, __LINE__, __VA_ARGS__) } // namespace pcm pcm-202307/src/exceptions/000077500000000000000000000000001445420033100153345ustar00rootroot00000000000000pcm-202307/src/exceptions/unsupported_processor_exception.hpp000066400000000000000000000004361445420033100246150ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Steven Briscoe #include class UnsupportedProcessorException: public std::exception { virtual const char* what() const throw() { return "Unsupported processor"; } }; pcm-202307/src/favicon.ico.h000066400000000000000000000035451445420033100155310ustar00rootroot00000000000000signed char favicon_ico[] = { 0x00_uc, 0x00_uc, 0x01_uc, 0x00_uc, 0x01_uc, 0x00_uc, 0x10_uc, 0x10_uc, 0x02_uc, 0x00_uc, 0x01_uc, 0x00_uc, 0x01_uc, 0x00_uc, 0xb0_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x16_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x28_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x10_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x20_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x01_uc, 0x00_uc, 0x01_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x40_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x02_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x02_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0xff_uc, 0xff_uc, 0xff_uc, 0x00_uc, 0xff_uc, 0xff_uc, 0x00_uc, 0x00_uc, 0x7e_uc, 0x6e_uc, 0x00_uc, 0x00_uc, 0x7d_uc, 0xae_uc, 0x00_uc, 0x00_uc, 0x7d_uc, 0xae_uc, 0x00_uc, 0x00_uc, 0x7d_uc, 0xee_uc, 0x00_uc, 0x00_uc, 0x7d_uc, 0xee_uc, 0x00_uc, 0x00_uc, 0x7d_uc, 0xee_uc, 0x00_uc, 0x00_uc, 0x7d_uc, 0xee_uc, 0x00_uc, 0x00_uc, 0x0d_uc, 0xee_uc, 0x00_uc, 0x00_uc, 0x75_uc, 0xea_uc, 0x00_uc, 0x00_uc, 0x75_uc, 0xea_uc, 0x00_uc, 0x00_uc, 0x75_uc, 0xea_uc, 0x00_uc, 0x00_uc, 0x75_uc, 0xa4_uc, 0x00_uc, 0x00_uc, 0x75_uc, 0xa4_uc, 0x00_uc, 0x00_uc, 0x0e_uc, 0x6e_uc, 0x00_uc, 0x00_uc, 0xff_uc, 0xff_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc, 0x00_uc }; unsigned int favicon_ico_len = 198; pcm-202307/src/freegetopt/000077500000000000000000000000001445420033100153175ustar00rootroot00000000000000pcm-202307/src/freegetopt/ChangeLog000066400000000000000000000020141445420033100170660ustar00rootroot000000000000002003-10-25 Mark K. Kim {getopt*cbreak.org} * *: freegetopt 0.11 released. 2003-10-25 Mark K. Kim {getopt*cbreak.org} * ChangeLog: ChangeLog file added. * LICENSE: License file added. License is now officially BSD. * README: Updated to reflect the new license. CVS tracking data added. * getopt.h, getopt.c: License text added to the beginning of each file. CVS tracking data added. * test.c: License text added to the beginning of the file. CVS tracking data added. 2003-10-20 Mark K. Kim {getopt*cbreak.org} * *: Sourceforge project approval. Name changed to freegetopt. License is now officially BSD. 2003-09-29 Jon Higdon {jhigdon*nni.com} * test.c: GCC 3.x gives compound statement warning. Fixed. 2003.01.12 Mark K. Kim {getopt*cbreak.org} * *: Getopt 1.0 released! Supports only short options, but I think it's fully compatible with GNU getopt except for the value of optind while getopt is still running. The value of optind is same after getopt finishes running, however. pcm-202307/src/freegetopt/LICENSE000066400000000000000000000027701445420033100163320ustar00rootroot00000000000000Free Getopt Copyright (c)2002-2003 Mark K. Kim All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the original author of this software nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pcm-202307/src/freegetopt/README000066400000000000000000000034601445420033100162020ustar00rootroot00000000000000Free Getopt ****************************************************************************** Please read the file LICENSE for the terms of use and distribution of this software. ****************************************************************************** "getopt" is a library that allows a parsing of arguments passed to a program. This is a useful library used in many software. There are many versions of the getopt library available, two popular versions being the BSD getopt and the GNU getopt. BSD getopt is somewhat old, dated, and isn't very user-friendly. The GNU getopt is great, except the user license doesn't let you statically link the library to a proprietary software. This is usually not a problem on modern operating systems that allow dynamic links to libraries, but sometimes you just gotta link the library statically for one reason or another. That's where Free Getopt steps in. Functionally, this getopt library is equivalent to GNU's getopt library (the short option version, not the long one) in almost every aspect. The only exception is how the "optind" variable increments. Apparently due to different algorithms used by my program and the GNU getopt, the "optind" changes quite differently between our two software. I personally find my algorithm to be quite elegant; I couldn't tell you about the GNU version since I never looked at its source. GNU's getopt_long support is in progress. This library was deliberately left in non-library (not in *.lib, *.so, or *.a) form because it's most likely to be statically-linked in various platforms, and linking it directly from source is probably the most straight-forward way to use the software in any platform. I hope you find this software useful. Mark K. Kim $Header: /cvsroot/freegetopt/freegetopt/README,v 1.2 2003/10/26 03:10:19 vindaci Exp $ pcm-202307/src/freegetopt/getopt.cpp000066400000000000000000000145421445420033100173330ustar00rootroot00000000000000/***************************************************************************** * getopt.c - competent and free getopt library. * $Header: /cvsroot/freegetopt/freegetopt/getopt.c,v 1.2 2003/10/26 03:10:20 vindaci Exp $ * * Copyright (c)2002-2003 Mark K. Kim * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the original author of this software nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ #define _CRT_SECURE_NO_WARNINGS #include #include #include #include "getopt.h" //static const char* ID = "$Id: getopt.c,v 1.2 2003/10/26 03:10:20 vindaci Exp $"; char* optarg = NULL; int optind = 0; int opterr = 1; int optopt = '?'; static char** prev_argv = NULL; /* Keep a copy of argv and argc to */ static int prev_argc = 0; /* tell if getopt params change */ static int argv_index = 0; /* Option we're checking */ static int argv_index2 = 0; /* Option argument we're checking */ static int opt_offset = 0; /* Index into compounded "-option" */ static int dashdash = 0; /* True if "--" option reached */ static int nonopt = 0; /* How many nonopts we've found */ static void increment_index() { /* Move onto the next option */ if(argv_index < argv_index2) { while(prev_argv[++argv_index] && prev_argv[argv_index][0] != '-' && argv_index < argv_index2+1); } else argv_index++; opt_offset = 1; } /* * Permutes argv[] so that the argument currently being processed is moved * to the end. */ static int permute_argv_once() { /* Movability check */ if(argv_index + nonopt >= prev_argc) return 1; /* Move the current option to the end, bring the others to front */ else { char* tmp = prev_argv[argv_index]; /* Move the data */ memmove(&prev_argv[argv_index], &prev_argv[argv_index+1], sizeof(char**) * (prev_argc - argv_index - 1)); prev_argv[prev_argc - 1] = tmp; nonopt++; return 0; } } int getopt(int argc, char** argv, char* optstr) { int c = 0; /* If we have new argv, reinitialize */ if(prev_argv != argv || prev_argc != argc) { /* Initialize variables */ prev_argv = argv; prev_argc = argc; argv_index = 1; argv_index2 = 1; opt_offset = 1; dashdash = 0; nonopt = 0; } /* Jump point in case we want to ignore the current argv_index */ getopt_top: /* Misc. initializations */ optarg = NULL; /* Dash-dash check */ if(argv[argv_index] && !strcmp(argv[argv_index], "--")) { dashdash = 1; increment_index(); } /* If we're at the end of argv, that's it. */ if(argv[argv_index] == NULL) { c = -1; } /* Are we looking at a string? Single dash is also a string */ else if(dashdash || argv[argv_index][0] != '-' || !strcmp(argv[argv_index], "-")) { /* If we want a string... */ if(optstr[0] == '-') { c = 1; optarg = argv[argv_index]; increment_index(); } /* If we really don't want it (we're in POSIX mode), we're done */ else if(optstr[0] == '+' || getenv("POSIXLY_CORRECT")) { c = -1; /* Everything else is a non-opt argument */ nonopt = argc - argv_index; } /* If we mildly don't want it, then move it back */ else { if(!permute_argv_once()) goto getopt_top; else c = -1; } } /* Otherwise we're looking at an option */ else { char* opt_ptr = NULL; /* Grab the option */ c = argv[argv_index][opt_offset++]; /* Is the option in the optstr? */ if(optstr[0] == '-') opt_ptr = strchr(optstr+1, c); else opt_ptr = strchr(optstr, c); /* Invalid argument */ if(!opt_ptr) { if(opterr) { fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); } optopt = c; c = '?'; /* Move onto the next option */ increment_index(); } /* Option takes argument */ else if(opt_ptr[1] == ':') { /* ie, -oARGUMENT, -xxxoARGUMENT, etc. */ if(argv[argv_index][opt_offset] != '\0') { optarg = &argv[argv_index][opt_offset]; increment_index(); } /* ie, -o ARGUMENT (only if it's a required argument) */ else if(opt_ptr[2] != ':') { /* One of those "you're not expected to understand this" moment */ if(argv_index2 < argv_index) argv_index2 = argv_index; while(argv[++argv_index2] && argv[argv_index2][0] == '-'); optarg = argv[argv_index2]; /* Don't cross into the non-option argument list */ if(argv_index2 + nonopt >= prev_argc) optarg = NULL; /* Move onto the next option */ increment_index(); } else { /* Move onto the next option */ increment_index(); } /* In case we got no argument for an option with required argument */ if(optarg == NULL && opt_ptr[2] != ':') { optopt = c; c = '?'; if(opterr) { fprintf(stderr,"%s: option requires an argument -- %c\n", argv[0], optopt); } } } /* Option does not take argument */ else { /* Next argv_index */ if(argv[argv_index][opt_offset] == '\0') { increment_index(); } } } /* Calculate optind */ if(c == -1) { optind = argc - nonopt; } else { optind = argv_index; } return c; } /* vim:ts=3 */ pcm-202307/src/freegetopt/getopt.h000066400000000000000000000040331445420033100167720ustar00rootroot00000000000000/***************************************************************************** * getopt.h - competent and free getopt library. * $Header: /cvsroot/freegetopt/freegetopt/getopt.h,v 1.2 2003/10/26 03:10:20 vindaci Exp $ * * Copyright (c)2002-2003 Mark K. Kim * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * * Neither the original author of this software nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ #ifndef GETOPT_H_ #define GETOPT_H_ #ifdef __cplusplus extern "C" { #endif extern char* optarg; extern int optind; extern int opterr; extern int optopt; int getopt(int argc, char** argv, char* optstr); #ifdef __cplusplus } #endif #endif /* GETOPT_H_ */ /* vim:ts=3 */ pcm-202307/src/lspci.h000066400000000000000000000416441445420033100144470ustar00rootroot00000000000000#ifndef CPUCounters_LSPCI_H #define CPUCounters_LSPCI_H #include #include #include #include "cpucounters.h" #if defined(_MSC_VER) #define PCI_IDS_PATH "pci.ids" #define PCI_IDS_NOT_FOUND "pci.ids file is not available. Download it from" \ " https://raw.githubusercontent.com/pciutils/pciids/master/pci.ids." #elif defined (__FreeBSD__) || defined(__DragonFly__) #define PCI_IDS_PATH "/usr/local/share/pciids/pci.ids" #define PCI_IDS_NOT_FOUND "/usr/local/share/pciids/pci.ids file is not available." \ " Ensure that the \"pciids\" package is properly installed or download" \ " https://raw.githubusercontent.com/pciutils/pciids/master/pci.ids and" \ " copy it to the current directory." #else #define PCI_IDS_PATH "/usr/share/hwdata/pci.ids" #define PCI_IDS_NOT_FOUND "/usr/share/hwdata/pci.ids file is not available." \ " Ensure that the \"hwdata\" package is properly installed or download" \ " https://raw.githubusercontent.com/pciutils/pciids/master/pci.ids and" \ " copy it to the current directory." #endif namespace pcm { typedef uint32_t h_id; typedef uint32_t v_id; typedef std::map,uint64_t> ctr_data; typedef std::vector stack_content; typedef std::vector result_content; class ccr { public: virtual uint64_t get_event_select() const = 0; virtual void set_event_select(uint64_t value) = 0; virtual uint64_t get_umask() const = 0; virtual void set_umask(uint64_t value) = 0; virtual uint64_t get_reset() const = 0; virtual void set_reset(uint64_t value) = 0; virtual uint64_t get_edge() const = 0; virtual void set_edge(uint64_t value) = 0; virtual uint64_t get_ov_en() const = 0; virtual void set_ov_en(uint64_t value) = 0; virtual uint64_t get_enable() const = 0; virtual void set_enable(uint64_t value) = 0; virtual uint64_t get_invert() const = 0; virtual void set_invert(uint64_t value) = 0; virtual uint64_t get_thresh() const = 0; virtual void set_thresh(uint64_t value) = 0; virtual uint64_t get_ch_mask() const = 0; virtual void set_ch_mask(uint64_t value) = 0; virtual uint64_t get_fc_mask() const = 0; virtual void set_fc_mask(uint64_t value) = 0; virtual uint64_t get_ccr_value() const = 0; virtual void set_ccr_value(uint64_t value) = 0; virtual ~ccr() {}; }; class skx_ccr: public ccr { public: skx_ccr(uint64_t &v){ ccr_value = &v; } virtual uint64_t get_event_select() const { return (*ccr_value & 0xFF); } virtual void set_event_select(uint64_t value) { *ccr_value |= value; } virtual uint64_t get_umask() const { return ((*ccr_value >> 8) & 0xFF); } virtual void set_umask(uint64_t value) { *ccr_value |= (value << 8); } virtual uint64_t get_reset() const { return ((*ccr_value >> 17) & 0x01); } virtual void set_reset(uint64_t value) { *ccr_value |= (value << 17); } virtual uint64_t get_edge() const { return ((*ccr_value >> 18) & 0x01); } virtual void set_edge(uint64_t value) { *ccr_value |= (value << 18); } virtual uint64_t get_ov_en() const { return ((*ccr_value >> 20) & 0x01); } virtual void set_ov_en(uint64_t value) { *ccr_value |= (value << 20); } virtual uint64_t get_enable() const { return ((*ccr_value >> 22) & 0x01); } virtual void set_enable(uint64_t value) { *ccr_value |= (value << 22); } virtual uint64_t get_invert() const { return ((*ccr_value >> 23) & 0x01); } virtual void set_invert(uint64_t value) { *ccr_value |= (value << 23); } virtual uint64_t get_thresh() const { return ((*ccr_value >> 24) & 0xFFF); } virtual void set_thresh(uint64_t value) { *ccr_value |= (value << 24); } virtual uint64_t get_ch_mask() const { return ((*ccr_value >> 36) & 0xFF); } virtual void set_ch_mask(uint64_t value) { *ccr_value |= (value << 36); } virtual uint64_t get_fc_mask() const { return ((*ccr_value >> 44) & 0x07); } virtual void set_fc_mask(uint64_t value) { *ccr_value |= (value << 44); } virtual uint64_t get_ccr_value() const { return *ccr_value; } virtual void set_ccr_value(uint64_t value) { *ccr_value = value; } private: uint64_t* ccr_value = NULL; }; class icx_ccr: public ccr { public: icx_ccr(uint64_t &v){ ccr_value = &v; } virtual uint64_t get_event_select() const { return (*ccr_value & 0xFF); } virtual void set_event_select(uint64_t value) { *ccr_value |= value; } virtual uint64_t get_umask() const { return ((*ccr_value >> 8) & 0xFF); } virtual void set_umask(uint64_t value) { *ccr_value |= (value << 8); } virtual uint64_t get_reset() const { return ((*ccr_value >> 17) & 0x01); } virtual void set_reset(uint64_t value) { *ccr_value |= (value << 17); } virtual uint64_t get_edge() const { return ((*ccr_value >> 18) & 0x01); } virtual void set_edge(uint64_t value) { *ccr_value |= (value << 18); } virtual uint64_t get_ov_en() const { return ((*ccr_value >> 20) & 0x01); } virtual void set_ov_en(uint64_t value) { *ccr_value |= (value << 20); } virtual uint64_t get_enable() const { return ((*ccr_value >> 22) & 0x01); } virtual void set_enable(uint64_t value) { *ccr_value |= (value << 22); } virtual uint64_t get_invert() const { return ((*ccr_value >> 23) & 0x01); } virtual void set_invert(uint64_t value) { *ccr_value |= (value << 23); } virtual uint64_t get_thresh() const { return ((*ccr_value >> 24) & 0xFFF); } virtual void set_thresh(uint64_t value) { *ccr_value |= (value << 24); } virtual uint64_t get_ch_mask() const { return ((*ccr_value >> 36) & 0xFFF); } virtual void set_ch_mask(uint64_t value) { *ccr_value |= (value << 36); } virtual uint64_t get_fc_mask() const { return ((*ccr_value >> 48) & 0x07); } virtual void set_fc_mask(uint64_t value) { *ccr_value |= (value << 48); } virtual uint64_t get_ccr_value() const { return *ccr_value; } virtual void set_ccr_value(uint64_t value) { *ccr_value = value; } private: uint64_t* ccr_value = NULL; }; struct bdf { uint32_t domainno; uint8_t busno; uint8_t devno; uint8_t funcno; bdf() : domainno(0), busno(0), devno(0), funcno(0) {} bdf(uint32_t domain, uint8_t bus, uint8_t device, uint8_t function) : domainno(domain), busno(bus), devno(device), funcno(function) {} bdf(uint8_t bus, uint8_t device, uint8_t function) : domainno(0), busno(bus), devno(device), funcno(function) {} }; struct pci { bool exist = false; struct bdf bdf; union { struct { uint16_t vendor_id; uint16_t device_id; }; uint32_t offset_0; }; int8_t header_type; union { struct { uint8_t primary_bus_number; uint8_t secondary_bus_number; uint8_t subordinate_bus_number; uint8_t junk; }; uint32_t offset_18; }; union { struct { uint16_t link_ctrl; union { struct { uint16_t link_speed : 4; uint16_t link_width : 6; uint16_t undefined : 1; uint16_t link_trained : 1; }; uint16_t link_sta; }; }; uint32_t link_info; }; std::vector parts_no; std::vector child_pci_devs; pci() : exist(false), offset_0(0), header_type(0), offset_18(0), link_info(0), parts_no{}, child_pci_devs{} {} pci(uint32_t domain, uint8_t bus, uint8_t device, uint8_t function) : exist(false), bdf(domain, bus, device, function), offset_0(0), header_type(0), offset_18(0), link_info(0), parts_no{}, child_pci_devs{} {} pci(uint8_t bus, uint8_t device, uint8_t function) : exist(false), bdf(bus, device, function), offset_0(0), header_type(0), offset_18(0), link_info(0), parts_no{}, child_pci_devs{} {} pci(const struct bdf &address) : exist(false), bdf(address), offset_0(0), header_type(0), offset_18(0), link_info(0), parts_no{}, child_pci_devs{} {} bool hasChildDevices() const { return (child_pci_devs.size() != 0); } }; struct iio_skx { struct { struct { struct pci root_pci_dev; /* single device represent root port */ std::vector child_pci_devs; /* Contain child switch and end-point devices */ } parts[4]{}; /* part 0, 1, 2, 3 */ uint8_t busno{}; /* holding busno for each IIO stack */ std::string stack_name{}; std::vector values{}; bool flipped = false; } stacks[6]; /* iio stack 0, 1, 2, 3, 4, 5 */ uint32_t socket_id{}; }; struct iio_bifurcated_part { int part_id{0}; /* single device represent root port */ struct pci root_pci_dev; /* Contain child switch and end-point devices */ std::vector child_pci_devs; }; struct iio_stack { std::vector parts{}; uint32_t iio_unit_id{}; std::string stack_name{}; std::vector values{}; bool flipped = false; /* holding busno for each IIO stack */ uint32_t domain{}; uint8_t busno{}; iio_stack() : iio_unit_id(0), stack_name(""), domain(0), busno(0) {} }; bool operator<(const iio_stack& lh, const iio_stack& rh) { return lh.iio_unit_id < rh.iio_unit_id; } struct iio_stacks_on_socket { std::vector stacks{}; uint32_t socket_id{}; }; bool operator<(const bdf &l, const bdf &r) { if (l.domainno < r.domainno) return true; if (l.domainno > r.domainno) return false; if (l.busno < r.busno) return true; if (l.busno > r.busno) return false; if (l.devno < r.devno) return true; if (l.devno > r.devno) return false; if (l.funcno < r.funcno) return true; if (l.funcno > r.funcno) return false; return false; // bdf == bdf }; void probe_capability_pci_express(struct pci *p, uint32_t cap_ptr) { struct cap { union { struct { uint8_t id; union { uint8_t next; uint8_t cap_ptr; }; uint16_t junk; }; uint32 dw0; }; } cap; uint32 value; PciHandleType h(0, p->bdf.busno, p->bdf.devno, p->bdf.funcno); h.read32(cap_ptr, &value); //Capability pointer cap.dw0 = value; if (cap.id != 0x10 && cap.next != 0x00) { probe_capability_pci_express(p, cap.cap_ptr); } else { if (cap.id == 0x10) { // We're in PCI express capability structure h.read32(cap_ptr+0x10, &value); p->link_info = value; } else { /*Finish recursive searching but cannot find PCI express capability structure*/ } } } bool probe_pci(struct pci *p) { uint32 value; p->exist = false; struct bdf *bdf = &p->bdf; if (PciHandleType::exists(bdf->domainno, bdf->busno, bdf->devno, bdf->funcno)) { PciHandleType h(bdf->domainno, bdf->busno, bdf->devno, bdf->funcno); // VID:DID h.read32(0x0, &value); // Invalid VID::DID if (value != (std::numeric_limits::max)()) { p->offset_0 = value; h.read32(0xc, &value); p->header_type = (value >> 16) & 0x7f; if (p->header_type == 0) { // Status register h.read32(0x4, &value); // Capability list == true if (value & 0x100000) { // Capability pointer h.read32(0x34, &value); probe_capability_pci_express(p, value); } } else if (p->header_type == 1) { h.read32(0x18, &value); p->offset_18 = value; } p->exist = true; } } return p->exist; } /* first : [vendorID] -> vencor name second : [vendorID][deviceID] -> device name */ typedef std::pair< std::map ,std::map< int, std::map > > PCIDB; void print_pci(struct pci p, const PCIDB & pciDB) { printf("Parent bridge info:"); printf("%x:%x.%d [%04x:%04x] %s %s %d P:%x S:%x S:%x ", p.bdf.busno, p.bdf.devno, p.bdf.funcno, p.vendor_id, p.device_id, (pciDB.first.count(p.vendor_id) > 0)?pciDB.first.at(p.vendor_id).c_str():"unknown vendor", (pciDB.second.count(p.vendor_id) > 0 && pciDB.second.at(p.vendor_id).count(p.device_id) > 0)?pciDB.second.at(p.vendor_id).at(p.device_id).c_str():"unknown device", p.header_type, p.primary_bus_number, p.secondary_bus_number, p.subordinate_bus_number); printf("Device info:"); printf("%x:%x.%d [%04x:%04x] %s %s %d Gen%d x%d\n", p.bdf.busno, p.bdf.devno, p.bdf.funcno, p.vendor_id, p.device_id, (pciDB.first.count(p.vendor_id) > 0)?pciDB.first.at(p.vendor_id).c_str():"unknown vendor", (pciDB.second.count(p.vendor_id) > 0 && pciDB.second.at(p.vendor_id).count(p.device_id) > 0)?pciDB.second.at(p.vendor_id).at(p.device_id).c_str():"unknown device", p.header_type, p.link_speed, p.link_width); } void load_PCIDB(PCIDB & pciDB) { std::ifstream in(PCI_IDS_PATH); std::string line, item; if (!in.is_open()) { #ifndef _MSC_VER // On Unix, try the current directory if the default path failed in.open("pci.ids"); } if (!in.is_open()) { #endif std::cerr << PCI_IDS_NOT_FOUND << "\n"; return; } int vendorID = -1; while (std::getline(in, line)) { // Ignore any line starting with # if (line.size() == 0 || line[0] == '#') continue; if (line[0] == '\t' && line[1] == '\t') { // subvendor subdevice subsystem_name continue; } if (line[0] == '\t') { int deviceID = stoi(line.substr(1,4),0,16); //std::cout << vendorID << ";" << vendorName << ";" << deviceID << ";" << line.substr(7) << "\n"; pciDB.second[vendorID][deviceID] = line.substr(7); continue; } // vendor vendorID = stoi(line.substr(0,4),0,16); pciDB.first[vendorID] = line.substr(6); } } } // namespace pcm #endif pcm-202307/src/memoptest.cpp000066400000000000000000000053271445420033100157030ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // #include "cpucounters.h" #include #include #include #include #include #include #include using std::cout; inline double my_timestamp() { struct timeval tp; gettimeofday(&tp, NULL); return double(tp.tv_sec) + tp.tv_usec / 1000000.; } struct T { int key[1] = { 0 }; int data[3] = { 0, 0, 0 }; T() { } T(int a) { key[0] = a; } bool operator == (const T & k) const { return k.key[0] == key[0]; } }; template void write_intensive_task(Y * p, Y * e, int value) { __m128i i = _mm_set_epi32(value, value, value, value); #if 0 while (p != e) { *p = value; ++p; } #else while (p != e) { _mm_store_si128((__m128i *)p++, i); } #endif } template void stream_write_task(Y * p, Y * e, int value) { __m128i i = _mm_set_epi32(value, value, value, value); while (p != e) { _mm_stream_si128((__m128i *)p++, i); } } template void read_intensive_task(Y * p, Y * e, int value) { // cppcheck-suppress ignoredReturnValue std::find(p, e, -1); } int main(int argc, char * argv[]) { assert((argc > 1) && "Need operation type as parameter: 0 - read, 1 - write, 2 - streaming write "); int op = atoi(argv[1]); T * vector; int nelements = 13000000; vector = new T[nelements]; int i = 0; cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB\n"; for ( ; i < nelements; ++i) { vector[i].key[0] = 10; } double before_ts, after_ts; while (1) { before_ts = my_timestamp(); switch (op) { case 1: cout << "Writing memory\n"; break; case 0: cout << "Reading memory\n"; break; default: cout << "Streaming to memory\n"; } cout << std::flush; int niter = 32; i = niter; int r = rand(); while (i--) { switch (op) { case 1: write_intensive_task(vector, vector + nelements, r); break; case 0: read_intensive_task(vector, vector + nelements, r); break; default: stream_write_task(vector, vector + nelements, r); } after_ts = my_timestamp(); } cout << "Bandwidth: " << (sizeof(T) * nelements * niter) / ((after_ts - before_ts) * 1024 * 1024) << " MByte/sec\n" << std::flush; } delete[] vector; return 0; } pcm-202307/src/mmio.cpp000066400000000000000000000164101445420033100146220ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev, // Patrick Konsor // #include #include #ifndef _MSC_VER #include #endif #include #include #include "pci.h" #include "mmio.h" #ifndef _MSC_VER #include #include #endif #ifdef _MSC_VER #include #include "utils.h" #endif #include namespace pcm { #ifdef _MSC_VER class PCMPmem : public WinPmem { protected: virtual int load_driver_() { SYSTEM_INFO sys_info; SecureZeroMemory(&sys_info, sizeof(sys_info)); GetCurrentDirectory(MAX_PATH - 10, driver_filename); GetNativeSystemInfo(&sys_info); switch (sys_info.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: _tcscat_s(driver_filename, MAX_PATH, TEXT("\\winpmem_x64.sys")); if (GetFileAttributes(driver_filename) == INVALID_FILE_ATTRIBUTES) { std::cerr << "ERROR: winpmem_x64.sys not found in current directory. Download it from https://github.com/Velocidex/WinPmem/blob/master/kernel/binaries/winpmem_x64.sys .\n"; std::cerr << "ERROR: Memory bandwidth statistics will not be available.\n"; } break; case PROCESSOR_ARCHITECTURE_INTEL: _tcscat_s(driver_filename, MAX_PATH, TEXT("\\winpmem_x86.sys")); if (GetFileAttributes(driver_filename) == INVALID_FILE_ATTRIBUTES) { std::cerr << "ERROR: winpmem_x86.sys not found in current directory. Download it from https://github.com/Velocidex/WinPmem/blob/master/kernel/binaries/winpmem_x86.sys .\n"; std::cerr << "ERROR: Memory bandwidth statistics will not be available.\n"; } break; default: return -1; } return 1; } virtual int write_crashdump_header_(struct PmemMemoryInfo * info) { return -1; } }; std::shared_ptr WinPmemMMIORange::pmem; Mutex WinPmemMMIORange::mutex; bool WinPmemMMIORange::writeSupported = false; WinPmemMMIORange::WinPmemMMIORange(uint64 baseAddr_, uint64 /* size_ */, bool readonly_) : startAddr(baseAddr_), readonly(readonly_) { mutex.lock(); if (pmem.get() == NULL) { pmem = std::make_shared(); pmem->install_driver(false); pmem->set_acquisition_mode(PMEM_MODE_IOSPACE); writeSupported = pmem->toggle_write_mode() >= 0; // since it is a global object enable write mode just in case someone needs it } mutex.unlock(); } MMIORange::MMIORange(uint64 baseAddr_, uint64 size_, bool readonly_) { auto hDriver = openMSRDriver(); if (hDriver != INVALID_HANDLE_VALUE) { DWORD reslength = 0; uint64 result = 0; const BOOL status = DeviceIoControl(hDriver, IO_CTL_MMAP_SUPPORT, NULL, 0, &result, sizeof(uint64), &reslength, NULL); CloseHandle(hDriver); if (status == TRUE && reslength == sizeof(uint64) && result == 1) { impl = std::make_shared(baseAddr_, size_, readonly_); return; } else { std::cerr << "MSR.sys does not support mmap operations\n"; } } impl = std::make_shared(baseAddr_, size_, readonly_); } OwnMMIORange::OwnMMIORange(uint64 baseAddr_, uint64 size_, bool /* readonly_ */) { hDriver = openMSRDriver(); MMAP_Request req{}; uint64 result = 0; DWORD reslength = 0; req.address.QuadPart = baseAddr_; req.size = size_; const BOOL status = DeviceIoControl(hDriver, IO_CTL_MMAP, &req, sizeof(MMAP_Request), &result, sizeof(uint64), &reslength, NULL); if (status == FALSE || result == 0) { std::cerr << "Error mapping address 0x" << std::hex << req.address.QuadPart << " with size " << std::dec << req.size << "\n"; throw std::runtime_error("OwnMMIORange error"); } mmapAddr = (char*)result; } uint32 OwnMMIORange::read32(uint64 offset) { return *((uint32*)(mmapAddr + offset)); } uint64 OwnMMIORange::read64(uint64 offset) { return *((uint64*)(mmapAddr + offset)); } void OwnMMIORange::write32(uint64 offset, uint32 val) { *((uint32*)(mmapAddr + offset)) = val; } void OwnMMIORange::write64(uint64 offset, uint64 val) { *((uint64*)(mmapAddr + offset)) = val; } OwnMMIORange::~OwnMMIORange() { MMAP_Request req{}; uint64 result = 0; DWORD reslength = 0; req.address.QuadPart = (LONGLONG)mmapAddr; req.size = 0; DeviceIoControl(hDriver, IO_CTL_MUNMAP, &req, sizeof(MMAP_Request), &result, sizeof(uint64), &reslength, NULL); CloseHandle(hDriver); } #elif __APPLE__ #include "PCIDriverInterface.h" MMIORange::MMIORange(uint64 physical_address, uint64 size_, bool) : mmapAddr(NULL), size(size_) { if (size > 4096) { std::cerr << "PCM Error: the driver does not support mapping of regions > 4KB\n"; return; } if (physical_address) { PCIDriver_mapMemory((uint32_t)physical_address, (uint8_t **)&mmapAddr); } } uint32 MMIORange::read32(uint64 offset) { uint32 val = 0; PCIDriver_readMemory32((uint8_t *)mmapAddr + offset, &val); return val; } uint64 MMIORange::read64(uint64 offset) { uint64 val = 0; PCIDriver_readMemory64((uint8_t *)mmapAddr + offset, &val); return val; } void MMIORange::write32(uint64 offset, uint32 val) { std::cerr << "PCM Error: the driver does not support writing to MMIORange\n"; } void MMIORange::write64(uint64 offset, uint64 val) { std::cerr << "PCM Error: the driver does not support writing to MMIORange\n"; } MMIORange::~MMIORange() { if(mmapAddr) PCIDriver_unmapMemory((uint8_t *)mmapAddr); } #elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) MMIORange::MMIORange(uint64 baseAddr_, uint64 size_, bool readonly_) : fd(-1), mmapAddr(NULL), size(size_), readonly(readonly_) { const int oflag = readonly ? O_RDONLY : O_RDWR; int handle = ::open("/dev/mem", oflag); if (handle < 0) { std::cerr << "opening /dev/mem failed: errno is " << errno << " (" << strerror(errno) << ")\n"; throw std::exception(); } fd = handle; const int prot = readonly ? PROT_READ : (PROT_READ | PROT_WRITE); mmapAddr = (char *)mmap(NULL, size, prot, MAP_SHARED, fd, baseAddr_); if (mmapAddr == MAP_FAILED) { std::cerr << "mmap failed: errno is " << errno << " (" << strerror(errno) << ")\n"; throw std::exception(); } } uint32 MMIORange::read32(uint64 offset) { return *((uint32 *)(mmapAddr + offset)); } uint64 MMIORange::read64(uint64 offset) { return *((uint64 *)(mmapAddr + offset)); } void MMIORange::write32(uint64 offset, uint32 val) { if (readonly) { std::cerr << "PCM Error: attempting to write to a read-only MMIORange\n"; return; } *((uint32 *)(mmapAddr + offset)) = val; } void MMIORange::write64(uint64 offset, uint64 val) { if (readonly) { std::cerr << "PCM Error: attempting to write to a read-only MMIORange\n"; return; } *((uint64 *)(mmapAddr + offset)) = val; } MMIORange::~MMIORange() { if (mmapAddr) munmap(mmapAddr, size); if (fd >= 0) ::close(fd); } #endif } // namespace pcm pcm-202307/src/mmio.h000066400000000000000000000074011445420033100142670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012-2022, Intel Corporation // written by Roman Dementiev // Patrick Konsor // #pragma once /*! \file mmio.h \brief Interface to access memory mapped IO registers */ #include "types.h" #ifdef _MSC_VER #include "windows.h" #include "winpmem\winpmem.h" #include "Winmsrdriver\msrstruct.h" #else #include #endif #include "mutex.h" #include namespace pcm { #ifdef _MSC_VER class MMIORangeInterface { public: virtual uint32 read32(uint64 offset) = 0; virtual uint64 read64(uint64 offset) = 0; virtual void write32(uint64 offset, uint32 val) = 0; virtual void write64(uint64 offset, uint64 val) = 0; virtual ~MMIORangeInterface() {} }; class WinPmemMMIORange : public MMIORangeInterface { static std::shared_ptr pmem; static Mutex mutex; static bool writeSupported; uint64 startAddr; template void writeInternal(uint64 offset, T val) { if (!writeSupported) { std::cerr << "PCM Error: MMIORange writes are not supported by the driver\n"; return; } if (readonly) { std::cerr << "PCM Error: attempting to write to a read-only MMIORange\n"; return; } mutex.lock(); pmem->write(startAddr + offset, val); mutex.unlock(); } template void readInternal(uint64 offset, T & res) { mutex.lock(); pmem->read(startAddr + offset, res); mutex.unlock(); } const bool readonly; public: WinPmemMMIORange(uint64 baseAddr_, uint64 size_, bool readonly_ = true); uint32 read32(uint64 offset) { uint32 result = 0; readInternal(offset, result); return result; } uint64 read64(uint64 offset) { uint64 result = 0; readInternal(offset, result); return result; } void write32(uint64 offset, uint32 val) { writeInternal(offset, val); } void write64(uint64 offset, uint64 val) { writeInternal(offset, val); } }; class OwnMMIORange : public MMIORangeInterface { HANDLE hDriver; char * mmapAddr; OwnMMIORange(const OwnMMIORange&) = delete; OwnMMIORange& operator = (const OwnMMIORange&) = delete; public: OwnMMIORange(uint64 baseAddr_, uint64 size_, bool readonly_ = true); uint32 read32(uint64 offset); uint64 read64(uint64 offset); void write32(uint64 offset, uint32 val); void write64(uint64 offset, uint64 val); ~OwnMMIORange(); }; class MMIORange { std::shared_ptr impl; MMIORange(const MMIORange &) = delete; MMIORange & operator = (const MMIORange &) = delete; public: MMIORange(uint64 baseAddr_, uint64 size_, bool readonly_ = true); uint32 read32(uint64 offset) { return impl->read32(offset); } uint64 read64(uint64 offset) { return impl->read64(offset); } void write32(uint64 offset, uint32 val) { impl->write32(offset, val); } void write64(uint64 offset, uint64 val) { impl->write64(offset, val); } }; #elif defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) class MMIORange { #ifndef __APPLE__ int32 fd; #endif char * mmapAddr; const uint64 size; #ifndef __APPLE__ const bool readonly; #endif MMIORange(const MMIORange &) = delete; MMIORange & operator = (const MMIORange &) = delete; public: MMIORange(uint64 baseAddr_, uint64 size_, bool readonly_ = true); uint32 read32(uint64 offset); uint64 read64(uint64 offset); void write32(uint64 offset, uint32 val); void write64(uint64 offset, uint64 val); ~MMIORange(); }; #endif } // namespace pcm pcm-202307/src/msr.cpp000066400000000000000000000141721445420033100144650ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // Austen Ott // Jim Harris (FreeBSD) #include #include #include #include #ifndef _MSC_VER #include #endif #include "types.h" #include "msr.h" #include "utils.h" #include #ifdef _MSC_VER #include #include "utils.h" #include "Winmsrdriver\msrstruct.h" #include "winring0/OlsApiInitExt.h" #endif #if defined(__FreeBSD__) || defined(__DragonFly__) #include #include #endif #include namespace pcm { #ifdef _MSC_VER extern HMODULE hOpenLibSys; // here comes an implementation for Windows MsrHandle::MsrHandle(uint32 cpu) : cpu_id(cpu) { hDriver = openMSRDriver(); if (hDriver == INVALID_HANDLE_VALUE && hOpenLibSys == NULL) throw std::exception(); } MsrHandle::~MsrHandle() { if (hDriver != INVALID_HANDLE_VALUE) CloseHandle(hDriver); } int32 MsrHandle::write(uint64 msr_number, uint64 value) { if (hDriver != INVALID_HANDLE_VALUE) { MSR_Request req; ULONG64 result; DWORD reslength = 0; req.core_id = cpu_id; req.msr_address = msr_number; req.write_value = value; BOOL status = DeviceIoControl(hDriver, IO_CTL_MSR_WRITE, &req, sizeof(MSR_Request), &result, sizeof(uint64), &reslength, NULL); assert(status && "Error in DeviceIoControl"); return status ? sizeof(uint64) : 0; } cvt_ds cvt; cvt.ui64 = value; ThreadGroupTempAffinity affinity(cpu_id); DWORD status = Wrmsr((DWORD)msr_number, cvt.ui32.low, cvt.ui32.high); return status ? sizeof(uint64) : 0; } int32 MsrHandle::read(uint64 msr_number, uint64 * value) { if (hDriver != INVALID_HANDLE_VALUE) { MSR_Request req; // ULONG64 result; DWORD reslength = 0; req.core_id = cpu_id; req.msr_address = msr_number; BOOL status = DeviceIoControl(hDriver, IO_CTL_MSR_READ, &req, sizeof(MSR_Request), value, sizeof(uint64), &reslength, NULL); assert(status && "Error in DeviceIoControl"); return (int32)reslength; } cvt_ds cvt; cvt.ui64 = 0; ThreadGroupTempAffinity affinity(cpu_id); DWORD status = Rdmsr((DWORD)msr_number, &(cvt.ui32.low), &(cvt.ui32.high)); if (status) *value = cvt.ui64; return status ? sizeof(uint64) : 0; } #elif __APPLE__ // OSX Version MSRAccessor * MsrHandle::driver = NULL; int MsrHandle::num_handles = 0; MsrHandle::MsrHandle(uint32 cpu) { cpu_id = cpu; if (!driver) { driver = new MSRAccessor(); MsrHandle::num_handles = 1; } else { MsrHandle::num_handles++; } } MsrHandle::~MsrHandle() { MsrHandle::num_handles--; if (MsrHandle::num_handles == 0) { delete driver; driver = NULL; } } int32 MsrHandle::write(uint64 msr_number, uint64 value) { return driver->write(cpu_id, msr_number, value); } int32 MsrHandle::read(uint64 msr_number, uint64 * value) { return driver->read(cpu_id, msr_number, value); } int32 MsrHandle::buildTopology(uint32 num_cores, void * ptr) { return driver->buildTopology(num_cores, ptr); } uint32 MsrHandle::getNumInstances() { return driver->getNumInstances(); } uint32 MsrHandle::incrementNumInstances() { return driver->incrementNumInstances(); } uint32 MsrHandle::decrementNumInstances() { return driver->decrementNumInstances(); } #elif defined(__FreeBSD__) || defined(__DragonFly__) MsrHandle::MsrHandle(uint32 cpu) : fd(-1), cpu_id(cpu) { char path[200]; snprintf(path, 200, "/dev/cpuctl%d", cpu); int handle = ::open(path, O_RDWR); if (handle < 0) throw std::exception(); fd = handle; } MsrHandle::~MsrHandle() { if (fd >= 0) ::close(fd); } int32 MsrHandle::write(uint64 msr_number, uint64 value) { cpuctl_msr_args_t args; int ret; args.msr = msr_number; args.data = value; ret = ::ioctl(fd, CPUCTL_WRMSR, &args); if (ret) return ret; return sizeof(value); } int32 MsrHandle::read(uint64 msr_number, uint64 * value) { cpuctl_msr_args_t args; int32 ret; args.msr = msr_number; ret = ::ioctl(fd, CPUCTL_RDMSR, &args); if (ret) return ret; *value = args.data; return sizeof(*value); } #else // here comes a Linux version bool noMSRMode() { static int noMSR = -1; if (noMSR < 0) { noMSR = (safe_getenv("PCM_NO_MSR") == std::string("1")) ? 1 : 0; } return 1 == noMSR; } MsrHandle::MsrHandle(uint32 cpu) : fd(-1), cpu_id(cpu) { if (noMSRMode()) return; constexpr auto allowWritesPath = "/sys/module/msr/parameters/allow_writes"; static bool writesEnabled = false; if (writesEnabled == false) { if (readSysFS(allowWritesPath, true).length() > 0) { writeSysFS(allowWritesPath, "on", false); } writesEnabled = true; } char * path = new char[200]; snprintf(path, 200, "/dev/cpu/%d/msr", cpu); int handle = ::open(path, O_RDWR); if (handle < 0) { // try Android msr device path snprintf(path, 200, "/dev/msr%d", cpu); handle = ::open(path, O_RDWR); } delete[] path; if (handle < 0) { std::cerr << "PCM Error: can't open MSR handle for core " << cpu << " (" << strerror(errno) << ")\n"; std::cerr << "Try no-MSR mode by setting env variable PCM_NO_MSR=1\n"; throw std::exception(); } fd = handle; } MsrHandle::~MsrHandle() { if (fd >= 0) ::close(fd); } int32 MsrHandle::write(uint64 msr_number, uint64 value) { #if 0 static std::mutex m; std::lock_guard g(m); std::cout << "DEBUG: writing MSR 0x" << std::hex << msr_number << " value 0x" << value << " on cpu " << std::dec << cpu_id << std::endl; #endif if (fd < 0) return 0; return ::pwrite(fd, (const void *)&value, sizeof(uint64), msr_number); } int32 MsrHandle::read(uint64 msr_number, uint64 * value) { if (fd < 0) return 0; return ::pread(fd, (void *)value, sizeof(uint64), msr_number); } #endif #ifndef __linux__ bool noMSRMode() { return false; } #endif } // namespace pcm pcm-202307/src/msr.h000066400000000000000000000056321445420033100141330ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // Austen Ott #ifndef CPUCounters_MSR_H #define CPUCounters_MSR_H /*! \file msr.h \brief Low level interface to access hardware model specific registers Implemented and tested for Linux and 64-bit Windows 7 */ #include "types.h" #ifdef _MSC_VER #include "windows.h" #elif __APPLE__ #include #endif #include "mutex.h" #include namespace pcm { bool noMSRMode(); class MsrHandle { #ifdef _MSC_VER HANDLE hDriver; #elif __APPLE__ static MSRAccessor * driver; static int num_handles; #else int32 fd; #endif uint32 cpu_id; MsrHandle(); // forbidden MsrHandle(const MsrHandle &); // forbidden MsrHandle & operator = (const MsrHandle &); // forbidden public: MsrHandle(uint32 cpu); int32 read(uint64 msr_number, uint64 * value); int32 write(uint64 msr_number, uint64 value); int32 getCoreId() { return (int32)cpu_id; } #ifdef __APPLE__ int32 buildTopology(uint32 num_cores, void *); uint32 getNumInstances(); uint32 incrementNumInstances(); uint32 decrementNumInstances(); #endif virtual ~MsrHandle(); }; class SafeMsrHandle { std::shared_ptr pHandle; Mutex mutex; SafeMsrHandle(const SafeMsrHandle &); // forbidden SafeMsrHandle & operator = (const SafeMsrHandle &); // forbidden public: SafeMsrHandle() { } SafeMsrHandle(uint32 core_id) : pHandle(new MsrHandle(core_id)) { } int32 read(uint64 msr_number, uint64 * value) { if (pHandle) return pHandle->read(msr_number, value); *value = 0; return (int32)sizeof(uint64); } int32 write(uint64 msr_number, uint64 value) { if (pHandle) return pHandle->write(msr_number, value); return (int32)sizeof(uint64); } int32 getCoreId() { if (pHandle) return pHandle->getCoreId(); throw std::runtime_error("Core is offline"); } void lock() { mutex.lock(); } void unlock() { mutex.unlock(); } #ifdef __APPLE__ int32 buildTopology(uint32 num_cores, void * p) { if (pHandle) return pHandle->buildTopology(num_cores, p); throw std::exception(); } uint32 getNumInstances() { if (pHandle) return pHandle->getNumInstances(); throw std::exception(); } uint32 incrementNumInstances() { if (pHandle) return pHandle->incrementNumInstances(); throw std::exception(); } uint32 decrementNumInstances() { if (pHandle) return pHandle->decrementNumInstances(); throw std::exception(); } #endif virtual ~SafeMsrHandle() { } }; } // namespace pcm #endif pcm-202307/src/msrtest.cpp000066400000000000000000000051771445420033100153720ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // Austen Ott #include #include #include #include "msr.h" #define NUM_CORES 16 int main() { uint32 i = 0; uint32 res; MsrHandle * cpu_msr[NUM_CORES]; for (i = 0; i < NUM_CORES; ++i) { cpu_msr[i] = new MsrHandle(i); assert(cpu_msr[i]); FixedEventControlRegister ctrl_reg; res = cpu_msr[i]->read(IA32_CR_FIXED_CTR_CTRL, &ctrl_reg.value); assert(res >= 0); ctrl_reg.fields.os0 = 1; ctrl_reg.fields.usr0 = 1; ctrl_reg.fields.any_thread0 = 0; ctrl_reg.fields.enable_pmi0 = 0; ctrl_reg.fields.os1 = 1; ctrl_reg.fields.usr1 = 1; ctrl_reg.fields.any_thread1 = 0; ctrl_reg.fields.enable_pmi1 = 0; ctrl_reg.fields.os2 = 1; ctrl_reg.fields.usr2 = 1; ctrl_reg.fields.any_thread2 = 0; ctrl_reg.fields.enable_pmi2 = 0; res = cpu_msr[i]->write(IA32_CR_FIXED_CTR_CTRL, ctrl_reg.value); assert(res >= 0); // start counting uint64 value = (1ULL << 0) + (1ULL << 1) + (1ULL << 2) + (1ULL << 3) + (1ULL << 32) + (1ULL << 33) + (1ULL << 34); res = cpu_msr[i]->write(IA32_CR_PERF_GLOBAL_CTRL, value); assert(res >= 0); } uint64 counters_before[NUM_CORES][3]; uint64 counters_after[NUM_CORES][3]; for (i = 0; i < NUM_CORES; ++i) { res = cpu_msr[i]->read(INST_RETIRED_ADDR, &counters_before[i][0]); assert(res >= 0); res = cpu_msr[i]->read(CPU_CLK_UNHALTED_THREAD_ADDR, &counters_before[i][1]); assert(res >= 0); res = cpu_msr[i]->read(CPU_CLK_UNHALTED_REF_ADDR, &counters_before[i][2]); assert(res >= 0); } //sleep for some time ::sleep(1); for (i = 0; i < NUM_CORES; ++i) { res = cpu_msr[i]->read(INST_RETIRED_ADDR, &counters_after[i][0]); assert(res >= 0); res = cpu_msr[i]->read(CPU_CLK_UNHALTED_THREAD_ADDR, &counters_after[i][1]); assert(res >= 0); res = cpu_msr[i]->read(CPU_CLK_UNHALTED_REF_ADDR, &counters_after[i][2]); assert(res >= 0); } for (i = 0; i < NUM_CORES; ++i) delete cpu_msr[i]; for (i = 0; i < NUM_CORES; ++i) std::cout << "Core " << i << "\t Instructions: " << (counters_after[i][0] - counters_before[i][0]) << "\t Cycles: " << (counters_after[i][1] - counters_before[i][1]) << "\t IPC: " << double(counters_after[i][0] - counters_before[i][0]) / double(counters_after[i][1] - counters_before[i][1]) << "\n"; } pcm-202307/src/mutex.h000066400000000000000000000025631445420033100144740ustar00rootroot00000000000000#ifndef MUTEX_HEADER_ #define MUTEX_HEADER_ #ifdef _MSC_VER #include #else #include #endif #include namespace pcm { class Mutex { Mutex(const Mutex&) = delete; Mutex& operator = (const Mutex&) = delete; #ifdef _MSC_VER HANDLE mutex_; #else pthread_mutex_t mutex_; #endif public: Mutex() { #ifdef _MSC_VER mutex_ = CreateMutex(NULL, FALSE, NULL); #else pthread_mutex_init(&mutex_, NULL); #endif } virtual ~Mutex() { #ifdef _MSC_VER CloseHandle(mutex_); #else if (pthread_mutex_destroy(&mutex_) != 0) std::cerr << "pthread_mutex_destroy failed\n"; #endif } void lock() { #ifdef _MSC_VER WaitForSingleObject(mutex_, INFINITE); #else if (pthread_mutex_lock(&mutex_) != 0) std::cerr << "pthread_mutex_lock failed\n";; #endif } void unlock() { #ifdef _MSC_VER ReleaseMutex(mutex_); #else if(pthread_mutex_unlock(&mutex_) != 0) std::cerr << "pthread_mutex_unlock failed\n"; #endif } class Scope { Mutex & m; public: Scope(Mutex & m_) : m(m_) { m.lock(); } ~Scope() { m.unlock(); } }; }; } #endif pcm-202307/src/opCode-106.txt000066400000000000000000000114671445420033100154420ustar00rootroot00000000000000#Clockticks #ctr=0,ev_sel=0x1,umask=0x0,en=1,ch_mask=0,fc_mask=0x0,multiplier=1,divider=1,hname=Clockticks,vname=Total # Inbound (PCIe device DMA into system) payload events ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part0 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part1 (2nd x4) ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part2 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part3 (4th x4) ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part4 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part5 (2nd x4) ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part6 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part7 (4th x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part0 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part1 (2nd x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part2 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part3 (4th x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part4 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part5 (2nd x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part6 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part7 (4th x4) # Outbound (CPU MMIO to the PCIe device) payload events ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part0 (1st x16/x8/x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part1 (2nd x4) ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part2 (2nd x8/3rd x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part3 (4th x4) ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part4 (1st x16/x8/x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part5 (2nd x4) ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part6 (2nd x8/3rd x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part7 (4th x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part0 (1st x16/x8/x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part1 (2nd x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part2 (2nd x8/3rd x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part3 (4th x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part4 (1st x16/x8/x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part5 (2nd x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part6 (2nd x8/3rd x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part7 (4th x4) # IOMMU events ctr=0,ev_sel=0x40,umask=0x01,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOTLB Lookup,vname=Total ctr=1,ev_sel=0x40,umask=0x20,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOTLB Miss,vname=Total ctr=2,ev_sel=0x40,umask=0x80,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=Ctxt Cache Hit,vname=Total ctr=3,ev_sel=0x41,umask=0x10,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=512G Cache Hit,vname=Total ctr=0,ev_sel=0x41,umask=0x08,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=1G Cache Hit,vname=Total ctr=1,ev_sel=0x41,umask=0x04,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=2M Cache Hit,vname=Total ctr=2,ev_sel=0x41,umask=0x02,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=4K Cache Hit,vname=Total ctr=3,ev_sel=0x41,umask=0x40,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOMMU Mem Access,vname=Total pcm-202307/src/opCode-134.txt000066400000000000000000000114661445420033100154420ustar00rootroot00000000000000#Clockticks #ctr=0,ev_sel=0x1,umask=0x0,en=1,ch_mask=0,fc_mask=0x0,multiplier=1,divider=1,hname=Clockticks,vname=Total # Inbound (PCIe device DMA into system) payload events ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part0 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part1 (2nd x4) ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part2 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part3 (4th x4) ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part4 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part5 (2nd x4) ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part6 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part7 (4th x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part0 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part1 (2nd x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part2 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part3 (4th x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part4 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part5 (2nd x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part6 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part7 (4th x4) # Outbound (CPU MMIO to the PCIe device) payload events ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part0 (1st x16/x8/x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part1 (2nd x4) ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part2 (2nd x8/3rd x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part3 (4th x4) ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part4 (1st x16/x8/x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part5 (2nd x4) ctr=2,ev_sel=0x83,umask=0x80,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part6 (2nd x8/3rd x4) ctr=3,ev_sel=0x83,umask=0x80,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part7 (4th x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part0 (1st x16/x8/x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part1 (2nd x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part2 (2nd x8/3rd x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part3 (4th x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part4 (1st x16/x8/x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part5 (2nd x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part6 (2nd x8/3rd x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part7 (4th x4) # IOMMU events ctr=0,ev_sel=0x40,umask=0x01,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOTLB Lookup,vname=Total ctr=1,ev_sel=0x40,umask=0x20,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOTLB Miss,vname=Total ctr=2,ev_sel=0x40,umask=0x80,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=Ctxt Cache Hit,vname=Total ctr=3,ev_sel=0x41,umask=0x10,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=512G Cache Hit,vname=Total ctr=0,ev_sel=0x41,umask=0x08,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=1G Cache Hit,vname=Total ctr=1,ev_sel=0x41,umask=0x04,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=2M Cache Hit,vname=Total ctr=2,ev_sel=0x41,umask=0x02,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=4K Cache Hit,vname=Total ctr=3,ev_sel=0x41,umask=0x40,en=1,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOMMU Mem Access,vname=Totalpcm-202307/src/opCode-143-accel.txt000066400000000000000000000025401445420033100165000ustar00rootroot00000000000000# IAA events ctr=0,ev_sel=0x1,ev_cat=0x1,en=1,multiplier=32,divider=1,hname=IAA,vname=Inbound_BW(Bps) ctr=1,ev_sel=0x2,ev_cat=0x1,en=1,multiplier=512,divider=1,hname=IAA,vname=Outbound_BW(Bps) ctr=2,ev_sel=0x5,ev_cat=0x0,en=1,multiplier=1,divider=1,hname=IAA,vname=ShareWQ_ReqNb ctr=3,ev_sel=0x10,ev_cat=0x0,en=1,multiplier=1,divider=1,hname=IAA,vname=DedicateWQ_ReqNb # DSA events ctr=0,ev_sel=0x1,ev_cat=0x1,en=1,multiplier=32,divider=1,hname=DSA,vname=Inbound_BW(Bps) ctr=1,ev_sel=0x2,ev_cat=0x1,en=1,multiplier=32,divider=1,hname=DSA,vname=Outbound_BW(Bps) ctr=2,ev_sel=0x5,ev_cat=0x0,en=1,multiplier=1,divider=1,hname=DSA,vname=ShareWQ_ReqNb ctr=3,ev_sel=0x10,ev_cat=0x0,en=1,multiplier=1,divider=1,hname=DSA,vname=DedicateWQ_ReqNb # QAT events ctr=0,ev_sel=0x6,ev_cat=0x1,en=1,multiplier=1048576,divider=1,hname=QAT,vname=Inbound_BW(Bps) ctr=1,ev_sel=0x7,ev_cat=0x1,en=1,multiplier=1048576,divider=1,hname=QAT,vname=Outbound_BW(Bps) ctr=2,ev_sel=0xB,ev_cat=0x1,en=1,multiplier=1,divider=1,hname=QAT,vname=util_comp0(%) ctr=3,ev_sel=0xC,ev_cat=0x1,en=1,multiplier=1,divider=1,hname=QAT,vname=util_decomp0(%) ctr=4,ev_sel=0xD,ev_cat=0x1,en=1,multiplier=1,divider=1,hname=QAT,vname=util_decomp1(%) ctr=5,ev_sel=0xE,ev_cat=0x1,en=1,multiplier=1,divider=1,hname=QAT,vname=util_decomp2(%) ctr=6,ev_sel=0xF,ev_cat=0x1,en=1,multiplier=1,divider=1,hname=QAT,vname=util_xlt0(%) pcm-202307/src/opCode-143.txt000066400000000000000000000103411445420033100154310ustar00rootroot00000000000000#Clockticks #ctr=0,ev_sel=0x1,umask=0x0,en=1,ch_mask=0,fc_mask=0x0,multiplier=1,divider=1,hname=Clockticks,vname=Total # Inbound (PCIe device DMA into system) payload events ctr=0,ev_sel=0x83,umask=0x1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part0 ctr=1,ev_sel=0x83,umask=0x1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part1 ctr=0,ev_sel=0x83,umask=0x1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part2 ctr=1,ev_sel=0x83,umask=0x1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part3 ctr=0,ev_sel=0x83,umask=0x1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part4 ctr=1,ev_sel=0x83,umask=0x1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part5 ctr=0,ev_sel=0x83,umask=0x1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part6 ctr=1,ev_sel=0x83,umask=0x1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=IB write,vname=Part7 ctr=0,ev_sel=0x83,umask=0x4,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part0 ctr=1,ev_sel=0x83,umask=0x4,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part1 ctr=0,ev_sel=0x83,umask=0x4,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part2 ctr=1,ev_sel=0x83,umask=0x4,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part3 ctr=0,ev_sel=0x83,umask=0x4,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part4 ctr=1,ev_sel=0x83,umask=0x4,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part5 ctr=0,ev_sel=0x83,umask=0x4,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part6 ctr=1,ev_sel=0x83,umask=0x4,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=IB read,vname=Part7 # Outbound (CPU MMIO to the PCIe device) payload events ctr=2,ev_sel=0x83,umask=0x80,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part0 ctr=3,ev_sel=0x83,umask=0x80,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part1 ctr=2,ev_sel=0x83,umask=0x80,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part2 ctr=3,ev_sel=0x83,umask=0x80,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part3 ctr=2,ev_sel=0x83,umask=0x80,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part4 ctr=3,ev_sel=0x83,umask=0x80,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part5 ctr=2,ev_sel=0x83,umask=0x80,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part6 ctr=3,ev_sel=0x83,umask=0x80,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=OB read,vname=Part7 ctr=2,ev_sel=0xc0,umask=0x1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part0 ctr=3,ev_sel=0xc0,umask=0x1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part1 ctr=2,ev_sel=0xc0,umask=0x1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part2 ctr=3,ev_sel=0xc0,umask=0x1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part3 ctr=2,ev_sel=0xc0,umask=0x1,ch_mask=16,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part4 ctr=3,ev_sel=0xc0,umask=0x1,ch_mask=32,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part5 ctr=2,ev_sel=0xc0,umask=0x1,ch_mask=64,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part6 ctr=3,ev_sel=0xc0,umask=0x1,ch_mask=128,fc_mask=0x7,multiplier=4,divider=1,hname=OB write,vname=Part7 # IOMMU events ctr=0,ev_sel=0x40,umask=0x01,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOTLB Lookup,vname=Total ctr=1,ev_sel=0x40,umask=0x20,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOTLB Miss,vname=Total ctr=2,ev_sel=0x40,umask=0x80,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=Ctxt Cache Hit,vname=Total ctr=3,ev_sel=0x41,umask=0x10,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=256T Cache Hit,vname=Total ctr=0,ev_sel=0x41,umask=0x08,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=512G Cache Hit,vname=Total ctr=1,ev_sel=0x41,umask=0x04,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=1G Cache Hit,vname=Total ctr=2,ev_sel=0x41,umask=0x02,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=2M Cache Hit,vname=Total ctr=3,ev_sel=0x41,umask=0xc0,ch_mask=0x0,fc_mask=0x0,multiplier=1,divider=1,hname=IOMMU Mem Access,vname=Total pcm-202307/src/opCode-85.txt000066400000000000000000000055161445420033100153660ustar00rootroot00000000000000# Inbound (PCIe device DMA into system) payload events ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB write (bytes),vname=Part0 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB write (bytes),vname=Part1 (2nd x4) ctr=0,ev_sel=0x83,umask=0x1,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB write (bytes),vname=Part2 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x1,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB write (bytes),vname=Part3 (4th x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=IB read (bytes),vname=Part0 (1st x16/x8/x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=IB read (bytes),vname=Part1 (2nd x4) ctr=0,ev_sel=0x83,umask=0x4,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=IB read (bytes),vname=Part2 (2nd x8/3rd x4) ctr=1,ev_sel=0x83,umask=0x4,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=IB read (bytes),vname=Part3 (4th x4) # Outbound (CPU MMIO to the PCIe device) payload events ctr=2,ev_sel=0xc0,umask=0x4,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB read (bytes),vname=Part0 (1st x16/x8/x4) ctr=3,ev_sel=0xc0,umask=0x4,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB read (bytes),vname=Part1 (2nd x4) ctr=2,ev_sel=0xc0,umask=0x4,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB read (bytes),vname=Part2 (2nd x8/3rd x4) ctr=3,ev_sel=0xc0,umask=0x4,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB read (bytes),vname=Part3 (4th x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=1,fc_mask=0x7,multiplier=4,divider=1,hname=OB write (bytes),vname=Part0 (1st x16/x8/x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=2,fc_mask=0x7,multiplier=4,divider=1,hname=OB write (bytes),vname=Part1 (2nd x4) ctr=2,ev_sel=0xc0,umask=0x1,en=1,ch_mask=4,fc_mask=0x7,multiplier=4,divider=1,hname=OB write (bytes),vname=Part2 (2nd x8/3rd x4) ctr=3,ev_sel=0xc0,umask=0x1,en=1,ch_mask=8,fc_mask=0x7,multiplier=4,divider=1,hname=OB write (bytes),vname=Part3 (4th x4) # VTd events ctr=0,ev_sel=0x41,umask=0x1,en=1,ch_mask=1,fc_mask=0x7,multiplier=1,divider=1,hname=IOTLB Hit,vname=Total ctr=1,ev_sel=0x41,umask=0x20,en=1,ch_mask=1,fc_mask=0x7,multiplier=1,divider=1,hname=IOTLB Miss,vname=Total ctr=2,ev_sel=0x41,umask=0x02,en=1,ch_mask=4,fc_mask=0x7,multiplier=1,divider=1,hname=VT-d CTXT Miss,vname=Total ctr=3,ev_sel=0x41,umask=0x04,en=1,ch_mask=4,fc_mask=0x7,multiplier=1,divider=1,hname=VT-d L1 Miss,vname=Total ctr=0,ev_sel=0x41,umask=0x08,en=1,ch_mask=4,fc_mask=0x7,multiplier=1,divider=1,hname=VT-d L2 Miss,vname=Total ctr=1,ev_sel=0x41,umask=0x10,en=1,ch_mask=1,fc_mask=0x7,multiplier=1,divider=1,hname=VT-d L3 Miss,vname=Total ctr=2,ev_sel=0x84,umask=0x04,en=1,ch_mask=0x10,fc_mask=0x7,multiplier=1,divider=1,hname=VT-d Mem Read,vname=Total pcm-202307/src/pci.cpp000066400000000000000000000432321445420033100144360ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev, // Pat Fay // Austen Ott // Jim Harris (FreeBSD) #include #include #include #include #include #include #include "pci.h" #ifndef _MSC_VER #include #include #include #endif #ifdef _MSC_VER #include #include "Winmsrdriver\msrstruct.h" #include "winring0/OlsDef.h" #include "winring0/OlsApiInitExt.h" #include "utils.h" #endif #if defined (__FreeBSD__) || defined(__DragonFly__) #include #endif namespace pcm { #ifdef _MSC_VER extern HMODULE hOpenLibSys; PciHandle::PciHandle(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) : bus(bus_), device(device_), function(function_), pciAddress(PciBusDevFunc(bus_, device_, function_)) { if (groupnr_ != 0) { std::cerr << "Non-zero PCI group segments are not supported in PCM/Windows\n"; throw std::exception(); } hDriver = openMSRDriver(); if (hDriver == INVALID_HANDLE_VALUE && hOpenLibSys == NULL) throw std::exception(); } bool PciHandle::exists(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) { if (groupnr_ != 0) { std::cerr << "Non-zero PCI group segments are not supported in PCM/Windows\n"; return false; } if (hOpenLibSys != NULL) { DWORD addr(PciBusDevFunc(bus_, device_, function_)); DWORD result = 0; return ReadPciConfigDwordEx(addr, 0, &result)?true:false; } HANDLE tempHandle = openMSRDriver(); if (tempHandle == INVALID_HANDLE_VALUE) return false; // TODO: check device availability CloseHandle(tempHandle); return true; } int32 PciHandle::read32(uint64 offset, uint32 * value) { if (hDriver != INVALID_HANDLE_VALUE) { PCICFG_Request req; ULONG64 result = 0; DWORD reslength = 0; req.bus = (ULONG)bus; req.dev = (ULONG)device; req.func = (ULONG)function; req.bytes = sizeof(uint32); req.reg = (ULONG)offset; BOOL status = DeviceIoControl(hDriver, IO_CTL_PCICFG_READ, &req, (DWORD)sizeof(PCICFG_Request), &result, (DWORD)sizeof(uint64), &reslength, NULL); *value = (uint32)result; if (!status) { //std::cerr << "Error reading PCI Config space at bus "<> 16) & 0xffff; //if (vendor_id == PCM_INTEL_PCI_VENDOR_ID) { if (vendor_id != 0xffff && device_id != 0xffff) { return true; } else { return false; } } int32 PciHandle::read32(uint64 offset, uint32 * value) { uint32_t pci_address = FORM_PCI_ADDR(bus, device, function, (uint32_t)offset); return PCIDriver_read32(pci_address, value); } int32 PciHandle::write32(uint64 offset, uint32 value) { uint32_t pci_address = FORM_PCI_ADDR(bus, device, function, (uint32_t)offset); return PCIDriver_write32(pci_address, value); } int32 PciHandle::read64(uint64 offset, uint64 * value) { uint32_t pci_address = FORM_PCI_ADDR(bus, device, function, (uint32_t)offset); return PCIDriver_read64(pci_address, value); } PciHandle::~PciHandle() { } #elif defined (__FreeBSD__) || defined(__DragonFly__) PciHandle::PciHandle(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) : fd(-1), bus(bus_), device(device_), function(function_) { if (groupnr_ != 0) { std::cout << "ERROR: non-zero PCI segment groupnr is not supported in this PciHandle implementation\n"; throw std::exception(); } int handle = ::open("/dev/pci", O_RDWR); if (handle < 0) throw std::exception(); fd = handle; } bool PciHandle::exists(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) { if (groupnr_ != 0) { std::cerr << "Non-zero PCI group segments are not supported in PCM/FreeBSD/DragonFlyBSD\n"; return false; } struct pci_conf_io pc; struct pci_match_conf pattern; struct pci_conf conf[4]; int fd; int ret; fd = ::open("/dev/pci", O_RDWR, 0); if (fd < 0) return false; bzero(&pc, sizeof(pc)); pattern.pc_sel.pc_bus = bus_; pattern.pc_sel.pc_dev = device_; pattern.pc_sel.pc_func = function_; pattern.flags = (pci_getconf_flags)(PCI_GETCONF_MATCH_BUS | PCI_GETCONF_MATCH_DEV | PCI_GETCONF_MATCH_FUNC); pc.pat_buf_len = sizeof(pattern); pc.patterns = &pattern; pc.num_patterns = 1; pc.match_buf_len = sizeof(conf); pc.matches = conf; ret = ioctl(fd, PCIOCGETCONF, &pc); ::close(fd); if (ret) return false; if (pc.status != PCI_GETCONF_LAST_DEVICE) return false; if (pc.num_matches > 0) return true; return false; } int32 PciHandle::read32(uint64 offset, uint32 * value) { struct pci_io pi; int ret; pi.pi_sel.pc_domain = 0; pi.pi_sel.pc_bus = bus; pi.pi_sel.pc_dev = device; pi.pi_sel.pc_func = function; pi.pi_reg = offset; pi.pi_width = 4; ret = ioctl(fd, PCIOCREAD, &pi); if (ret) return ret; *value = pi.pi_data; return sizeof(*value); } int32 PciHandle::write32(uint64 offset, uint32 value) { struct pci_io pi; int ret; pi.pi_sel.pc_domain = 0; pi.pi_sel.pc_bus = bus; pi.pi_sel.pc_dev = device; pi.pi_sel.pc_func = function; pi.pi_reg = offset; pi.pi_width = 4; pi.pi_data = value; ret = ioctl(fd, PCIOCWRITE, &pi); if (ret) return ret; return sizeof(value); } int32 PciHandle::read64(uint64 offset, uint64 * value) { struct pci_io pi; int32 ret; pi.pi_sel.pc_domain = 0; pi.pi_sel.pc_bus = bus; pi.pi_sel.pc_dev = device; pi.pi_sel.pc_func = function; pi.pi_reg = offset; pi.pi_width = 4; ret = ioctl(fd, PCIOCREAD, &pi); if (ret) return ret; *value = pi.pi_data; pi.pi_reg += 4; ret = ioctl(fd, PCIOCREAD, &pi); if (ret) return ret; *value += ((uint64)pi.pi_data << 32); return sizeof(value); } PciHandle::~PciHandle() { if (fd >= 0) ::close(fd); } #else // Linux implementation int openHandle(uint32 groupnr_, uint32 bus, uint32 device, uint32 function) { std::ostringstream path(std::ostringstream::out); path << std::hex << "/proc/bus/pci/"; if (groupnr_) { path << std::setw(4) << std::setfill('0') << groupnr_ << ":"; } path << std::setw(2) << std::setfill('0') << bus << "/" << std::setw(2) << std::setfill('0') << device << "." << function; // std::cout << "PciHandle: Opening "<= 0) ::close(fd); } int PciHandle::openMcfgTable() { const std::vector base_paths = {"/sys/firmware/acpi/tables/MCFG", "/sys/firmware/acpi/tables/MCFG1"}; std::vector paths = base_paths; for (const auto & p: base_paths) { paths.push_back(std::string("/pcm") + p); } int handle = -1; for (const auto & p: paths) { if (handle < 0) { handle = ::open(p.c_str(), O_RDONLY); } } if (handle < 0) { for (const auto & p: paths) { std::cerr << "Can't open MCFG table. Check permission of " << p << "\n"; } } return handle; } #ifndef PCM_USE_PCI_MM_LINUX PciHandleM::PciHandleM(uint32 bus_, uint32 device_, uint32 function_) : fd(-1), bus(bus_), device(device_), function(function_), base_addr(0) { int handle = ::open("/dev/mem", O_RDWR); if (handle < 0) throw std::exception(); fd = handle; int mcfg_handle = PciHandle::openMcfgTable(); if (mcfg_handle < 0) throw std::exception(); int32 result = ::pread(mcfg_handle, (void *)&base_addr, sizeof(uint64), 44); if (result != sizeof(uint64)) { ::close(mcfg_handle); throw std::exception(); } unsigned char max_bus = 0; result = ::pread(mcfg_handle, (void *)&max_bus, sizeof(unsigned char), 55); ::close(mcfg_handle); if (result != sizeof(unsigned char)) { throw std::exception(); } if (bus > (unsigned)max_bus) { std::cout << "WARNING: Requested bus number " << bus << " is larger than the max bus number " << (unsigned)max_bus << "\n"; throw std::exception(); } // std::cout << "PCI config base addr: "<< std::hex << base_addr<< "\n" << std::dec; base_addr += (bus * 1024ULL * 1024ULL + device * 32ULL * 1024ULL + function * 4ULL * 1024ULL); } bool PciHandleM::exists(uint32 /*groupnr_*/, uint32 /* bus_*/, uint32 /* device_ */, uint32 /* function_ */) { int handle = ::open("/dev/mem", O_RDWR); if (handle < 0) { perror("error opening /dev/mem"); return false; } ::close(handle); handle = PciHandle::openMcfgTable(); if (handle < 0) { return false; } ::close(handle); return true; } int32 PciHandleM::read32(uint64 offset, uint32 * value) { return ::pread(fd, (void *)value, sizeof(uint32), offset + base_addr); } int32 PciHandleM::write32(uint64 offset, uint32 value) { return ::pwrite(fd, (const void *)&value, sizeof(uint32), offset + base_addr); } int32 PciHandleM::read64(uint64 offset, uint64 * value) { return ::pread(fd, (void *)value, sizeof(uint64), offset + base_addr); } PciHandleM::~PciHandleM() { if (fd >= 0) ::close(fd); } #endif // PCM_USE_PCI_MM_LINUX // mmapped I/O version MCFGHeader PciHandleMM::mcfgHeader; std::vector PciHandleMM::mcfgRecords; const std::vector & PciHandleMM::getMCFGRecords() { readMCFG(); return mcfgRecords; } void PciHandleMM::readMCFG() { if (mcfgRecords.size() > 0) return; // already initialized int mcfg_handle = PciHandle::openMcfgTable(); if (mcfg_handle < 0) { throw std::exception(); } ssize_t read_bytes = ::read(mcfg_handle, (void *)&mcfgHeader, sizeof(MCFGHeader)); if (read_bytes == 0) { ::close(mcfg_handle); std::cerr << "PCM Error: Cannot read MCFG-table\n"; throw std::exception(); } const unsigned segments = mcfgHeader.nrecords(); #ifdef PCM_DEBUG mcfgHeader.print(); std::cout << "PCM Debug: total segments: " << segments << "\n"; #endif for (unsigned int i = 0; i < segments; ++i) { MCFGRecord record; read_bytes = ::read(mcfg_handle, (void *)&record, sizeof(MCFGRecord)); if (read_bytes == 0) { ::close(mcfg_handle); std::cerr << "PCM Error: Cannot read MCFG-table (2)\n"; throw std::exception(); } #ifdef PCM_DEBUG std::cout << "PCM Debug: segment " << std::dec << i << " "; record.print(); #endif mcfgRecords.push_back(record); } ::close(mcfg_handle); } PciHandleMM::PciHandleMM(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_) : fd(-1), mmapAddr(NULL), bus(bus_), device(device_), function(function_), base_addr(0) { int handle = ::open("/dev/mem", O_RDWR); if (handle < 0) throw std::exception(); fd = handle; readMCFG(); unsigned segment = 0; for ( ; segment < mcfgRecords.size(); ++segment) { if (mcfgRecords[segment].PCISegmentGroupNumber == groupnr_ && mcfgRecords[segment].startBusNumber <= bus_ && bus <= mcfgRecords[segment].endBusNumber) break; } if (segment == mcfgRecords.size()) { std::cerr << "PCM Error: (group " << groupnr_ << ", bus " << bus_ << ") not found in the MCFG table.\n"; throw std::exception(); } else { #ifdef PCM_DEBUG std::cout << "PCM Debug: (group " << groupnr_ << ", bus " << bus_ << ") found in the MCFG table in segment " << segment << "\n"; #endif } base_addr = mcfgRecords[segment].baseAddress; base_addr += (bus * 1024ULL * 1024ULL + device * 32ULL * 1024ULL + function * 4ULL * 1024ULL); mmapAddr = (char *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, base_addr); if (mmapAddr == MAP_FAILED) { std::cout << "mmap failed: errno is " << errno << "\n"; throw std::exception(); } } bool PciHandleMM::exists(uint32 /*groupnr_*/, uint32 /*bus_*/, uint32 /*device_*/, uint32 /*function_*/) { int handle = ::open("/dev/mem", O_RDWR); if (handle < 0) { perror("error opening /dev/mem"); return false; } ::close(handle); handle = PciHandle::openMcfgTable(); if (handle < 0) { return false; } ::close(handle); return true; } int32 PciHandleMM::read32(uint64 offset, uint32 * value) { *value = *((uint32 *)(mmapAddr + offset)); return sizeof(uint32); } int32 PciHandleMM::write32(uint64 offset, uint32 value) { *((uint32 *)(mmapAddr + offset)) = value; return sizeof(uint32); } int32 PciHandleMM::read64(uint64 offset, uint64 * value) { read32(offset, (uint32 *)value); read32(offset + sizeof(uint32), ((uint32 *)value) + 1); return sizeof(uint64); } PciHandleMM::~PciHandleMM() { if (mmapAddr) munmap(mmapAddr, 4096); if (fd >= 0) ::close(fd); } #endif } // namespace pcm pcm-202307/src/pci.h000066400000000000000000000130051445420033100140760ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // Pat Fay // Jim Harris (FreeBSD) #ifndef CPUCounters_PCI_H #define CPUCounters_PCI_H /*! \file pci.h \brief Low level interface to access PCI configuration space */ #include "types.h" #ifdef _MSC_VER #include "windows.h" #else #include #endif #ifdef __APPLE__ #include "PCIDriverInterface.h" #endif #include namespace pcm { class PciHandle { #ifdef _MSC_VER HANDLE hDriver; #else int32 fd; #endif uint32 bus; uint32 device; uint32 function; #ifdef _MSC_VER DWORD pciAddress; #endif friend class PciHandleM; friend class PciHandleMM; PciHandle(); // forbidden PciHandle(const PciHandle &); // forbidden PciHandle & operator = (const PciHandle &); // forbidden public: PciHandle(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_); static bool exists(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_); int32 read32(uint64 offset, uint32 * value); int32 write32(uint64 offset, uint32 value); int32 read64(uint64 offset, uint64 * value); virtual ~PciHandle(); protected: static int openMcfgTable(); }; #ifdef _MSC_VER typedef PciHandle PciHandleType; #elif __APPLE__ // This may need to change if it can be implemented for OSX typedef PciHandle PciHandleType; #elif defined(__FreeBSD__) || defined(__DragonFly__) typedef PciHandle PciHandleType; #else // read/write PCI config space using physical memory class PciHandleM { #ifdef _MSC_VER #else int32 fd; #endif uint32 bus; uint32 device; uint32 function; uint64 base_addr; PciHandleM() = delete; // forbidden PciHandleM(PciHandleM &) = delete; // forbidden PciHandleM & operator = (PciHandleM &) = delete; // forbidden public: PciHandleM(uint32 bus_, uint32 device_, uint32 function_); static bool exists(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_); int32 read32(uint64 offset, uint32 * value); int32 write32(uint64 offset, uint32 value); int32 read64(uint64 offset, uint64 * value); virtual ~PciHandleM(); }; #ifndef _MSC_VER // read/write PCI config space using physical memory using mmapped file I/O class PciHandleMM { int32 fd; char * mmapAddr; uint32 bus; uint32 device; uint32 function; uint64 base_addr; #ifdef __linux__ static MCFGHeader mcfgHeader; static std::vector mcfgRecords; static void readMCFG(); #endif PciHandleMM() = delete; // forbidden PciHandleMM(const PciHandleMM &) = delete; // forbidden PciHandleMM & operator = (const PciHandleMM &) = delete; public: PciHandleMM(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_); static bool exists(uint32 groupnr_, uint32 bus_, uint32 device_, uint32 function_); int32 read32(uint64 offset, uint32 * value); int32 write32(uint64 offset, uint32 value); int32 read64(uint64 offset, uint64 * value); virtual ~PciHandleMM(); #ifdef __linux__ static const std::vector & getMCFGRecords(); #endif }; #ifdef PCM_USE_PCI_MM_LINUX #define PciHandleType PciHandleMM #else #define PciHandleType PciHandle #endif #endif // _MSC_VER #endif inline void getMCFGRecords(std::vector & mcfg) { #ifdef __linux__ mcfg = PciHandleMM::getMCFGRecords(); #else MCFGRecord segment; segment.PCISegmentGroupNumber = 0; segment.startBusNumber = 0; segment.endBusNumber = 0xff; mcfg.push_back(segment); #endif } template inline void forAllIntelDevices(F f, int requestedDevice = -1, int requestedFunction = -1) { std::vector mcfg; getMCFGRecords(mcfg); auto probe = [&f](const uint32 group, const uint32 bus, const uint32 device, const uint32 function) { uint32 value = 0; try { PciHandleType h(group, bus, device, function); h.read32(0, &value); } catch(...) { // invalid bus:devicei:function return; } const uint32 vendor_id = value & 0xffff; const uint32 device_id = (value >> 16) & 0xffff; if (vendor_id != PCM_INTEL_PCI_VENDOR_ID) { return; } f(group, bus, device, function, device_id); }; for (uint32 s = 0; s < (uint32)mcfg.size(); ++s) { const auto group = mcfg[s].PCISegmentGroupNumber; for (uint32 bus = (uint32)mcfg[s].startBusNumber; bus <= (uint32)mcfg[s].endBusNumber; ++bus) { auto forAllFunctions = [requestedFunction,&probe](const uint32 group, const uint32 bus, const uint32 device) { if (requestedFunction < 0) { for (uint32 function = 0 ; function < 8; ++function) { probe(group, bus, device, function); } } else { probe(group, bus, device, requestedFunction); } }; if (requestedDevice < 0) { for (uint32 device = 0 ; device < 32; ++device) { forAllFunctions(group, bus, device); } } else { forAllFunctions(group, bus, requestedDevice); } } } } } // namespace pcm #endif pcm-202307/src/pcm-accel.cpp000066400000000000000000000765111445420033100155150ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2022, Intel Corporation // written by White.Hu #include "cpucounters.h" #ifdef _MSC_VER #pragma warning(disable : 4996) // for sprintf #include #include "windows/windriver.h" #else #include #endif #include #include #include #include // std::length_error #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #ifdef __linux__ #include #endif #include "lspci.h" #include "utils.h" using namespace std; using namespace pcm; #define PCM_DELAY_DEFAULT 3.0 // in seconds class idx_ccr { public: virtual uint64_t get_event_select() const = 0; virtual void set_event_select(uint64_t value) = 0; virtual uint64_t get_event_category() const = 0; virtual void set_event_category(uint64_t value) = 0; virtual uint64_t get_enable() const = 0; virtual void set_enable(uint64_t value) = 0; virtual uint64_t get_ccr_value() const = 0; virtual void set_ccr_value(uint64_t value) = 0; virtual ~idx_ccr() {}; }; class spr_idx_ccr: public idx_ccr { public: spr_idx_ccr(uint64_t &v){ ccr_value = &v; } virtual uint64_t get_event_select() const { //EVENT bit, bit 32 return ((*ccr_value >> 32) & 0xFFFFFFF); } virtual void set_event_select(uint64_t value) { *ccr_value |= (value << 32); } virtual uint64_t get_event_category() const { //EVENT Categorg, bit 8 return ((*ccr_value >> 8) & 0xF); } virtual void set_event_category(uint64_t value) { *ccr_value |= (value << 8); } virtual uint64_t get_enable() const { //Enable counter, bit 0 return ((*ccr_value >> 0 ) & 0x01); } virtual void set_enable(uint64_t value) { *ccr_value |= (value << 0); } virtual uint64_t get_ccr_value() const { return *ccr_value; } virtual void set_ccr_value(uint64_t value) { *ccr_value = value; } private: uint64_t* ccr_value = NULL; }; idx_ccr* idx_get_ccr(uint64_t& ccr) { return new spr_idx_ccr(ccr); } typedef enum { ACCEL_IAA, ACCEL_DSA, ACCEL_QAT, ACCEL_MAX, } ACCEL_IP; enum IDXPerfmonField { DPF_BASE = 0x100, //start from 0x100 to different with PerfmonField in cpucounter.h EVENT_CATEGORY, FILTER_WQ, FILTER_ENG, FILTER_TC, FILTER_PGSZ, FILTER_XFERSZ }; typedef enum { SOCKET_MAP, NUMA_MAP, } ACCEL_DEV_LOC_MAPPING; const std::vector idx_accel_mapping = { PCM::IDX_IAA, PCM::IDX_DSA, PCM::IDX_QAT }; #define ACCEL_IP_DEV_COUNT_MAX (16) typedef uint32_t h_id; typedef uint32_t v_id; typedef std::map,uint64_t> ctr_data; typedef std::vector dev_content; typedef std::vector accel_content; accel_content accel_results(ACCEL_MAX, dev_content(ACCEL_IP_DEV_COUNT_MAX, ctr_data())); struct accel_counter : public counter { //filter config for IDX Accelerator. uint32_t cfr_wq = 0; uint32_t cfr_eng = 0; uint32_t cfr_tc = 0; uint32_t cfr_pgsz = 0; uint32_t cfr_xfersz = 0; }; typedef struct { PCM *m; ACCEL_IP accel; accel_counter ctr; vector ctrs; } accel_evt_parse_context; uint32_t getNumOfAccelDevs(PCM *m, ACCEL_IP accel) { uint32_t dev_count = 0; if (accel >= ACCEL_MAX || m == NULL) return 0; switch (accel) { case ACCEL_IAA: dev_count = m->getNumOfIDXAccelDevs(PCM::IDX_IAA); break; case ACCEL_DSA: dev_count = m->getNumOfIDXAccelDevs(PCM::IDX_DSA); break; case ACCEL_QAT: dev_count = m->getNumOfIDXAccelDevs(PCM::IDX_QAT); break; default: dev_count = 0; break; } return dev_count; } uint32_t getMaxNumOfAccelCtrs(PCM *m, ACCEL_IP accel) { uint32_t ctr_count = 0; if (accel >= ACCEL_MAX || m == NULL) return 0; switch (accel) { case ACCEL_IAA: case ACCEL_DSA: case ACCEL_QAT: ctr_count = m->getMaxNumOfIDXAccelCtrs(accel); break; default: ctr_count = 0; break; } return ctr_count; } int32_t programAccelCounters(PCM *m, ACCEL_IP accel, std::vector& ctrs) { vector rawEvents; vector filters_wq, filters_tc, filters_pgsz, filters_xfersz, filters_eng; if (m == NULL || accel >= ACCEL_MAX || ctrs.size() == 0 || ctrs.size() > getMaxNumOfAccelCtrs(m, accel)) return -1; switch (accel) { case ACCEL_IAA: case ACCEL_DSA: case ACCEL_QAT: for (auto pctr = ctrs.begin(); pctr != ctrs.end(); ++pctr) { rawEvents.push_back(pctr->ccr); filters_wq.push_back(pctr->cfr_wq); filters_tc.push_back(pctr->cfr_tc); filters_pgsz.push_back(pctr->cfr_pgsz); filters_xfersz.push_back(pctr->cfr_xfersz); filters_eng.push_back(pctr->cfr_eng); //std::cout<<"ctr idx=0x" << std::hex << pctr->idx << " hid=0x" << std::hex << pctr->h_id << " vid=0x" << std::hex << pctr->v_id <<" ccr=0x" << std::hex << pctr->ccr << "\n"; //std::cout<<"mul=0x" << std::hex << pctr->multiplier << " div=0x" << std::hex << pctr->divider << "\n" << std::dec; } m->programIDXAccelCounters(idx_accel_mapping[accel], rawEvents, filters_wq, filters_eng, filters_tc, filters_pgsz, filters_xfersz); break; default: break; } return 0; } SimpleCounterState getAccelCounterState(PCM *m, ACCEL_IP accel, uint32 dev, uint32 ctr_index) { SimpleCounterState result; if (m == NULL || accel >= ACCEL_MAX || dev >= getNumOfAccelDevs(m, accel) || ctr_index >= getMaxNumOfAccelCtrs(m, accel)) return result; switch (accel) { case ACCEL_IAA: case ACCEL_DSA: case ACCEL_QAT: result = m->getIDXAccelCounterState(accel, dev, ctr_index); break; default: break; } return result; } bool isAccelCounterAvailable(PCM *m, ACCEL_IP accel) { bool ret = true; if (m == NULL || accel >= ACCEL_MAX) ret =false; if (getNumOfAccelDevs(m, accel) == 0) ret = false; return ret; } std::string getAccelCounterName(ACCEL_IP accel) { std::string ret; switch (accel) { case ACCEL_IAA: ret = "iaa"; break; case ACCEL_DSA: ret = "dsa"; break; case ACCEL_QAT: ret = "qat"; break; default: ret = "id=" + std::to_string(accel) + "(unknown)"; break; } return ret; } bool getAccelDevLocation(PCM *m, const ACCEL_IP accel, uint32_t dev, const ACCEL_DEV_LOC_MAPPING loc_map, uint32_t &location) { bool ret = true; switch (loc_map) { case SOCKET_MAP: location = m->getCPUSocketIdOfIDXAccelDev(accel, dev); break; case NUMA_MAP: location = m->getNumaNodeOfIDXAccelDev(accel, dev); break; default: ret = false; break; } return ret; } std::vector build_counter_names(std::string dev_name, std::vector& ctrs, const ACCEL_DEV_LOC_MAPPING loc_map) { std::vector v; std::map> v_sort; v.push_back(dev_name); switch (loc_map) { case SOCKET_MAP: v.push_back("Socket"); break; case NUMA_MAP: v.push_back("NUMA Node"); break; default: break; } //re-organize data collection to be row wise for (std::vector::iterator counter = ctrs.begin(); counter != ctrs.end(); ++counter) { v_sort[counter->h_id][counter->v_id] = &(*counter); } for (std::map>::const_iterator hunit = v_sort.cbegin(); hunit != v_sort.cend(); ++hunit) { std::map v_array = hunit->second; //std::cout << "hunit: hhid=" << hh_id << "\n"; for (std::map::const_iterator vunit = v_array.cbegin(); vunit != v_array.cend(); ++vunit) { std::string v_name = vunit->second->v_event_name; v.push_back(v_name); } } return v; } void print_usage(const std::string& progname) { std::cout << "\n Usage: \n " << progname << " --help | [interval] [options] \n"; std::cout << " => time interval in seconds (floating point number is accepted)\n"; std::cout << " to sample performance counters.\n"; std::cout << " If not specified - 3.0 is used\n"; std::cout << " Supported are: \n"; std::cout << " -h | --help | /h => print this help and exit\n"; std::cout << " -silent => silence information output and print only measurements\n"; std::cout << " -iaa | /iaa => print IAA accel device measurements(default)\n"; std::cout << " -dsa | /dsa => print DSA accel device measurements\n"; #ifdef __linux__ std::cout << " -qat | /qat => print QAT accel device measurements\n"; std::cout << " -numa | /numa => print accel device numa node mapping(for linux only)\n"; #endif std::cout << " -evt[=cfg.txt] | /evt[=cfg.txt] => specify the event cfg file to cfg.txt \n"; std::cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; std::cout << " -csv-delimiter= | /csv-delimiter= => set custom csv delimiter\n"; std::cout << " -human-readable | /human-readable => use human readable format for output (for csv only)\n"; std::cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; std::cout << " Examples:\n"; std::cout << " " << progname << " -iaa 1.0 -i=10 => print IAA counters every second 10 times and exit\n"; std::cout << " " << progname << " -iaa 0.5 -csv=test.log => twice a second save IAA counter values to test.log in CSV format\n"; std::cout << " " << progname << " -iaa -csv -human-readable => every 3 second print IAA counters in human-readable CSV format\n"; std::cout << "\n"; } std::vector build_csv(PCM *m, const ACCEL_IP accel, std::vector& ctrs, const bool human_readable, const std::string& csv_delimiter, accel_content& sample_data, const ACCEL_DEV_LOC_MAPPING loc_map) { std::vector result; std::vector current_row; auto header = build_counter_names("Accelerator", ctrs, loc_map); result.push_back(build_csv_row(header, csv_delimiter)); std::map> v_sort; uint32_t dev_count = getNumOfAccelDevs(m, accel); for (uint32_t dev = 0; dev != dev_count; ++dev) { //Re-organize data collection to be row wise std::map> v_sort; size_t max_name_width = 0; for (std::vector::iterator counter = ctrs.begin(); counter != ctrs.end(); ++counter) { max_name_width = (std::max)(max_name_width, counter->v_event_name.size()); v_sort[counter->h_id][counter->v_id] = &(*counter); //std::cout << "v_sort: h_id=" << std::hex << counter->h_id << ", v_id=" << std::hex << counter->v_id << "\n" << std::dec; } //Print data for (std::map>::const_iterator hunit = v_sort.cbegin(); hunit != v_sort.cend(); ++hunit) { std::map v_array = hunit->second; uint32_t hh_id = hunit->first; std::vector v_data; std::string h_name = v_array[0]->h_event_name + "#" + std::to_string(dev); uint32 location = 0xff; current_row.clear(); current_row.push_back(h_name); //dev name if (getAccelDevLocation(m, accel, dev, loc_map, location) == true) { current_row.push_back(std::to_string(location)); //location info } //std::cout << "location mapping=" << loc_map << ", data=" << location << "\n"; //std::cout << "hunit: hhid=" << hh_id << "\n"; for (std::map::const_iterator vunit = v_array.cbegin(); vunit != v_array.cend(); ++vunit) { uint32_t vv_id = vunit->first; uint64_t raw_data = sample_data[accel][dev][std::pair(hh_id,vv_id)]; //std::cout << "vunit: hhid=" << hh_id << ", vname=" << vunit->second->v_event_name << ", data=" << raw_data << "\n"; current_row.push_back(human_readable ? unit_format(raw_data) : std::to_string(raw_data)); //counter data } result.push_back(build_csv_row(current_row, csv_delimiter)); } } return result; } std::vector build_display(PCM *m, const ACCEL_IP accel, std::vector& ctrs, accel_content& sample_data, const ACCEL_DEV_LOC_MAPPING loc_map) { std::vector buffer; std::vector headers; std::vector data; std::string row; uint32_t dev_count = getNumOfAccelDevs(m, accel); headers = build_counter_names("Accelerator", ctrs, loc_map); //Print first row row = std::accumulate(headers.begin(), headers.end(), std::string(" "), a_header_footer); buffer.push_back(row); //Print a_title row = std::accumulate(headers.begin(), headers.end(), std::string("|"), a_title); buffer.push_back(row); //Print deliminator row = std::accumulate(headers.begin(), headers.end(), std::string("|"), a_header_footer); buffer.push_back(row); for (uint32_t dev = 0; dev != dev_count; ++dev) { //Print data std::map> v_sort; //re-organize data collection to be row wise for (std::vector::iterator counter = ctrs.begin(); counter != ctrs.end(); ++counter) { v_sort[counter->h_id][counter->v_id] = &(*counter); //std::cout << "v_sort: h_id=" << std::hex << counter->h_id << ", v_id=" << std::hex << counter->v_id << "\n" << std::dec; } for (std::map>::const_iterator hunit = v_sort.cbegin(); hunit != v_sort.cend(); ++hunit) { std::map v_array = hunit->second; uint32_t hh_id = hunit->first; std::vector v_data; std::string h_name = v_array[0]->h_event_name; uint32 location = 0xff; if (getAccelDevLocation(m, accel, dev, loc_map, location) == true) { v_data.push_back(location); //location info } //std::cout << "location mapping=" << loc_map << ", data=" << location << "\n"; //std::cout << "hunit: hhid=" << hh_id << "\n"; for (std::map::const_iterator vunit = v_array.cbegin(); vunit != v_array.cend(); ++vunit) { uint32_t vv_id = vunit->first; uint64_t raw_data = sample_data[accel][dev][std::pair(hh_id,vv_id)]; //std::cout << "vunit: hhid=" << hh_id << ", vname=" << vunit->second->v_event_name << ", data=" << raw_data << "\n"; v_data.push_back(raw_data); //counter data } data = prepare_data(v_data, headers); row = "| " + h_name + "#" + std::to_string(dev); //dev name row += std::string(abs(int(headers[0].size() - (row.size() - 1))), ' '); row += std::accumulate(data.begin(), data.end(), std::string("|"), a_data); buffer.push_back(row); } } //Print deliminator row = std::accumulate(headers.begin(), headers.end(), std::string("|"), a_header_footer); buffer.push_back(row); //Print footer row = std::accumulate(headers.begin(), headers.end(), std::string(" "), a_header_footer); buffer.push_back(row); return buffer; } void collect_data(PCM *m, const double delay, const ACCEL_IP accel, std::vector& ctrs) { const uint32_t delay_ms = uint32_t(delay * 1000); SimpleCounterState *before, *after; const uint32_t dev_count = getNumOfAccelDevs(m, accel); const uint32_t counter_nb = ctrs.size(); uint32_t ctr_index = 0; before = new SimpleCounterState[dev_count*counter_nb]; after = new SimpleCounterState[dev_count*counter_nb]; programAccelCounters(m, accel, ctrs); switch (accel) { case ACCEL_IAA: case ACCEL_DSA: for (uint32_t dev = 0; dev != dev_count; ++dev) { ctr_index = 0; for (auto pctr = ctrs.begin(); pctr != ctrs.end(); ++pctr) { before[dev*counter_nb + ctr_index] = getAccelCounterState(m, accel, dev, ctr_index); ctr_index++; } } MySleepMs(delay_ms); for (uint32_t dev = 0; dev != dev_count; ++dev) { ctr_index = 0; for (auto pctr = ctrs.begin();pctr != ctrs.end(); ++pctr) { after[dev*counter_nb + ctr_index] = getAccelCounterState(m, accel, dev, ctr_index); uint64_t raw_result = getNumberOfEvents(before[dev*counter_nb + ctr_index], after[dev*counter_nb + ctr_index]); uint64_t trans_result = uint64_t (raw_result * pctr->multiplier / (double) pctr->divider * (1000 / (double) delay_ms)); accel_results[accel][dev][std::pair(pctr->h_id,pctr->v_id)] = trans_result; //std::cout << "collect_data: accel=" << accel << " dev=" << dev << " h_id=" << pctr->h_id << " v_id=" << pctr->v_id << " data=" << std::hex << trans_result << "\n" << std::dec; ctr_index++; } } break; case ACCEL_QAT: MySleepMs(delay_ms); for (uint32_t dev = 0; dev != dev_count; ++dev) { m->controlQATTelemetry(dev, PCM::QAT_TLM_REFRESH); ctr_index = 0; for (auto pctr = ctrs.begin();pctr != ctrs.end(); ++pctr) { after[dev*counter_nb + ctr_index] = getAccelCounterState(m, accel, dev, ctr_index); uint64_t raw_result = after[dev*counter_nb + ctr_index].getRawData(); uint64_t trans_result = uint64_t (raw_result * pctr->multiplier / (double) pctr->divider ); accel_results[accel][dev][std::pair(pctr->h_id,pctr->v_id)] = trans_result; //std::cout << "collect_data: accel=" << accel << " dev=" << dev << " h_id=" << pctr->h_id << " v_id=" << pctr->v_id << " data=" << std::hex << trans_result << "\n" << std::dec; ctr_index++; } } break; default: break; } delete[] before; delete[] after; } int idx_evt_parse_handler(evt_cb_type cb_type, void *cb_ctx, counter &base_ctr, std::map &ofm, std::string key, uint64 numValue) { accel_evt_parse_context *context = (accel_evt_parse_context *)cb_ctx; PCM *m = context->m; if (cb_type == EVT_LINE_START) //this event will be called per line(start) { context->ctr.cfr_wq = 0xFFFF; context->ctr.cfr_eng = 0xFFFF; context->ctr.cfr_tc = 0xFFFF; context->ctr.cfr_pgsz = 0xFFFF; context->ctr.cfr_xfersz = 0xFFFF; context->ctr.ccr = 0; } else if (cb_type == EVT_LINE_FIELD) //this event will be called per field of line { std::unique_ptr pccr(idx_get_ccr(context->ctr.ccr)); //std::cout << "Key:" << key << " Value:" << value << " opcodeFieldMap[key]:" << ofm[key] << "\n"; switch (ofm[key]) { case PCM::EVENT_SELECT: pccr->set_event_select(numValue); //std::cout << "pccr value:" << std::hex << pccr->get_ccr_value() <<"\n" << std::dec; break; case PCM::ENABLE: pccr->set_enable(numValue); //std::cout << "pccr value:" << std::hex << pccr->get_ccr_value() <<"\n" << std::dec; break; case EVENT_CATEGORY: pccr->set_event_category(numValue); //std::cout << "pccr value:" << std::hex << pccr->get_ccr_value() <<"\n" << std::dec; break; case FILTER_WQ: context->ctr.cfr_wq = (uint32_t)numValue; break; case FILTER_ENG: context->ctr.cfr_eng = (uint32_t)numValue; break; case FILTER_TC: context->ctr.cfr_tc = (uint32_t)numValue; break; case FILTER_PGSZ: context->ctr.cfr_pgsz = (uint32_t)numValue; break; case FILTER_XFERSZ: context->ctr.cfr_xfersz = (uint32_t)numValue; break; case PCM::INVALID: default: std::cerr << "Field in -o file not recognized. The key is: " << key << "\n"; return -1; } } else if(cb_type == EVT_LINE_COMPLETE) //this event will be called every line(end) { if (context->accel == ACCEL_IAA && base_ctr.h_event_name != "IAA") { return 0; //skip non-IAA cfg line } else if(context->accel == ACCEL_DSA && base_ctr.h_event_name != "DSA") { return 0; //skip non-DSA cfg line } else if(context->accel == ACCEL_QAT && base_ctr.h_event_name != "QAT") { return 0; //skip non-QAT cfg line } //Validate the total number of counter exceed the maximum or not. if ((uint32)base_ctr.idx >= getMaxNumOfAccelCtrs(m, context->accel)) { std::cerr << "line parse KO due to invalid value!" << std::dec << "\n"; return 0; //skip the invalid cfg line } context->ctr.h_event_name = base_ctr.h_event_name; context->ctr.v_event_name = base_ctr.v_event_name; context->ctr.idx = base_ctr.idx; context->ctr.multiplier = base_ctr.multiplier; context->ctr.divider = base_ctr.divider; context->ctr.h_id = base_ctr.h_id; context->ctr.v_id = base_ctr.v_id; //std::cout << "line parse OK, ctrcfg=0x" << std::hex << context->ctr.ccr << ", h_event_name=" << base_ctr.h_event_name << ", v_event_name=" << base_ctr.v_event_name; //std::cout << ", h_id=0x" << std::hex << base_ctr.h_id << ", v_id=0x" << std::hex << base_ctr.v_id; //std::cout << ", idx=0x"<< std::hex << base_ctr.idx << ", multiplier=0x" << std::hex << base_ctr.multiplier << ", divider=0x" << std::hex << base_ctr.divider << std::dec << "\n"; context->ctrs.push_back(context->ctr); } return 0; } typedef int (*pfn_evt_handler)(evt_cb_type, void *, counter &, std::map &, std::string, uint64); PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { null_stream nullStream; check_and_set_silent(argc, argv, nullStream); set_signal_handlers(); std::cout << "\n Intel(r) Performance Counter Monitor " << PCM_VERSION ; std::cout << "\n This utility measures Sapphire Rapids-SP accelerators information.\n"; std::string program = string(argv[0]); bool csv = false; bool human_readable = false; std::string csv_delimiter = ","; std::string output_file; double delay = PCM_DELAY_DEFAULT; ACCEL_IP accel=ACCEL_IAA; //default is IAA bool evtfile = false; std::string specify_evtfile; ACCEL_DEV_LOC_MAPPING loc_map = SOCKET_MAP; //default is socket mapping MainLoop mainLoop; PCM * m; accel_evt_parse_context evt_ctx; std::map opcodeFieldMap; std::string ev_file_name; pfn_evt_handler p_evt_handler; while (argc > 1) { argv++; argc--; std::string arg_value; if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { //handled in check_and_set_silent continue; } else if (extract_argument_value(*argv, {"-csv-delimiter", "/csv-delimiter"}, arg_value)) { csv_delimiter = std::move(arg_value); } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; output_file = std::move(arg_value); } else if (check_argument_equals(*argv, {"-human-readable", "/human-readable"})) { human_readable = true; } else if (check_argument_equals(*argv, {"-iaa", "/iaa"})) { accel = ACCEL_IAA; } else if (check_argument_equals(*argv, {"-dsa", "/dsa"})) { accel = ACCEL_DSA; } #ifdef __linux__ else if (check_argument_equals(*argv, {"-qat", "/qat"})) { accel = ACCEL_QAT; } else if (check_argument_equals(*argv, {"-numa", "/numa"})) { loc_map = NUMA_MAP; } #endif else if (extract_argument_value(*argv, {"-evt", "/evt"}, arg_value)) { evtfile = true; specify_evtfile = std::move(arg_value); } else if (mainLoop.parseArg(*argv)) { continue; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } print_cpu_details(); #ifdef __linux__ // check kernel version for driver dependency. std::cout << "Info: IDX - Please ensure the required driver(e.g idxd driver for iaa/dsa, qat driver and etc) correct enabled with this system, else the tool may fail to run.\n"; struct utsname sys_info; if (!uname(&sys_info)) { std::string krel_str; uint32 krel_major_ver=0, krel_minor_ver=0; krel_str = sys_info.release; std::vector krel_info = split(krel_str, '.'); std::istringstream iss_krel_major(krel_info[0]); std::istringstream iss_krel_minor(krel_info[1]); iss_krel_major >> std::setbase(0) >> krel_major_ver; iss_krel_minor >> std::setbase(0) >> krel_minor_ver; switch (accel) { case ACCEL_IAA: case ACCEL_DSA: if ((krel_major_ver < 5) || (krel_major_ver == 5 && krel_minor_ver < 11)) { std::cout<< "Warning: IDX - current linux kernel version(" << krel_str << ") is too old, please upgrade it to the latest due to required idxd driver integrated to kernel since 5.11.\n"; } break; default: break; } } #endif try { m = PCM::getInstance(); } catch (std::exception & e) { std::cerr << "Error: " << e.what() << "\n"; exit(EXIT_FAILURE); } if (m->supportIDXAccelDev() == false) { std::cerr << "Error: IDX accelerator is NOT supported with this platform! Program aborted\n"; exit(EXIT_FAILURE); } if (isAccelCounterAvailable(m, accel) == true) { if (evtfile == false) //All platform use the spr config file by default. { ev_file_name = "opCode-143-accel.txt"; } else { ev_file_name = specify_evtfile; } //std::cout << "load event config file from:" << ev_file_name << "\n"; } else { std::cerr << "Error: " << getAccelCounterName(accel) << " device is NOT available/ready with this platform! Program aborted\n"; exit(EXIT_FAILURE); } switch (accel) { case ACCEL_IAA: case ACCEL_DSA: case ACCEL_QAT: opcodeFieldMap["hname"] = PCM::H_EVENT_NAME; opcodeFieldMap["vname"] = PCM::V_EVENT_NAME; opcodeFieldMap["multiplier"] = PCM::MULTIPLIER; opcodeFieldMap["divider"] = PCM::DIVIDER; opcodeFieldMap["ctr"] = PCM::COUNTER_INDEX; opcodeFieldMap["en"] = PCM::ENABLE; opcodeFieldMap["ev_sel"] = PCM::EVENT_SELECT; opcodeFieldMap["ev_cat"] = EVENT_CATEGORY; opcodeFieldMap["filter_wq"] = FILTER_WQ; opcodeFieldMap["filter_eng"] = FILTER_ENG; opcodeFieldMap["filter_tc"] = FILTER_TC; opcodeFieldMap["filter_pgsz"] = FILTER_PGSZ; opcodeFieldMap["filter_xfersz"] = FILTER_XFERSZ; p_evt_handler = idx_evt_parse_handler; evt_ctx.m = m; evt_ctx.accel = accel; evt_ctx.ctrs.clear();//fill the ctrs by evt_handler callback func. break; default: std::cerr << "Error: Accel type=0x" << std::hex << accel << " is not supported! Program aborted\n" << std::dec; exit(EXIT_FAILURE); break; } try { load_events(ev_file_name, opcodeFieldMap, p_evt_handler, (void *)&evt_ctx); } catch (std::exception & e) { std::cerr << "Error: " << e.what() << "\n"; std::cerr << "Error: event cfg file have the problem, please double check it! Program aborted\n"; exit(EXIT_FAILURE); } if (evt_ctx.ctrs.size() ==0 || evt_ctx.ctrs.size() > getMaxNumOfAccelCtrs(m, evt_ctx.accel)) { std::cerr << "Error: event counter size is 0 or exceed maximum, please check the event cfg file! Program aborted\n"; exit(EXIT_FAILURE); } std::ostream* output = &std::cout; std::fstream file_stream; if (!output_file.empty()) { file_stream.open(output_file.c_str(), std::ios_base::out); output = &file_stream; } if (accel == ACCEL_QAT) { const uint32_t dev_count = getNumOfAccelDevs(m, accel); for (uint32_t dev = 0; dev != dev_count; ++dev) { m->controlQATTelemetry(dev, PCM::QAT_TLM_START); //start the QAT telemetry service } } mainLoop([&]() { collect_data(m, delay, accel, evt_ctx.ctrs); std::vector display_buffer = csv ? build_csv(m, accel, evt_ctx.ctrs, human_readable, csv_delimiter, accel_results, loc_map) : build_display(m, accel, evt_ctx.ctrs, accel_results, loc_map); display(display_buffer, *output); return true; }); file_stream.close(); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-bw-histogram.sh000077500000000000000000000044421445420033100166760ustar00rootroot00000000000000#!/bin/sh if [ "$#" -ne 1 ]; then echo echo "Usage: $0 " >&2 echo echo "duration is in the same format as the argument of sleep command:" echo sleep --help exit 1 fi out=bw-tmp rm $out echo echo ========= CHECKING FOR PMM SUPPORT ========= echo ./pcm-memory -pmm -- sleep 1 >tmp 2>&1 dram_only=`cat tmp | grep "PMM traffic metrics are not available" | wc -l` rm tmp if [ $dram_only -gt 0 ] then echo PMM support is not present else echo PMM support is present fi echo echo ========= MEASURING ========= echo if [ $dram_only -gt 0 ] then chrt --rr 1 nice --adjustment=-20 ./pcm-memory 0.005 -nc -csv=$out -- sleep $1 else chrt --rr 1 nice --adjustment=-20 ./pcm-memory 0.005 -pmm -nc -csv=$out -- sleep $1 fi cat $out | sed 's/;/,/g' > $out.csv num_sockets=`lscpu | grep Socket | awk '{print $2}'` echo echo ======== POST-PROCESSING ==== echo for s in `seq 0 $(($num_sockets-1))`; do echo ============ Socket $s ============; if [ $dram_only -gt 0 ] then cat $out.csv | cut -d, -f$((4*s+6)) | awk '{ n=n+1; f[int($1/10000)] = f[int($1/10000)] + 1; } END { print "bandwidth(GB/s),count,time(%),chart"; for (i=0; i<32; i++) { if(i in f){ v=100.*f[i]/n; printf "%d-%d\t,%d\t,%3.2f\t,",i*10,(i+1)*10,f[i],v; for (j=0; j #ifdef _MSC_VER #define strtok_r strtok_s #include #include "windows/windriver.h" #else #include #include #include // for gettimeofday() #endif #include #include #include #include #include #include #include #include #include "cpucounters.h" #include "utils.h" #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include #define PCM_DELAY_DEFAULT 1.0 // in seconds #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs #define MAX_CORES 4096 using namespace std; using namespace pcm; void build_event(const char * argv, EventSelectRegister *reg, int idx); struct CoreEvent { char name[256]; uint64 value; uint64 msr_value; char * description; } events[PERF_MAX_CUSTOM_COUNTERS]; extern "C" { SystemCounterState globalSysBeforeState, globalSysAfterState; std::vector globalBeforeState, globalAfterState; std::vector globalDummySocketStates; EventSelectRegister globalRegs[PERF_MAX_COUNTERS]; PCM::ExtendedCustomCoreEventDescription globalConf; int pcm_c_build_core_event(uint8_t idx, const char * argv) { if(idx > 3) return -1; cout << "building core event " << argv << " " << idx << "\n"; build_event(argv, &globalRegs[idx], idx); return 0; } int pcm_c_init() { PCM * m = PCM::getInstance(); globalConf.fixedCfg = NULL; // default globalConf.nGPCounters = m->getMaxCustomCoreEvents(); globalConf.gpCounterCfg = globalRegs; globalConf.OffcoreResponseMsrValue[0] = events[0].msr_value; globalConf.OffcoreResponseMsrValue[1] = events[1].msr_value; m->resetPMU(); PCM::ErrorCode status = m->program(PCM::EXT_CUSTOM_CORE_EVENTS, &globalConf); if(status == PCM::Success) return 0; else return -1; } void pcm_c_start() { PCM * m = PCM::getInstance(); m->getAllCounterStates(globalSysBeforeState, globalDummySocketStates, globalBeforeState); } void pcm_c_stop() { PCM * m = PCM::getInstance(); m->getAllCounterStates(globalSysAfterState, globalDummySocketStates, globalAfterState); } uint64_t pcm_c_get_cycles(uint32_t core_id) { return getCycles(globalBeforeState[core_id], globalAfterState[core_id]); } uint64_t pcm_c_get_instr(uint32_t core_id) { return getInstructionsRetired(globalBeforeState[core_id], globalAfterState[core_id]); } uint64_t pcm_c_get_core_event(uint32_t core_id, uint32_t event_id) { return getNumberOfCustomEvents(event_id, globalBeforeState[core_id], globalAfterState[core_id]); } } void print_usage(const string & progname) { cout << "\n Usage: \n " << progname << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -c | /c => print CPU Model name and exit (used for pmu-query.py)\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; cout << " [-e event1] [-e event2] [-e event3] .. => optional list of custom events to monitor\n"; cout << " event description example: cpu/umask=0x01,event=0x05,name=MISALIGN_MEM_REF.LOADS/ \n"; cout << " -yc | --yescores | /yc => enable specific cores to output\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; print_help_force_rtm_abort_mode(41); cout << " Examples:\n"; cout << " " << progname << " 1 => print counters every second without core and socket output\n"; cout << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << progname << " /csv 5 2>/dev/null => one sample every 5 seconds, and discard all diagnostic output\n"; cout << "\n"; } template void print_custom_stats(const StateType & BeforeState, const StateType & AfterState ,bool csv, uint64 txn_rate) { const uint64 cycles = getCycles(BeforeState, AfterState); const uint64 refCycles = getRefCycles(BeforeState, AfterState); const uint64 instr = getInstructionsRetired(BeforeState, AfterState); if(!csv) { cout << double(instr)/double(cycles); if(txn_rate == 1) { cout << setw(14) << unit_format(instr); cout << setw(11) << unit_format(cycles); cout << setw(12) << unit_format(refCycles); } else { cout << setw(14) << double(instr)/double(txn_rate); cout << setw(11) << double(cycles)/double(txn_rate); cout << setw(12) << double(refCycles) / double(txn_rate); } } else { cout << double(instr)/double(cycles) << ","; cout << double(instr)/double(txn_rate) << ","; cout << double(cycles)/double(txn_rate) << ","; cout << double(refCycles) / double(txn_rate) << ","; } const auto max_ctr = PCM::getInstance()->getMaxCustomCoreEvents(); for (int i = 0; i < max_ctr; ++i) if(!csv) { cout << setw(10); if(txn_rate == 1) cout << unit_format(getNumberOfCustomEvents(i, BeforeState, AfterState)); else cout << double(getNumberOfCustomEvents(i, BeforeState, AfterState))/double(txn_rate); } else cout << double(getNumberOfCustomEvents(i, BeforeState, AfterState))/double(txn_rate) << ","; cout << "\n"; } // emulates scanf %i for hex 0x prefix otherwise assumes dec (no oct support) bool match(const char * subtoken, const char * name, int * result) { std::string sname(name); if (pcm_sscanf(subtoken) >> s_expect(sname + "0x") >> std::hex >> *result) return true; if (pcm_sscanf(subtoken) >> s_expect(sname) >> std::dec >> *result) return true; return false; } void build_event(const char * argv, EventSelectRegister *reg, int idx) { char *token, *subtoken, *saveptr1, *saveptr2; char *str1, *str2; int j, tmp; uint64 tmp2; reg->value = 0; reg->fields.usr = 1; reg->fields.os = 1; reg->fields.enable = 1; /* uint64 apic_int : 1; offcore_rsp=2,period=10000 */ for (j = 1, str1 = (char*) argv; ; j++, str1 = NULL) { token = strtok_r(str1, "/", &saveptr1); if (token == NULL) break; printf("%d: %s\n", j, token); if(strncmp(token,"cpu",3) == 0) continue; for (str2 = token; ; str2 = NULL) { tmp = -1; subtoken = strtok_r(str2, ",", &saveptr2); if (subtoken == NULL) break; if(match(subtoken,"event=",&tmp)) reg->fields.event_select = tmp; else if(match(subtoken,"umask=",&tmp)) reg->fields.umask = tmp; else if(strcmp(subtoken,"edge") == 0) reg->fields.edge = 1; else if(match(subtoken,"any=",&tmp)) reg->fields.any_thread = tmp; else if(match(subtoken,"inv=",&tmp)) reg->fields.invert = tmp; else if(match(subtoken,"cmask=",&tmp)) reg->fields.cmask = tmp; else if(match(subtoken,"in_tx=",&tmp)) reg->fields.in_tx = tmp; else if(match(subtoken,"in_tx_cp=",&tmp)) reg->fields.in_txcp = tmp; else if(match(subtoken,"pc=",&tmp)) reg->fields.pin_control = tmp; else if(pcm_sscanf(subtoken) >> s_expect("offcore_rsp=") >> std::hex >> tmp2) { if(idx >= 2) { cerr << "offcore_rsp must specify in first or second event only. idx=" << idx << "\n"; throw idx; } events[idx].msr_value = tmp2; } else if(pcm_sscanf(subtoken) >> s_expect("name=") >> setw(255) >> events[idx].name) { if (check_for_injections(events[idx].name)) throw events[idx].name; } else { cerr << "Event '" << subtoken << "' is not supported. See the list of supported events\n"; throw subtoken; } } } events[idx].value = reg->value; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream2; #ifdef PCM_FORCE_SILENT null_stream nullStream1; std::cout.rdbuf(&nullStream1); std::cerr.rdbuf(&nullStream2); #else check_and_set_silent(argc, argv, nullStream2); #endif set_signal_handlers(); cerr << "\n"; cerr << " Intel(r) Performance Counter Monitor: Core Monitoring Utility \n"; cerr << "\n"; double delay = -1.0; char *sysCmd = NULL; char **sysArgv = NULL; uint32 cur_event = 0; bool csv = false; uint64 txn_rate = 1; MainLoop mainLoop; string program = string(argv[0]); EventSelectRegister regs[PERF_MAX_COUNTERS]; PCM::ExtendedCustomCoreEventDescription conf; bool show_partial_core_output = false; std::bitset ycores; PCM * m = PCM::getInstance(); conf.fixedCfg = NULL; // default conf.nGPCounters = m->getMaxCustomCoreEvents(); conf.gpCounterCfg = regs; if(argc > 1) do { argv++; argc--; string arg_value; if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; if (!arg_value.empty()) { m->setOutput(arg_value); } continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (check_argument_equals(*argv, {"-c", "/c"})) { cout << m->getCPUFamilyModelString() << "\n"; exit(EXIT_SUCCESS); } else if (check_argument_equals(*argv, {"-txn", "/txn"})) { argv++; argc--; txn_rate = strtoull(*argv,NULL,10); cout << "txn_rate set to " << txn_rate << "\n"; continue; } else if (check_argument_equals(*argv, {"--yescores", "-yc", "/yc"})) { argv++; argc--; show_partial_core_output = true; if(*argv == NULL) { cerr << "Error: --yescores requires additional argument.\n"; exit(EXIT_FAILURE); } std::stringstream ss(*argv); while(ss.good()) { string s; int core_id; std::getline(ss, s, ','); if(s.empty()) continue; core_id = atoi(s.c_str()); if(core_id > MAX_CORES) { cerr << "Core ID:" << core_id << " exceed maximum range " << MAX_CORES << ", program abort\n"; exit(EXIT_FAILURE); } ycores.set(atoi(s.c_str()),true); } if(m->getNumCores() > MAX_CORES) { cerr << "Error: --yescores option is enabled, but #define MAX_CORES " << MAX_CORES << " is less than m->getNumCores() = " << m->getNumCores() << "\n"; cerr << "There is a potential to crash the system. Please increase MAX_CORES to at least " << m->getNumCores() << " and re-enable this option.\n"; exit(EXIT_FAILURE); } continue; } else if (check_argument_equals(*argv, {"-e"})) { argv++; argc--; if(cur_event >= conf.nGPCounters) { cerr << "At most " << conf.nGPCounters << " events are allowed\n"; exit(EXIT_FAILURE); } try { build_event(*argv,®s[cur_event],cur_event); cur_event++; } catch (...) { exit(EXIT_FAILURE); } continue; } else if (CheckAndForceRTMAbortMode(*argv, m)) { continue; } else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } while(argc > 1); // end of command line parsing loop if ( cur_event == 0 ) cerr << "WARNING: you did not provide any custom events, is this intentional?\n"; conf.OffcoreResponseMsrValue[0] = events[0].msr_value; conf.OffcoreResponseMsrValue[1] = events[1].msr_value; PCM::ErrorCode status = m->program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf); m->checkError(status); print_cpu_details(); uint64 BeforeTime = 0, AfterTime = 0; SystemCounterState SysBeforeState, SysAfterState; const uint32 ncores = m->getNumCores(); std::vector BeforeState, AfterState; std::vector DummySocketStates; if ( (sysCmd != NULL) && (delay<=0.0) ) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } if (csv) { if( delay<=0.0 ) delay = PCM_DELAY_DEFAULT; } else { // for non-CSV mode delay < 1.0 does not make a lot of practical sense: // hard to read from the screen, or // in case delay is not provided in command line => set default if( ((delay<1.0) && (delay>0.0)) || (delay<=0.0) ) delay = PCM_DELAY_DEFAULT; } cerr << "Update every " << delay << " seconds\n"; std::cout.precision(2); std::cout << std::fixed; BeforeTime = m->getTickCount(); m->getAllCounterStates(SysBeforeState, DummySocketStates, BeforeState); if( sysCmd != NULL ) { MySystem(sysCmd, sysArgv); } mainLoop([&]() { if(!csv) cout << std::flush; calibratedSleep(delay, sysCmd, mainLoop, m); AfterTime = m->getTickCount(); m->getAllCounterStates(SysAfterState, DummySocketStates, AfterState); cout << "Time elapsed: " << dec << fixed << AfterTime-BeforeTime << " ms\n"; cout << "txn_rate: " << txn_rate << "\n"; //cout << "Called sleep function for " << dec << fixed << delay_ms << " ms\n"; for(uint32 i=0;iisCoreOnline(i) == false || (show_partial_core_output && ycores.test(i) == false)) continue; if(csv) cout << i << ","; else cout << " " << setw(3) << i << " " << setw(2) ; print_custom_stats(BeforeState[i], AfterState[i], csv, txn_rate); } if(csv) cout << "*,"; else { cout << "---------------------------------------------------------------------------------------------------------------------------------\n"; cout << " * "; } print_custom_stats(SysBeforeState, SysAfterState, csv, txn_rate); std::cout << "\n"; swap(BeforeTime, AfterTime); swap(BeforeState, AfterState); swap(SysBeforeState, SysAfterState); if ( m->isBlocked() ) { // in case PCM was blocked after spawning child application: break monitoring loop here return false; } return true; }); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-iio.cpp000066400000000000000000001636631445420033100152330ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2017-2022, Intel Corporation // written by Patrick Lu, // Aaron Cruz // and others #include "cpucounters.h" #ifdef _MSC_VER #pragma warning(disable : 4996) // for sprintf #include #include "windows/windriver.h" #else #include #endif #include #include #include #include // std::length_error #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include "lspci.h" #include "utils.h" using namespace std; using namespace pcm; #define PCM_DELAY_DEFAULT 3.0 // in seconds #define QAT_DID 0x18DA #define NIS_DID 0x18D1 #define HQM_DID 0x270B #define ROOT_BUSES_OFFSET 0xCC #define ROOT_BUSES_OFFSET_2 0xD0 #define SKX_SOCKETID_UBOX_DID 0x2014 #define SKX_UBOX_DEVICE_NUM 0x08 #define SKX_UBOX_FUNCTION_NUM 0x02 #define SKX_BUS_NUM_STRIDE 8 //the below LNID and GID applies to Skylake Server #define SKX_UNC_SOCKETID_UBOX_LNID_OFFSET 0xC0 #define SKX_UNC_SOCKETID_UBOX_GID_OFFSET 0xD4 const uint8_t max_sockets = 4; static const std::string iio_stack_names[6] = { "IIO Stack 0 - CBDMA/DMI ", "IIO Stack 1 - PCIe0 ", "IIO Stack 2 - PCIe1 ", "IIO Stack 3 - PCIe2 ", "IIO Stack 4 - MCP0 ", "IIO Stack 5 - MCP1 " }; static const std::string skx_iio_stack_names[6] = { "IIO Stack 0 - CBDMA/DMI ", "IIO Stack 1 - PCIe0 ", "IIO Stack 2 - PCIe1 ", "IIO Stack 3 - PCIe2 ", "IIO Stack 4 - MCP0 ", "IIO Stack 5 - MCP1 " }; static const std::string icx_iio_stack_names[6] = { "IIO Stack 0 - PCIe0 ", "IIO Stack 1 - PCIe1 ", "IIO Stack 2 - MCP ", "IIO Stack 3 - PCIe2 ", "IIO Stack 4 - PCIe3 ", "IIO Stack 5 - CBDMA/DMI " }; static const std::string icx_d_iio_stack_names[6] = { "IIO Stack 0 - MCP ", "IIO Stack 1 - PCIe0 ", "IIO Stack 2 - CBDMA/DMI ", "IIO Stack 3 - PCIe2 ", "IIO Stack 4 - PCIe3 ", "IIO Stack 5 - PCIe1 " }; static const std::string snr_iio_stack_names[5] = { "IIO Stack 0 - QAT ", "IIO Stack 1 - CBDMA/DMI ", "IIO Stack 2 - NIS ", "IIO Stack 3 - HQM ", "IIO Stack 4 - PCIe " }; #define ICX_CBDMA_DMI_SAD_ID 0 #define ICX_MCP_SAD_ID 3 #define ICX_PCH_PART_ID 0 #define ICX_CBDMA_PART_ID 3 #define SNR_ICX_SAD_CONTROL_CFG_OFFSET 0x3F4 #define SNR_ICX_MESH2IIO_MMAP_DID 0x09A2 #define ICX_VMD_PCI_DEVNO 0x00 #define ICX_VMD_PCI_FUNCNO 0x05 static const std::map icx_sad_to_pmu_id_mapping = { { ICX_CBDMA_DMI_SAD_ID, 5 }, { 1, 0 }, { 2, 1 }, { ICX_MCP_SAD_ID, 2 }, { 4, 3 }, { 5, 4 } }; static const std::map icx_d_sad_to_pmu_id_mapping = { { ICX_CBDMA_DMI_SAD_ID, 2 }, { 1, 5 }, { 2, 1 }, { ICX_MCP_SAD_ID, 0 }, { 4, 3 }, { 5, 4 } }; #define SNR_ACCELERATOR_PART_ID 4 #define SNR_ROOT_PORT_A_DID 0x334A #define SNR_CBDMA_DMI_SAD_ID 0 #define SNR_PCIE_GEN3_SAD_ID 1 #define SNR_HQM_SAD_ID 2 #define SNR_NIS_SAD_ID 3 #define SNR_QAT_SAD_ID 4 static const std::map snr_sad_to_pmu_id_mapping = { { SNR_CBDMA_DMI_SAD_ID, 1 }, { SNR_PCIE_GEN3_SAD_ID, 4 }, { SNR_HQM_SAD_ID , 3 }, { SNR_NIS_SAD_ID , 2 }, { SNR_QAT_SAD_ID , 0 } }; #define HQMV2_DID 0x2710 // Hardware Queue Manager v2 #define DSA_DID 0x0b25 // Data Streaming Accelerator (DSA) #define IAX_DID 0x0cfe // In-Memory Database Analytics Accelerator (IAX) #define QATV2_DID 0x4940 // QuickAssist (CPM) v2 #define SPR_DMI_PART_ID 7 #define SPR_SAD_CONTROL_CFG_OFFSET SNR_ICX_SAD_CONTROL_CFG_OFFSET #define SPR_DMI_PMON_ID 1 #define SPR_PCIE_GEN5_0_PMON_ID 2 #define SPR_PCIE_GEN5_1_PMON_ID 4 #define SPR_PCIE_GEN5_2_PMON_ID 6 #define SPR_PCIE_GEN5_3_PMON_ID 7 #define SPR_PCIE_GEN5_4_PMON_ID 9 #define SPR_IDX0_PMON_ID 0 #define SPR_IDX1_PMON_ID 3 #define SPR_IDX2_PMON_ID 5 #define SPR_IDX3_PMON_ID 8 const std::map spr_sad_to_pmu_id_mapping = { { 0, SPR_DMI_PMON_ID }, { 1, SPR_PCIE_GEN5_0_PMON_ID }, { 2, SPR_PCIE_GEN5_1_PMON_ID }, { 3, SPR_PCIE_GEN5_2_PMON_ID }, { 4, SPR_PCIE_GEN5_3_PMON_ID }, { 5, SPR_PCIE_GEN5_4_PMON_ID }, { 8, SPR_IDX0_PMON_ID }, { 9, SPR_IDX1_PMON_ID }, { 10, SPR_IDX2_PMON_ID }, { 11, SPR_IDX3_PMON_ID } }; static const std::string spr_iio_stack_names[] = { "IIO Stack 0 - IDX0 ", "IIO Stack 1 - DMI ", "IIO Stack 2 - PCIe0 ", "IIO Stack 3 - IDX1 ", "IIO Stack 4 - PCIe1 ", "IIO Stack 5 - IDX2 ", "IIO Stack 6 - PCIe2 ", "IIO Stack 7 - PCIe3", "IIO Stack 8 - IDX3 ", "IIO Stack 9 - PCIe4", "IIO Stack 10 - NONE ", "IIO Stack 11 - NONE ", }; struct iio_counter : public counter { std::vector data; }; //TODO: remove binding to stacks amount result_content results(max_sockets, stack_content(12, ctr_data())); typedef struct { PCM *m; iio_counter ctr; vector ctrs; } iio_evt_parse_context; vector combine_stack_name_and_counter_names(string stack_name, const map>> &nameMap) { vector v; vector tmp(nameMap.size()); v.push_back(stack_name); for (std::map>>::const_iterator iunit = nameMap.begin(); iunit != nameMap.end(); ++iunit) { string h_name = iunit->first; int h_id = (iunit->second).first; tmp[h_id] = h_name; //cout << "h_id:" << h_id << " name:" << h_name << "\n"; } //XXX: How to simplify and just combine tmp & v? for (uint32_t i = 0; i < nameMap.size(); i++) { v.push_back(tmp[i]); } return v; } string build_pci_header(const PCIDB & pciDB, uint32_t column_width, const struct pci &p, int part = -1, uint32_t level = 0) { string s = "|"; char bdf_buf[32]; char speed_buf[10]; char vid_did_buf[10]; char device_name_buf[128]; snprintf(bdf_buf, sizeof(bdf_buf), "%04X:%02X:%02X.%1d", p.bdf.domainno, p.bdf.busno, p.bdf.devno, p.bdf.funcno); snprintf(speed_buf, sizeof(speed_buf), "Gen%1d x%-2d", p.link_speed, p.link_width); snprintf(vid_did_buf, sizeof(vid_did_buf), "%04X:%04X", p.vendor_id, p.device_id); snprintf(device_name_buf, sizeof(device_name_buf), "%s %s", (pciDB.first.count(p.vendor_id) > 0)?pciDB.first.at(p.vendor_id).c_str():"unknown vendor", (pciDB.second.count(p.vendor_id) > 0 && pciDB.second.at(p.vendor_id).count(p.device_id) > 0)?pciDB.second.at(p.vendor_id).at(p.device_id).c_str():"unknown device" ); s += bdf_buf; s += '|'; s += speed_buf; s += '|'; s += vid_did_buf; s += " "; s += device_name_buf; if (!p.parts_no.empty()) { s += "; Part: "; for (auto& part : p.parts_no) { s += std::to_string(part) + ", "; } s += "\b\b "; } /* row with data */ if (part >= 0) { s.insert(1,"P" + std::to_string(part) + " "); s += std::string(column_width - (s.size()-1), ' '); } else { /* row without data, just child pci device */ s.insert(0, std::string(4*level, ' ')); } return s; } void build_pci_tree(vector &buffer, const PCIDB & pciDB, uint32_t column_width, const struct pci &p, int part, uint32_t level = 0) { string row; for (const auto& child : p.child_pci_devs) { row = build_pci_header(pciDB, column_width, child, part, level); buffer.push_back(row); if (child.hasChildDevices()) build_pci_tree(buffer, pciDB, column_width, child, part, level + 1); } } vector build_display(vector& iios, vector& ctrs, const PCIDB& pciDB, const map>> &nameMap) { vector buffer; vector headers; vector data; uint64_t header_width; string row; for (auto socket = iios.cbegin(); socket != iios.cend(); ++socket) { buffer.push_back("Socket" + std::to_string(socket->socket_id)); for (auto stack = socket->stacks.cbegin(); stack != socket->stacks.cend(); ++stack) { auto stack_id = stack->iio_unit_id; headers = combine_stack_name_and_counter_names(stack->stack_name, nameMap); //Print first row row = std::accumulate(headers.begin(), headers.end(), string(" "), a_header_footer); header_width = row.size(); buffer.push_back(row); //Print a_title row = std::accumulate(headers.begin(), headers.end(), string("|"), a_title); buffer.push_back(row); //Print deliminator row = std::accumulate(headers.begin(), headers.end(), string("|"), a_header_footer); buffer.push_back(row); //Print data std::map> v_sort; //re-organize data collection to be row wise for (std::vector::iterator counter = ctrs.begin(); counter != ctrs.end(); ++counter) { v_sort[counter->v_id][counter->h_id] = &(*counter); } for (std::map>::const_iterator vunit = v_sort.cbegin(); vunit != v_sort.cend(); ++vunit) { map h_array = vunit->second; uint32_t vv_id = vunit->first; vector h_data; string v_name = h_array[0]->v_event_name; for (map::const_iterator hunit = h_array.cbegin(); hunit != h_array.cend(); ++hunit) { uint32_t hh_id = hunit->first; uint64_t raw_data = hunit->second->data[0][socket->socket_id][stack_id][std::pair(hh_id,vv_id)]; h_data.push_back(raw_data); } data = prepare_data(h_data, headers); row = "| " + v_name; row += string(abs(int(headers[0].size() - (row.size() - 1))), ' '); row += std::accumulate(data.begin(), data.end(), string("|"), a_data); buffer.push_back(row); } //Print deliminator row = std::accumulate(headers.begin(), headers.end(), string("|"), a_header_footer); buffer.push_back(row); //Print pcie devices for (const auto& part : stack->parts) { uint8_t level = 1; for (const auto& pci_device : part.child_pci_devs) { row = build_pci_header(pciDB, (uint32_t)header_width, pci_device, -1, level); buffer.push_back(row); if (pci_device.hasChildDevices()) { build_pci_tree(buffer, pciDB, (uint32_t)header_width, pci_device, -1, level + 1); } else if (pci_device.header_type == 1) { level++; } } } //Print footer row = std::accumulate(headers.begin(), headers.end(), string(" "), a_header_footer); buffer.push_back(row); } } return buffer; } std::string get_root_port_dev(const bool show_root_port, int part_id, const pcm::iio_stack *stack) { char tmp[9] = " "; std::string rp_pci; if (!show_root_port) return rp_pci; for (auto part = stack->parts.begin(); part != stack->parts.end(); part = std::next(part)) { if (part->part_id == part_id) { std::snprintf(tmp, sizeof(tmp), "%02x:%02x.%x", part->root_pci_dev.bdf.busno, part->root_pci_dev.bdf.devno, part->root_pci_dev.bdf.funcno); break; } } rp_pci.append(tmp); return rp_pci; } vector build_csv(vector& iios, vector& ctrs, const bool human_readable, const bool show_root_port, const std::string& csv_delimiter, const map>> &nameMap) { vector result; vector current_row; auto header = combine_stack_name_and_counter_names("Part", nameMap); header.insert(header.begin(), "Name"); if (show_root_port) header.insert(header.begin(), "Root Port"); header.insert(header.begin(), "Socket"); result.push_back(build_csv_row(header, csv_delimiter)); std::map> v_sort; //re-organize data collection to be row wise size_t max_name_width = 0; for (std::vector::iterator counter = ctrs.begin(); counter != ctrs.end(); ++counter) { v_sort[counter->v_id][counter->h_id] = &(*counter); max_name_width = (std::max)(max_name_width, counter->v_event_name.size()); } for (auto socket = iios.cbegin(); socket != iios.cend(); ++socket) { for (auto stack = socket->stacks.cbegin(); stack != socket->stacks.cend(); ++stack) { const std::string socket_name = "Socket" + std::to_string(socket->socket_id); std::string stack_name = stack->stack_name; if (!human_readable) { stack_name.erase(stack_name.find_last_not_of(' ') + 1); } const uint32_t stack_id = stack->iio_unit_id; //Print data int part_id; std::map>::const_iterator vunit; for (vunit = v_sort.cbegin(), part_id = 0; vunit != v_sort.cend(); ++vunit, ++part_id) { map h_array = vunit->second; uint32_t vv_id = vunit->first; vector h_data; string v_name = h_array[0]->v_event_name; if (human_readable) { v_name += string(max_name_width - (v_name.size()), ' '); } current_row.clear(); current_row.push_back(socket_name); if (show_root_port) { auto pci_dev = get_root_port_dev(show_root_port, part_id, &(*stack)); current_row.push_back(pci_dev); } current_row.push_back(stack_name); current_row.push_back(v_name); for (map::const_iterator hunit = h_array.cbegin(); hunit != h_array.cend(); ++hunit) { uint32_t hh_id = hunit->first; uint64_t raw_data = hunit->second->data[0][socket->socket_id][stack_id][std::pair(hh_id,vv_id)]; current_row.push_back(human_readable ? unit_format(raw_data) : std::to_string(raw_data)); } result.push_back(build_csv_row(current_row, csv_delimiter)); } } } return result; } class IPlatformMapping { private: uint32_t m_sockets; protected: void probeDeviceRange(std::vector &child_pci_devs, int domain, int secondary, int subordinate); public: IPlatformMapping(uint32_t sockets_count) : m_sockets(sockets_count) {} virtual ~IPlatformMapping() {}; static std::unique_ptr getPlatformMapping(int cpu_model, uint32_t sockets_count); virtual bool pciTreeDiscover(std::vector& iios) = 0; uint32_t socketsCount() const { return m_sockets; } }; // Mapping for SkyLake Server. class PurleyPlatformMapping: public IPlatformMapping { private: void getUboxBusNumbers(std::vector& ubox); public: PurleyPlatformMapping(uint32_t sockets_count) : IPlatformMapping(sockets_count) {} ~PurleyPlatformMapping() = default; bool pciTreeDiscover(std::vector& iios) override; }; void PurleyPlatformMapping::getUboxBusNumbers(std::vector& ubox) { for (uint16_t bus = 0; bus < 256; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci pci_dev; pci_dev.bdf.busno = (uint8_t)bus; pci_dev.bdf.devno = device; pci_dev.bdf.funcno = function; if (probe_pci(&pci_dev)) { if ((pci_dev.vendor_id == PCM_INTEL_PCI_VENDOR_ID) && (pci_dev.device_id == SKX_SOCKETID_UBOX_DID)) { ubox.push_back(bus); } } } } } } bool PurleyPlatformMapping::pciTreeDiscover(std::vector& iios) { std::vector ubox; getUboxBusNumbers(ubox); if (ubox.empty()) { cerr << "UBOXs were not found! Program aborted" << endl; return false; } for (uint32_t socket_id = 0; socket_id < socketsCount(); socket_id++) { if (!PciHandleType::exists(0, ubox[socket_id], SKX_UBOX_DEVICE_NUM, SKX_UBOX_FUNCTION_NUM)) { cerr << "No access to PCICFG\n" << endl; return false; } uint64 cpubusno = 0; struct iio_stacks_on_socket iio_on_socket; iio_on_socket.socket_id = socket_id; PciHandleType h(0, ubox[socket_id], SKX_UBOX_DEVICE_NUM, SKX_UBOX_FUNCTION_NUM); h.read64(ROOT_BUSES_OFFSET, &cpubusno); iio_on_socket.stacks.reserve(6); for (int stack_id = 0; stack_id < 6; stack_id++) { struct iio_stack stack; stack.iio_unit_id = stack_id; stack.busno = (uint8_t)(cpubusno >> (stack_id * SKX_BUS_NUM_STRIDE)); stack.stack_name = skx_iio_stack_names[stack_id]; for (uint8_t part_id = 0; part_id < 4; part_id++) { struct iio_bifurcated_part part; part.part_id = part_id; struct pci *pci = &part.root_pci_dev; struct bdf *bdf = &pci->bdf; bdf->busno = stack.busno; bdf->devno = part_id; bdf->funcno = 0; /* This is a workaround to catch some IIO stack does not exist */ if (stack_id != 0 && stack.busno == 0) { pci->exist = false; } else if (probe_pci(pci)) { /* FIXME: for 0:0.0, we may need to scan from secondary switch down; lgtm [cpp/fixme-comment] */ for (uint8_t bus = pci->secondary_bus_number; bus <= pci->subordinate_bus_number; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci child_pci_dev; child_pci_dev.bdf.busno = bus; child_pci_dev.bdf.devno = device; child_pci_dev.bdf.funcno = function; if (probe_pci(&child_pci_dev)) { part.child_pci_devs.push_back(child_pci_dev); } } } } } stack.parts.push_back(part); } iio_on_socket.stacks.push_back(stack); } iios.push_back(iio_on_socket); } return true; } class IPlatformMapping10Nm: public IPlatformMapping { private: public: IPlatformMapping10Nm(uint32_t sockets_count) : IPlatformMapping(sockets_count) {} ~IPlatformMapping10Nm() = default; bool getSadIdRootBusMap(uint32_t socket_id, std::map& sad_id_bus_map); }; bool IPlatformMapping10Nm::getSadIdRootBusMap(uint32_t socket_id, std::map& sad_id_bus_map) { for (uint16_t bus = 0; bus < 256; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci pci_dev; pci_dev.bdf.busno = (uint8_t)bus; pci_dev.bdf.devno = device; pci_dev.bdf.funcno = function; if (probe_pci(&pci_dev) && (pci_dev.vendor_id == PCM_INTEL_PCI_VENDOR_ID) && (pci_dev.device_id == SNR_ICX_MESH2IIO_MMAP_DID)) { PciHandleType h(0, bus, device, function); std::uint32_t sad_ctrl_cfg; h.read32(SNR_ICX_SAD_CONTROL_CFG_OFFSET, &sad_ctrl_cfg); if (sad_ctrl_cfg == (std::numeric_limits::max)()) { cerr << "Could not read SAD_CONTROL_CFG" << endl; return false; } if ((sad_ctrl_cfg & 0xf) == socket_id) { uint8_t sid = (sad_ctrl_cfg >> 4) & 0x7; sad_id_bus_map.insert(std::pair(sid, (uint8_t)bus)); } } } } } if (sad_id_bus_map.empty()) { cerr << "Could not find Root Port bus numbers" << endl; return false; } return true; } // Mapping for IceLake Server. class WhitleyPlatformMapping: public IPlatformMapping10Nm { private: const bool icx_d; const std::map& sad_to_pmu_id_mapping; const std::string * iio_stack_names; public: WhitleyPlatformMapping(uint32_t sockets_count) : IPlatformMapping10Nm(sockets_count), icx_d(PCM::getInstance()->getCPUModelFromCPUID() == PCM::ICX_D), sad_to_pmu_id_mapping(icx_d ? icx_d_sad_to_pmu_id_mapping : icx_sad_to_pmu_id_mapping), iio_stack_names(icx_d ? icx_d_iio_stack_names : icx_iio_stack_names) { } ~WhitleyPlatformMapping() = default; bool pciTreeDiscover(std::vector& iios) override; }; bool WhitleyPlatformMapping::pciTreeDiscover(std::vector& iios) { for (uint32_t socket = 0; socket < socketsCount(); socket++) { struct iio_stacks_on_socket iio_on_socket; iio_on_socket.socket_id = socket; std::map sad_id_bus_map; if (!getSadIdRootBusMap(socket, sad_id_bus_map)) { return false; } { struct iio_stack stack; stack.iio_unit_id = sad_to_pmu_id_mapping.at(ICX_MCP_SAD_ID); stack.stack_name = iio_stack_names[stack.iio_unit_id]; iio_on_socket.stacks.push_back(stack); } for (auto sad_id_bus_pair = sad_id_bus_map.cbegin(); sad_id_bus_pair != sad_id_bus_map.cend(); ++sad_id_bus_pair) { int sad_id = sad_id_bus_pair->first; if (sad_to_pmu_id_mapping.find(sad_id) == sad_to_pmu_id_mapping.end()) { cerr << "Unknown SAD ID: " << sad_id << endl; return false; } if (sad_id == ICX_MCP_SAD_ID) { continue; } struct iio_stack stack; int root_bus = sad_id_bus_pair->second; if (sad_id == ICX_CBDMA_DMI_SAD_ID) { // There is one DMA Controller on each socket stack.iio_unit_id = sad_to_pmu_id_mapping.at(sad_id); stack.busno = root_bus; stack.stack_name = iio_stack_names[stack.iio_unit_id]; // PCH is on socket 0 only if (socket == 0) { struct iio_bifurcated_part pch_part; struct pci *pci = &pch_part.root_pci_dev; struct bdf *bdf = &pci->bdf; pch_part.part_id = ICX_PCH_PART_ID; bdf->busno = root_bus; bdf->devno = 0x00; bdf->funcno = 0x00; probe_pci(pci); // Probe child devices only under PCH part. for (uint8_t bus = pci->secondary_bus_number; bus <= pci->subordinate_bus_number; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci child_pci_dev; child_pci_dev.bdf.busno = bus; child_pci_dev.bdf.devno = device; child_pci_dev.bdf.funcno = function; if (probe_pci(&child_pci_dev)) { pch_part.child_pci_devs.push_back(child_pci_dev); } } } } stack.parts.push_back(pch_part); } struct iio_bifurcated_part part; part.part_id = ICX_CBDMA_PART_ID; struct pci *pci = &part.root_pci_dev; struct bdf *bdf = &pci->bdf; bdf->busno = root_bus; bdf->devno = 0x01; bdf->funcno = 0x00; probe_pci(pci); stack.parts.push_back(part); iio_on_socket.stacks.push_back(stack); continue; } stack.busno = root_bus; stack.iio_unit_id = sad_to_pmu_id_mapping.at(sad_id); stack.stack_name = iio_stack_names[stack.iio_unit_id]; for (int slot = 2; slot < 6; slot++) { struct pci pci; pci.bdf.busno = root_bus; pci.bdf.devno = slot; pci.bdf.funcno = 0x00; if (!probe_pci(&pci)) { continue; } struct iio_bifurcated_part part; part.part_id = slot - 2; part.root_pci_dev = pci; for (uint8_t bus = pci.secondary_bus_number; bus <= pci.subordinate_bus_number; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci child_pci_dev; child_pci_dev.bdf.busno = bus; child_pci_dev.bdf.devno = device; child_pci_dev.bdf.funcno = function; if (probe_pci(&child_pci_dev)) { part.child_pci_devs.push_back(child_pci_dev); } } } } stack.parts.push_back(part); } iio_on_socket.stacks.push_back(stack); } std::sort(iio_on_socket.stacks.begin(), iio_on_socket.stacks.end()); iios.push_back(iio_on_socket); } return true; } // Mapping for Snowridge. class JacobsvillePlatformMapping: public IPlatformMapping10Nm { private: public: JacobsvillePlatformMapping(uint32_t sockets_count) : IPlatformMapping10Nm(sockets_count) {} ~JacobsvillePlatformMapping() = default; bool pciTreeDiscover(std::vector& iios) override; bool JacobsvilleAccelerators(const std::pair& sad_id_bus_pair, struct iio_stack& stack); }; bool JacobsvillePlatformMapping::JacobsvilleAccelerators(const std::pair& sad_id_bus_pair, struct iio_stack& stack) { uint16_t expected_dev_id; auto sad_id = sad_id_bus_pair.first; switch (sad_id) { case SNR_HQM_SAD_ID: expected_dev_id = HQM_DID; break; case SNR_NIS_SAD_ID: expected_dev_id = NIS_DID; break; case SNR_QAT_SAD_ID: expected_dev_id = QAT_DID; break; default: return false; } stack.iio_unit_id = snr_sad_to_pmu_id_mapping.at(sad_id); stack.stack_name = snr_iio_stack_names[stack.iio_unit_id]; for (uint16_t bus = sad_id_bus_pair.second; bus < 256; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci pci_dev; pci_dev.bdf.busno = (uint8_t)bus; pci_dev.bdf.devno = device; pci_dev.bdf.funcno = function; if (probe_pci(&pci_dev)) { if (expected_dev_id == pci_dev.device_id) { struct iio_bifurcated_part part; part.part_id = SNR_ACCELERATOR_PART_ID; part.root_pci_dev = pci_dev; stack.busno = (uint8_t)bus; stack.parts.push_back(part); return true; } } } } } return false; } bool JacobsvillePlatformMapping::pciTreeDiscover(std::vector& iios) { std::map sad_id_bus_map; if (!getSadIdRootBusMap(0, sad_id_bus_map)) { return false; } struct iio_stacks_on_socket iio_on_socket; iio_on_socket.socket_id = 0; if (sad_id_bus_map.size() != snr_sad_to_pmu_id_mapping.size()) { cerr << "Found unexpected number of stacks: " << sad_id_bus_map.size() << ", expected: " << snr_sad_to_pmu_id_mapping.size() << endl; return false; } for (auto sad_id_bus_pair = sad_id_bus_map.cbegin(); sad_id_bus_pair != sad_id_bus_map.cend(); ++sad_id_bus_pair) { int sad_id = sad_id_bus_pair->first; struct iio_stack stack; switch (sad_id) { case SNR_CBDMA_DMI_SAD_ID: { int root_bus = sad_id_bus_pair->second; stack.iio_unit_id = snr_sad_to_pmu_id_mapping.at(sad_id); stack.stack_name = snr_iio_stack_names[stack.iio_unit_id]; stack.busno = root_bus; // DMA Controller struct iio_bifurcated_part part; part.part_id = 0; struct pci pci_dev; pci_dev.bdf.busno = root_bus; pci_dev.bdf.devno = 0x01; pci_dev.bdf.funcno = 0x00; probe_pci(&pci_dev); part.root_pci_dev = pci_dev; stack.parts.push_back(part); part.part_id = 4; pci_dev.bdf.busno = root_bus; pci_dev.bdf.devno = 0x00; pci_dev.bdf.funcno = 0x00; probe_pci(&pci_dev); for (uint8_t bus = pci_dev.secondary_bus_number; bus <= pci_dev.subordinate_bus_number; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci child_pci_dev; child_pci_dev.bdf.busno = bus; child_pci_dev.bdf.devno = device; child_pci_dev.bdf.funcno = function; if (probe_pci(&child_pci_dev)) { part.child_pci_devs.push_back(child_pci_dev); } } } } part.root_pci_dev = pci_dev; stack.parts.push_back(part); } break; case SNR_PCIE_GEN3_SAD_ID: { int root_bus = sad_id_bus_pair->second; stack.busno = root_bus; stack.iio_unit_id = snr_sad_to_pmu_id_mapping.at(sad_id); stack.stack_name = snr_iio_stack_names[stack.iio_unit_id]; for (int slot = 4; slot < 8; slot++) { struct pci pci_dev; pci_dev.bdf.busno = root_bus; pci_dev.bdf.devno = slot; pci_dev.bdf.funcno = 0x00; if (!probe_pci(&pci_dev)) { continue; } int part_id = 4 + pci_dev.device_id - SNR_ROOT_PORT_A_DID; if ((part_id < 0) || (part_id > 4)) { cerr << "Invalid part ID " << part_id << endl; return false; } struct iio_bifurcated_part part; part.part_id = part_id; part.root_pci_dev = pci_dev; for (uint8_t bus = pci_dev.secondary_bus_number; bus <= pci_dev.subordinate_bus_number; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci child_pci_dev; child_pci_dev.bdf.busno = bus; child_pci_dev.bdf.devno = device; child_pci_dev.bdf.funcno = function; if (probe_pci(&child_pci_dev)) { part.child_pci_devs.push_back(child_pci_dev); } } } } stack.parts.push_back(part); } } break; case SNR_HQM_SAD_ID: case SNR_NIS_SAD_ID: case SNR_QAT_SAD_ID: JacobsvilleAccelerators(*sad_id_bus_pair, stack); break; default: cerr << "Unknown SAD ID: " << sad_id << endl; return false; } iio_on_socket.stacks.push_back(stack); } std::sort(iio_on_socket.stacks.begin(), iio_on_socket.stacks.end()); iios.push_back(iio_on_socket); return true; } class EagleStreamPlatformMapping: public IPlatformMapping { private: bool getRootBuses(std::map> &root_buses); bool stackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket); bool eagleStreamDmiStackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket); bool eagleStreamPciStackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket); bool eagleStreamAcceleratorStackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket); public: EagleStreamPlatformMapping(uint32_t sockets_count) : IPlatformMapping(sockets_count) {} ~EagleStreamPlatformMapping() = default; bool pciTreeDiscover(std::vector& iios) override; }; bool EagleStreamPlatformMapping::getRootBuses(std::map> &root_buses) { bool mapped = true; for (uint32_t domain = 0; mapped; domain++) { mapped = false; for (uint16_t b = 0; b < 256; b++) { for (uint8_t d = 0; d < 32; d++) { for (uint8_t f = 0; f < 8; f++) { struct pci pci_dev(domain, b, d, f); if (!probe_pci(&pci_dev)) { break; } if (!((pci_dev.vendor_id == PCM_INTEL_PCI_VENDOR_ID) && (pci_dev.device_id == SPR_MSM_DEV_ID))) { continue; } std::uint32_t cpuBusValid; std::vector cpuBusNo; int package_id; if (get_cpu_bus(domain, b, d, f, cpuBusValid, cpuBusNo, package_id) == false) { return false; } for (int cpuBusId = 0; cpuBusId < SPR_MSM_CPUBUSNO_MAX; ++cpuBusId) { if (!((cpuBusValid >> cpuBusId) & 0x1)) { cout << "CPU bus " << cpuBusId << " is disabled on package " << package_id << endl; continue; } if (spr_sad_to_pmu_id_mapping.find(cpuBusId) == spr_sad_to_pmu_id_mapping.end()) { cerr << "Cannot map CPU bus " << cpuBusId << " to IO PMU ID" << endl; continue; } int pmuId = spr_sad_to_pmu_id_mapping.at(cpuBusId); int rootBus = (cpuBusNo[(int)(cpuBusId / 4)] >> ((cpuBusId % 4) * 8)) & 0xff; root_buses[package_id][pmuId] = bdf(domain, rootBus, 0, 0); cout << "Mapped CPU bus #" << cpuBusId << " (domain " << domain << " bus " << std::hex << rootBus << std::dec << ") to IO PMU #" << pmuId << " package " << package_id << endl; mapped = true; } } } } } return !root_buses.empty(); } bool EagleStreamPlatformMapping::eagleStreamDmiStackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket) { struct iio_stack stack; stack.iio_unit_id = unit; stack.stack_name = spr_iio_stack_names[unit]; stack.busno = address.busno; stack.domain = address.domainno; struct iio_bifurcated_part pch_part; struct pci *pci = &pch_part.root_pci_dev; pch_part.part_id = SPR_DMI_PART_ID; pci->bdf = address; if (!probe_pci(pci)) { cerr << "Failed to probe DMI Stack: address: " << std::setw(4) << std::setfill('0') << std::hex << address.domainno << std::setw(2) << std::setfill('0') << ":" << address.busno << ":" << address.devno << "." << address.funcno << std::dec << endl; return false; } /* Scan devices behind PCH port only */ if (!iio_on_socket.socket_id) probeDeviceRange(pch_part.child_pci_devs, pci->bdf.domainno, pci->secondary_bus_number, pci->subordinate_bus_number); pci->parts_no.push_back(SPR_DMI_PART_ID); stack.parts.push_back(pch_part); iio_on_socket.stacks.push_back(stack); return true; } bool EagleStreamPlatformMapping::eagleStreamPciStackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket) { /* * Stacks that manage PCIe 4.0 (device 2,4,6,8) and 5.0 (device 1,3,5,7) Root Ports. */ struct iio_stack stack; stack.domain = address.domainno; stack.busno = address.busno; stack.iio_unit_id = unit; stack.stack_name = spr_iio_stack_names[unit]; for (int slot = 1; slot < 9; ++slot) { // Check if port is enabled struct pci root_pci_dev; root_pci_dev.bdf = bdf(address.domainno, address.busno, slot, 0x0); if (probe_pci(&root_pci_dev)) { struct iio_bifurcated_part part; // Bifurcated Root Ports to channel mapping on SPR part.part_id = slot - 1; part.root_pci_dev = root_pci_dev; for (uint8_t b = root_pci_dev.secondary_bus_number; b <= root_pci_dev.subordinate_bus_number; ++b) { for (uint8_t d = 0; d < 32; ++d) { for (uint8_t f = 0; f < 8; ++f) { struct pci child_pci_dev(address.domainno, b, d, f); if (probe_pci(&child_pci_dev)) { child_pci_dev.parts_no.push_back(part.part_id); part.child_pci_devs.push_back(child_pci_dev); } } } } } } iio_on_socket.stacks.push_back(stack); return true; } bool EagleStreamPlatformMapping::eagleStreamAcceleratorStackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket) { struct iio_stack stack; stack.iio_unit_id = unit; stack.domain = address.domainno; stack.busno = address.busno; // Channel mappings are checked on B0 stepping auto rb = address.busno; const std::vector acceleratorBuses{ rb, rb + 1, rb + 2, rb + 3 }; stack.stack_name = spr_iio_stack_names[unit]; for (auto& b : acceleratorBuses) { for (auto d = 0; d < 32; ++d) { for (auto f = 0; f < 8; ++f) { struct iio_bifurcated_part part; struct pci pci_dev(address.domainno, b, d, f); if (probe_pci(&pci_dev)) { if (pci_dev.vendor_id == PCM_INTEL_PCI_VENDOR_ID) { switch (pci_dev.device_id) { case DSA_DID: pci_dev.parts_no.push_back(0); pci_dev.parts_no.push_back(1); pci_dev.parts_no.push_back(2); break; case IAX_DID: pci_dev.parts_no.push_back(0); pci_dev.parts_no.push_back(1); pci_dev.parts_no.push_back(2); break; case HQMV2_DID: pci_dev.parts_no.push_back(5); break; case QATV2_DID: pci_dev.parts_no.push_back(4); break; default: continue; } part.child_pci_devs.push_back(pci_dev); } stack.parts.push_back(part); } } } } iio_on_socket.stacks.push_back(stack); return true; } bool EagleStreamPlatformMapping::stackProbe(int unit, const struct bdf &address, struct iio_stacks_on_socket &iio_on_socket) { switch (unit) { case SPR_DMI_PMON_ID: return eagleStreamDmiStackProbe(unit, address, iio_on_socket); case SPR_PCIE_GEN5_0_PMON_ID: case SPR_PCIE_GEN5_1_PMON_ID: case SPR_PCIE_GEN5_2_PMON_ID: case SPR_PCIE_GEN5_3_PMON_ID: case SPR_PCIE_GEN5_4_PMON_ID: return eagleStreamPciStackProbe(unit, address, iio_on_socket); case SPR_IDX0_PMON_ID: case SPR_IDX1_PMON_ID: case SPR_IDX2_PMON_ID: case SPR_IDX3_PMON_ID: return eagleStreamAcceleratorStackProbe(unit, address, iio_on_socket); default: return false; } } bool EagleStreamPlatformMapping::pciTreeDiscover(std::vector& iios) { std::map> root_buses; if (!getRootBuses(root_buses)) { return false; } for (auto iter = root_buses.cbegin(); iter != root_buses.cend(); ++iter) { auto rbs_on_socket = iter->second; struct iio_stacks_on_socket iio_on_socket; iio_on_socket.socket_id = iter->first; for (auto rb = rbs_on_socket.cbegin(); rb != rbs_on_socket.cend(); ++rb) { if (!stackProbe(rb->first, rb->second, iio_on_socket)) { return false; } } std::sort(iio_on_socket.stacks.begin(), iio_on_socket.stacks.end()); iios.push_back(iio_on_socket); } return true; } void IPlatformMapping::probeDeviceRange(std::vector &pci_devs, int domain, int secondary, int subordinate) { for (uint8_t bus = secondary; bus <= subordinate; bus++) { for (uint8_t device = 0; device < 32; device++) { for (uint8_t function = 0; function < 8; function++) { struct pci child_dev; child_dev.bdf.domainno = domain; child_dev.bdf.busno = bus; child_dev.bdf.devno = device; child_dev.bdf.funcno = function; if (probe_pci(&child_dev)) { if (secondary < child_dev.secondary_bus_number && subordinate < child_dev.subordinate_bus_number) { probeDeviceRange(child_dev.child_pci_devs, domain, child_dev.secondary_bus_number, child_dev.subordinate_bus_number); } pci_devs.push_back(child_dev); } } } } } std::unique_ptr IPlatformMapping::getPlatformMapping(int cpu_model, uint32_t sockets_count) { switch (cpu_model) { case PCM::SKX: return std::unique_ptr{new PurleyPlatformMapping(sockets_count)}; case PCM::ICX: return std::unique_ptr{new WhitleyPlatformMapping(sockets_count)}; case PCM::SNOWRIDGE: return std::unique_ptr{new JacobsvillePlatformMapping(sockets_count)}; case PCM::SPR: return std::unique_ptr{new EagleStreamPlatformMapping(sockets_count)}; default: return nullptr; } } ccr* get_ccr(PCM* m, uint64_t& ccr) { switch (m->getCPUModel()) { case PCM::SKX: return new skx_ccr(ccr); case PCM::ICX: case PCM::SNOWRIDGE: case PCM::SPR: return new icx_ccr(ccr); default: cerr << m->getCPUFamilyModelString() << " is not supported! Program aborted" << endl; exit(EXIT_FAILURE); } } int iio_evt_parse_handler(evt_cb_type cb_type, void *cb_ctx, counter &base_ctr, std::map &ofm, std::string key, uint64 numValue) { iio_evt_parse_context *context = (iio_evt_parse_context *)cb_ctx; PCM *m = context->m; if (cb_type == EVT_LINE_START) //this event will be called per line(start) { context->ctr.ccr = 0; } else if (cb_type == EVT_LINE_FIELD) //this event will be called per field of line { std::unique_ptr pccr(get_ccr(m, context->ctr.ccr)); switch (ofm[key]) { case PCM::OPCODE: break; case PCM::EVENT_SELECT: pccr->set_event_select(numValue); break; case PCM::UMASK: pccr->set_umask(numValue); break; case PCM::RESET: pccr->set_reset(numValue); break; case PCM::EDGE_DET: pccr->set_edge(numValue); break; case PCM::IGNORED: break; case PCM::OVERFLOW_ENABLE: pccr->set_ov_en(numValue); break; case PCM::ENABLE: pccr->set_enable(numValue); break; case PCM::INVERT: pccr->set_invert(numValue); break; case PCM::THRESH: pccr->set_thresh(numValue); break; case PCM::CH_MASK: pccr->set_ch_mask(numValue); break; case PCM::FC_MASK: pccr->set_fc_mask(numValue); break; case PCM::INVALID: default: std::cerr << "Field in -o file not recognized. The key is: " << key << "\n"; return -1; } } else if(cb_type == EVT_LINE_COMPLETE) //this event will be called every line(end) { context->ctr.h_event_name = base_ctr.h_event_name; context->ctr.v_event_name = base_ctr.v_event_name; context->ctr.idx = base_ctr.idx; context->ctr.multiplier = base_ctr.multiplier; context->ctr.divider = base_ctr.divider; context->ctr.h_id = base_ctr.h_id; context->ctr.v_id = base_ctr.v_id; //std::cout << "line parse OK, ctrcfg=0x" << std::hex << context->ctr.ccr << ", h_event_name=" << base_ctr.h_event_name << ", v_event_name=" << base_ctr.v_event_name; //std::cout << ", h_id=0x" << std::hex << base_ctr.h_id << ", v_id=0x" << std::hex << base_ctr.v_id; //std::cout << ", idx=0x"<< std::hex << base_ctr.idx << ", multiplier=0x" << std::hex << base_ctr.multiplier << ", divider=0x" << std::hex << base_ctr.divider << std::dec << "\n"; context->ctrs.push_back(context->ctr); } return 0; } result_content get_IIO_Samples(PCM *m, const std::vector& iios, const struct iio_counter & ctr, uint32_t delay_ms) { IIOCounterState *before, *after; uint64 rawEvents[4] = {0}; auto ccrCopy = ctr.ccr; std::unique_ptr pccr(get_ccr(m, ccrCopy)); rawEvents[ctr.idx] = pccr->get_ccr_value(); const int stacks_count = (int)m->getMaxNumOfIIOStacks(); before = new IIOCounterState[iios.size() * stacks_count]; after = new IIOCounterState[iios.size() * stacks_count]; m->programIIOCounters(rawEvents); for (auto socket = iios.cbegin(); socket != iios.cend(); ++socket) { for (auto stack = socket->stacks.cbegin(); stack != socket->stacks.cend(); ++stack) { auto iio_unit_id = stack->iio_unit_id; uint32_t idx = (uint32_t)stacks_count * socket->socket_id + iio_unit_id; before[idx] = m->getIIOCounterState(socket->socket_id, iio_unit_id, ctr.idx); } } MySleepMs(delay_ms); for (auto socket = iios.cbegin(); socket != iios.cend(); ++socket) { for (auto stack = socket->stacks.cbegin(); stack != socket->stacks.cend(); ++stack) { auto iio_unit_id = stack->iio_unit_id; uint32_t idx = (uint32_t)stacks_count * socket->socket_id + iio_unit_id; after[idx] = m->getIIOCounterState(socket->socket_id, iio_unit_id, ctr.idx); uint64_t raw_result = getNumberOfEvents(before[idx], after[idx]); uint64_t trans_result = uint64_t (raw_result * ctr.multiplier / (double) ctr.divider * (1000 / (double) delay_ms)); results[socket->socket_id][iio_unit_id][std::pair(ctr.h_id,ctr.v_id)] = trans_result; } } delete[] before; delete[] after; return results; } void collect_data(PCM *m, const double delay, vector& iios, vector& ctrs) { const uint32_t delay_ms = uint32_t(delay * 1000 / ctrs.size()); for (auto counter = ctrs.begin(); counter != ctrs.end(); ++counter) { counter->data.clear(); result_content sample = get_IIO_Samples(m, iios, *counter, delay_ms); counter->data.push_back(sample); } } void print_PCIeMapping(const std::vector& iios, const PCIDB & pciDB, std::ostream& stream) { uint32_t header_width = 100; string row; vector buffer; for (auto it = iios.begin(); it != iios.end(); ++it) { row = "Socket " + std::to_string((*it).socket_id); buffer.push_back(row); for (auto & stack : it->stacks) { std::stringstream ss; ss << "\t" << stack.stack_name << " domain 0x" << std::hex << std::setfill('0') << std::setw(4) << stack.domain << "; root bus: 0x" << std::setfill('0') << std::setw(2) << (int)stack.busno << "\tflipped: " << (stack.flipped ? "true" : "false"); buffer.push_back(ss.str()); for (auto& part : stack.parts) { vector pp = part.child_pci_devs; uint8_t level = 5; for (std::vector::const_iterator iunit = pp.begin(); iunit != pp.end(); ++iunit) { row = build_pci_header(pciDB, header_width, *iunit, -1, level); buffer.push_back(row); if (iunit->hasChildDevices()) { build_pci_tree(buffer, pciDB, header_width, *iunit, -1, level + 1); } else if (iunit->header_type == 1) { level++; } } } } } display(buffer, stream); } void print_usage(const string& progname) { cout << "\n Usage: \n " << progname << " --help | [interval] [options] \n"; cout << " => time interval in seconds (floating point number is accepted)\n"; cout << " to sample performance counters.\n"; cout << " If not specified - 3.0 is used\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; cout << " -csv-delimiter= | /csv-delimiter= => set custom csv delimiter\n"; cout << " -human-readable | /human-readable => use human readable format for output (for csv only)\n"; cout << " -root-port | /root-port => add root port devices to output (for csv only)\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; cout << " Examples:\n"; cout << " " << progname << " 1.0 -i=10 => print counters every second 10 times and exit\n"; cout << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << progname << " -csv -human-readable => every 3 second print counters in human-readable CSV format\n"; cout << "\n"; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream; check_and_set_silent(argc, argv, nullStream); set_signal_handlers(); std::cout << "\n Intel(r) Performance Counter Monitor " << PCM_VERSION << "\n"; std::cout << "\n This utility measures IIO information\n\n"; string program = string(argv[0]); vector counters; PCIDB pciDB; load_PCIDB(pciDB); bool csv = false; bool human_readable = false; bool show_root_port = false; std::string csv_delimiter = ","; std::string output_file; double delay = PCM_DELAY_DEFAULT; bool list = false; MainLoop mainLoop; PCM * m = PCM::getInstance(); iio_evt_parse_context evt_ctx; // Map with metrics names. map>> nameMap; map opcodeFieldMap; while (argc > 1) { argv++; argc--; std::string arg_value; if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (extract_argument_value(*argv, {"-csv-delimiter", "/csv-delimiter"}, arg_value)) { csv_delimiter = std::move(arg_value); } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; output_file = std::move(arg_value); } else if (check_argument_equals(*argv, {"-human-readable", "/human-readable"})) { human_readable = true; } else if (check_argument_equals(*argv, {"--list"})) { list = true; } else if (check_argument_equals(*argv, {"-root-port", "/root-port"})) { show_root_port = true; } else if (mainLoop.parseArg(*argv)) { continue; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } print_cpu_details(); //TODO: remove binding to max sockets count. if (m->getNumSockets() > max_sockets) { cerr << "Only systems with up to " << max_sockets << " sockets are supported! Program aborted\n"; exit(EXIT_FAILURE); } auto mapping = IPlatformMapping::getPlatformMapping(m->getCPUModel(), m->getNumSockets()); if (!mapping) { cerr << "Failed to discover pci tree: unknown platform" << endl; exit(EXIT_FAILURE); } std::vector iios; if (!mapping->pciTreeDiscover(iios)) { exit(EXIT_FAILURE); } std::ostream* output = &std::cout; std::fstream file_stream; if (!output_file.empty()) { file_stream.open(output_file.c_str(), std::ios_base::out); output = &file_stream; } if (list) { print_PCIeMapping(iios, pciDB, *output); return 0; } string ev_file_name; if (m->IIOEventsAvailable()) { ev_file_name = "opCode-" + std::to_string(m->getCPUModel()) + ".txt"; } else { cerr << "This CPU is not supported by PCM IIO tool! Program aborted\n"; exit(EXIT_FAILURE); } opcodeFieldMap["opcode"] = PCM::OPCODE; opcodeFieldMap["ev_sel"] = PCM::EVENT_SELECT; opcodeFieldMap["umask"] = PCM::UMASK; opcodeFieldMap["reset"] = PCM::RESET; opcodeFieldMap["edge_det"] = PCM::EDGE_DET; opcodeFieldMap["ignored"] = PCM::IGNORED; opcodeFieldMap["overflow_enable"] = PCM::OVERFLOW_ENABLE; opcodeFieldMap["en"] = PCM::ENABLE; opcodeFieldMap["invert"] = PCM::INVERT; opcodeFieldMap["thresh"] = PCM::THRESH; opcodeFieldMap["ch_mask"] = PCM::CH_MASK; opcodeFieldMap["fc_mask"] = PCM::FC_MASK; opcodeFieldMap["hname"] =PCM::H_EVENT_NAME; opcodeFieldMap["vname"] =PCM::V_EVENT_NAME; opcodeFieldMap["multiplier"] = PCM::MULTIPLIER; opcodeFieldMap["divider"] = PCM::DIVIDER; opcodeFieldMap["ctr"] = PCM::COUNTER_INDEX; evt_ctx.m = m; evt_ctx.ctrs.clear();//fill the ctrs by evt_handler call back func. try { load_events(ev_file_name, opcodeFieldMap, iio_evt_parse_handler, (void *)&evt_ctx, nameMap); } catch (std::exception & e) { std::cerr << "Error info:" << e.what() << "\n"; std::cerr << "Event configure file have the problem and cause the program exit, please double check it!\n"; exit(EXIT_FAILURE); } //print_nameMap(nameMap); //TODO: Taking from cli mainLoop([&]() { collect_data(m, delay, iios, evt_ctx.ctrs); vector display_buffer = csv ? build_csv(iios, evt_ctx.ctrs, human_readable, show_root_port, csv_delimiter, nameMap) : build_display(iios, evt_ctx.ctrs, pciDB, nameMap); display(display_buffer, *output); return true; }); file_stream.close(); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-latency.cpp000066400000000000000000000402021445420033100160710ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2018-2022, Intel Corporation // // written by Subhiksha Ravisundar #include "cpucounters.h" #ifdef _MSC_VER #pragma warning(disable : 4996) // for sprintf #include #include "windows/windriver.h" #else #include #endif #include #include #include #include #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include "lspci.h" #include "utils.h" using namespace std; using namespace pcm; #define DDR 0 #define PMM 1 #define L1 0 #define L2 1 #define RPQ_OCC 0 #define RPQ_INS 1 #define WPQ_OCC 2 #define WPQ_INS 3 #define FB_OCC_RD 0 #define FB_INS_RD 1 #define PCM_DELAY_DEFAULT 3.0 // in seconds #define MAX_CORES 4096 EventSelectRegister regs[2]; const uint8_t max_sockets = 64; struct socket_info_uncore { int socket_id; double rlatency; double wlatency; double rinsert; double winsert; double roccupancy; double woccupancy; }; struct core_info { int core_id; int socket; int thread; double latency; double occ_rd; double insert_rd; core_info() : core_id(0), socket(0), thread(0), latency(0.), occ_rd(0.), insert_rd(0.) {} }; struct socket_info_pci { int socket_id; uint64_t latency; }; struct res_uncore { string name; vector skt; } uncore_event[10]; struct res_core { string name; vector core; vector socket; } core_latency[10]; double DRAMSpeed; ServerUncoreCounterState * BeforeState; ServerUncoreCounterState * AfterState; SystemCounterState SysBeforeState, SysAfterState; std::vector BeforeState_core, AfterState_core; std::vector DummySocketStates; void collect_beforestate_uncore(PCM *m) { for (unsigned int i=0; igetNumSockets(); i++) { BeforeState[i] = m->getServerUncoreCounterState(i); } } void collect_afterstate_uncore(PCM *m) { for (unsigned int i=0; igetNumSockets(); i++) { AfterState[i] = m->getServerUncoreCounterState(i); } } void store_latency_uncore(PCM *m, bool ddr, int delay_ms) { for (unsigned int i=0; igetNumSockets(); i++) { uncore_event[ddr].skt[i].socket_id = i; const double delay_seconds = double(delay_ms) / 1000.; DRAMSpeed = double(getDRAMClocks(0, BeforeState[i], AfterState[i]))/(double(1e9) * delay_seconds); uncore_event[ddr].skt[i].rinsert = 0; uncore_event[ddr].skt[i].roccupancy = 0; uncore_event[ddr].skt[i].winsert = 0; uncore_event[ddr].skt[i].woccupancy = 0; for (size_t channel = 0; channel < m->getMCChannelsPerSocket(); ++channel) { uncore_event[ddr].skt[i].rinsert += (double)getMCCounter((uint32)channel, RPQ_INS, BeforeState[i], AfterState[i]); uncore_event[ddr].skt[i].roccupancy += (double)getMCCounter((uint32)channel, RPQ_OCC, BeforeState[i], AfterState[i]); uncore_event[ddr].skt[i].winsert += (double)getMCCounter((uint32)channel, WPQ_INS, BeforeState[i], AfterState[i]); uncore_event[ddr].skt[i].woccupancy += (double)getMCCounter((uint32)channel, WPQ_OCC, BeforeState[i], AfterState[i]); } if (uncore_event[ddr].skt[i].rinsert == 0.) { uncore_event[ddr].skt[i].rlatency = 0; } else { uncore_event[ddr].skt[i].rlatency = uncore_event[ddr].skt[i].roccupancy / uncore_event[ddr].skt[i].rinsert; } if (uncore_event[ddr].skt[i].winsert == 0.) { uncore_event[ddr].skt[i].wlatency = 0; } else { uncore_event[ddr].skt[i].wlatency = uncore_event[ddr].skt[i].woccupancy / uncore_event[ddr].skt[i].winsert; } swap(BeforeState[i], AfterState[i]); } } void collect_beforestate_core(PCM *m) { m->getAllCounterStates(SysBeforeState, DummySocketStates, BeforeState_core); } void collect_afterstate_core(PCM *m) { m->getAllCounterStates(SysAfterState, DummySocketStates, AfterState_core); } void store_latency_core(PCM *m) { unsigned int extra_clocks_for_L1_miss = 5; struct res_core core_event[3]; for (int k=0; k < 3; k++) { core_event[k].core.resize(MAX_CORES); } for (auto & s : core_latency[L1].socket) { s.occ_rd = 0; s.insert_rd = 0; } for (unsigned int i=0; igetNumCores(); i++) { const double frequency = (((double)getCycles(BeforeState_core[i], AfterState_core[i]) / (double)getRefCycles(BeforeState_core[i], AfterState_core[i])) * (double)m->getNominalFrequency()) / 1000000000; for(int j=0; j<2; j++)// 2 events { core_event[j].core[i].core_id = i; core_event[j].core[i].latency = (double)getNumberOfCustomEvents(j, BeforeState_core[i], AfterState_core[i]); } // L1 latency //Adding 5 clocks for L1 Miss core_latency[L1].core[i].latency = ((core_event[FB_OCC_RD].core[i].latency/core_event[FB_INS_RD].core[i].latency)+extra_clocks_for_L1_miss)/frequency; core_latency[L1].core[i].occ_rd = (core_event[FB_OCC_RD].core[i].latency); core_latency[L1].core[i].insert_rd = (core_event[FB_INS_RD].core[i].latency); const auto s = m->getSocketId(i); core_latency[L1].socket[s].occ_rd += (core_latency[L1].core[i].occ_rd + extra_clocks_for_L1_miss * core_latency[L1].core[i].insert_rd) / frequency; core_latency[L1].socket[s].insert_rd += core_latency[L1].core[i].insert_rd; } for (auto & s : core_latency[L1].socket) { s.latency = s.occ_rd / s.insert_rd; } swap(BeforeState_core, AfterState_core); swap(SysBeforeState, SysAfterState); } void print_verbose(PCM *m, int ddr_ip) { cout << "L1 Cache Latency ============================= \n"; for (unsigned int i=0; igetNumCores(); i++) { cout << "Core: " << i << "\n"; cout << "L1 Occupancy read: " << core_latency[0].core[i].occ_rd << "\n"; cout << "L1 Inserts read: " << core_latency[0].core[i].insert_rd << "\n"; cout << "\n"; } if (ddr_ip == DDR) { cout << "DDR Latency =================================\n"; cout << "Read Inserts Socket0: " << uncore_event[DDR].skt[0].rinsert << "\n"; cout << "Read Occupancy Socket0: " << uncore_event[DDR].skt[0].roccupancy << "\n"; cout << "Read Inserts Socket1: " << uncore_event[DDR].skt[1].rinsert << "\n"; cout << "Read Occupancy Socket1: " << uncore_event[DDR].skt[1].roccupancy << "\n"; cout << "\n"; cout << "Write Inserts Socket0: " << uncore_event[DDR].skt[0].winsert << "\n"; cout << "Write Occupancy Socket0: " << uncore_event[DDR].skt[0].woccupancy << "\n"; cout << "Write Inserts Socket1: " << uncore_event[DDR].skt[1].winsert << "\n"; cout << "Write Occupancy Socket1: " << uncore_event[DDR].skt[1].woccupancy << "\n"; } if (ddr_ip == PMM) { cout << "PMM Latency =================================\n"; cout << "Read Inserts Socket0: " << uncore_event[PMM].skt[0].rinsert << "\n"; cout << "Read Occupancy Socket0: " << uncore_event[PMM].skt[0].roccupancy << "\n"; cout << "Read Inserts Socket1: " << uncore_event[PMM].skt[1].rinsert << "\n"; cout << "Read Occupancy Socket1: " << uncore_event[PMM].skt[1].roccupancy << "\n"; cout << "\n"; cout << "Write Inserts Socket0: " << uncore_event[PMM].skt[0].winsert << "\n"; cout << "Write Occupancy Socket0: " << uncore_event[PMM].skt[0].woccupancy << "\n"; cout << "Write Inserts Socket1: " << uncore_event[PMM].skt[1].winsert << "\n"; cout << "Write Occupancy Socket1: " << uncore_event[PMM].skt[1].woccupancy << "\n"; } } void print_ddr(PCM *m, int ddr_ip) { if (ddr_ip == PMM) { if (m->PMMTrafficMetricsAvailable()) { cout << "PMM read Latency(ns)\n"; for (unsigned int n=0; ngetNumSockets(); n++) { cout << "Socket" << n << ": " << double(uncore_event[PMM].skt[n].rlatency)/DRAMSpeed; cout << "\n"; } } else { cout << "PMM metrics are not supported on your processor\n"; } } if (ddr_ip == DDR) { cout << "DDR read Latency(ns)\n"; for (unsigned int n=0; ngetNumSockets(); n++) { cout << "Socket" << n << ": " << double(uncore_event[DDR].skt[n].rlatency)/DRAMSpeed; cout << "\n"; } } } void print_core_stats(PCM *m, unsigned int core_size_per_socket, vector>> &sk_th) { auto printHeader = []() { cout << "\n\n"; cout << "L1 Cache Miss Latency(ns) [Adding 5 clocks for L1 Miss]\n\n";; }; printHeader(); for (unsigned int sid=0; sidgetNumSockets(); sid++) { for (unsigned int tid=0; tid< m->getThreadsPerCore(); tid++) { cout << "Socket" << sid << " Thread" << tid << " "; } } cout << "\n-----------------------------------------------------------------------------\n"; for (unsigned int cid=0; cidgetNumSockets(); sid++) { for (unsigned int tid=0; tidgetThreadsPerCore(); tid++) { cout << "Core" << sk_th[sid][tid][cid].core_id << ": " << fixed << setprecision(2) << sk_th[sid][tid][cid].latency << " "; } } cout << "\n"; } cout << "\n"; cout << "Average latency per socket"; printHeader(); for (unsigned int s = 0; s < m->getNumSockets(); ++s) { cout << "Socket" << s << ": " << core_latency[L1].socket[s].latency << "\n"; } cout << "\n"; } void print_all_stats(PCM *m, bool enable_pmm, bool enable_verbose) { vector < vector < vector < struct core_info >>> sk_th; unsigned int sid, cid, tid; unsigned int core_size_per_socket=0; //Populate Core info per Socket and thread_id //Create 3D vector with Socket as 1D, Thread as 2D and Core info for the 3D for (sid = 0; sid < m->getNumSockets(); sid++) { vector < vector > tmp_thread; for (tid = 0; tid < m->getThreadsPerCore(); tid++) { vector tmp_core; for (cid = 0; cid < m->getNumCores(); cid++) { if ((sid == (unsigned int)(m->getSocketId(cid))) && (tid == (unsigned int)(m->getThreadId(cid)))) { core_info tmp; tmp.core_id = cid; tmp.latency = core_latency[L1].core[cid].latency; tmp_core.push_back(tmp); } } core_size_per_socket = (unsigned int)tmp_core.size(); tmp_thread.push_back(tmp_core); } sk_th.push_back(tmp_thread); } print_core_stats(m, core_size_per_socket, sk_th); if (m->DDRLatencyMetricsAvailable()) { print_ddr(m, enable_pmm); if (enable_verbose) print_verbose(m, enable_pmm); } } EventSelectRegister build_core_register(uint64 reg_used, uint64 value, uint64 usr, uint64 os, uint64 enable, uint64 umask, uint64 event_select, uint64 edge) { regs[reg_used].value = value; regs[reg_used].fields.usr = usr; regs[reg_used].fields.os = os; regs[reg_used].fields.enable = enable; regs[reg_used].fields.umask = umask; regs[reg_used].fields.event_select = event_select; regs[reg_used].fields.edge = edge; return regs[reg_used]; } void check_status(PCM *m, PCM::ErrorCode status) { m->checkError(status); print_cpu_details(); if(!(m->LatencyMetricsAvailable())) { cerr << "Platform not Supported! Program aborted\n"; exit(EXIT_FAILURE); } if(m->getNumSockets() > max_sockets) { cerr << "Only systems with up to " <<(int)max_sockets<< " sockets are supported! Program aborted\n"; exit(EXIT_FAILURE); } } void build_registers(PCM *m, PCM::ExtendedCustomCoreEventDescription conf, bool enable_pmm, bool /*enable_verbose*/) { //Check if Online Cores = Available Cores. This version only supports available cores = online cores if (m->getNumCores() != m->getNumOnlineCores()) { cout << "Number of online cores should be equal to number of available cores\n"; exit(EXIT_FAILURE); } //Check for Maximum Custom Core Events if (m->getMaxCustomCoreEvents() < 2) { cout << "System should support a minimum of 2 Custom Core Events to run pcm-latency\n"; exit(EXIT_FAILURE); } //Creating conf conf.fixedCfg = NULL; // default conf.nGPCounters = 2; conf.gpCounterCfg = regs; conf.OffcoreResponseMsrValue[0] = 0; conf.OffcoreResponseMsrValue[1] = 0; // Registers for L1 cache regs[FB_OCC_RD] = build_core_register(FB_OCC_RD, 0, 1, 1, 1, 0x01, 0x48, 0); //L1d Fill Buffer Occupancy (Read Only) regs[FB_INS_RD] = build_core_register(FB_INS_RD, 0, 1, 1, 1, 0x48, 0xd1, 0); //MEM_LOAD_RETIRED(FB_HIT + L1_MISS) //Restructuring Counters for (int i=0; i <5; i++) { uncore_event[i].skt.resize(m->getNumSockets()); core_latency[i].core.resize(m->getNumCores()); core_latency[i].socket.resize(m->getNumSockets()); } //Program Core and Uncore m->resetPMU(); PCM::ErrorCode status = m->program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf); check_status(m, status); m->programServerUncoreLatencyMetrics(enable_pmm); } void collect_data(PCM *m, bool enable_pmm, bool enable_verbose, int delay_ms, MainLoop & mainLoop) { BeforeState = new ServerUncoreCounterState[m->getNumSockets()]; AfterState = new ServerUncoreCounterState[m->getNumSockets()]; mainLoop([&]() { collect_beforestate_uncore(m); collect_beforestate_core(m); MySleepMs(delay_ms); collect_afterstate_uncore(m); collect_afterstate_core(m); store_latency_uncore(m, enable_pmm, delay_ms);// 0 for DDR store_latency_core(m); print_all_stats(m, enable_pmm, enable_verbose); std::cout << std::flush; return true; }); delete[] BeforeState; delete[] AfterState; } void print_usage() { cout << "\nUsage: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " --PMM | -pmm => to enable PMM (Default DDR uncore latency)\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -v | --verbose => verbose Output\n"; cout << "\n"; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream; check_and_set_silent(argc, argv, nullStream); set_signal_handlers(); std::cout << "\n Intel(r) Performance Counter Monitor " << PCM_VERSION << "\n"; std::cout << "\n This utility measures Latency information\n\n"; bool enable_pmm = false; bool enable_verbose = false; int delay_ms = 1000; MainLoop mainLoop; if(argc > 1) do { argv++; argc--; if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (check_argument_equals(*argv, {"--PMM", "-pmm"})) { enable_pmm = true; continue; } else if (check_argument_equals(*argv, {"--verbose", "-v", "/v"})) { enable_verbose = true; continue; } } while(argc > 1); PCM::ExtendedCustomCoreEventDescription conf; PCM * m = PCM::getInstance(); build_registers(m, conf, enable_pmm, enable_verbose); collect_data(m, enable_pmm, enable_verbose, delay_ms, mainLoop); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-lspci.cpp000066400000000000000000000113741445420033100155540ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2017-2022, Intel Corporation // written by Patrick Lu #include "cpucounters.h" #ifdef _MSC_VER #pragma warning(disable : 4996) // for sprintf #include #include "windows/windriver.h" #else #include #endif #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include "lspci.h" using namespace std; using namespace pcm; void scanBus(int bus, const PCIDB & pciDB) { if(!PciHandleType::exists(0, bus, 8, 2)) return; std::cout << "BUS 0x" << std::hex << bus << std::dec << "\n"; struct iio_skx iio_skx; PciHandleType h(0, bus, 8, 2); uint32 cpubusno = 0; h.read32(0xcc, &cpubusno); // CPUBUSNO register iio_skx.stacks[0].busno = cpubusno & 0xff; iio_skx.stacks[1].busno = (cpubusno >> 8) & 0xff; iio_skx.stacks[2].busno = (cpubusno >> 16) & 0xff; iio_skx.stacks[3].busno = (cpubusno >> 24) & 0xff; h.read32(0xd0, &cpubusno); // CPUBUSNO1 register iio_skx.stacks[4].busno = cpubusno & 0xff; iio_skx.stacks[5].busno = (cpubusno >> 8) & 0xff; for (uint8_t stack = 0; stack < 6; stack++) { uint8_t busno = iio_skx.stacks[stack].busno; std::cout << "stack" << unsigned(stack) << std::hex << ":0x" << unsigned(busno) << std::dec << ",(" << unsigned(busno) << ")\n"; for (uint8_t part = 0; part < 3; part++) { struct pci *pci = &iio_skx.stacks[stack].parts[part].root_pci_dev; struct bdf *bdf = &pci->bdf; bdf->busno = busno; bdf->devno = part; bdf->funcno = 0; if (stack != 0 && busno == 0) /* This is a workaround to catch some IIO stack does not exist */ pci->exist = false; else probe_pci(pci); } } for (uint8_t stack = 0; stack < 6; stack++) { for (uint8_t part = 0; part < 4; part++) { struct pci p = iio_skx.stacks[stack].parts[part].root_pci_dev; if (!p.exist) continue; for (uint8_t b = p.secondary_bus_number; b <= p.subordinate_bus_number; b++) { /* FIXME: for 0:0.0, we may need to scan from secondary switch down; lgtm [cpp/fixme-comment] */ for (uint8_t d = 0; d < 32; d++) { for (uint8_t f = 0; f < 8; f++) { struct pci pci; pci.exist = false; pci.bdf.busno = b; pci.bdf.devno = d; pci.bdf.funcno = f; probe_pci(&pci); if (pci.exist) iio_skx.stacks[stack].parts[part].child_pci_devs.push_back(pci); } } } } } for (uint8_t stack = 1; stack < 6; stack++) { /* XXX: Maybe there is no point to display all built-in devices on DMI/CBDMA stacks, if so, change stack = 1 */ for (uint8_t part = 0; part < 4; part++) { vector v = iio_skx.stacks[stack].parts[part].child_pci_devs; struct pci pp = iio_skx.stacks[stack].parts[part].root_pci_dev; if (pp.exist) print_pci(pp, pciDB); for (vector::const_iterator iunit = v.begin(); iunit != v.end(); ++iunit) { struct pci p = *iunit; if (p.exist) print_pci(p, pciDB); } } } } PCM_MAIN_NOTHROW; int mainThrows(int /*argc*/, char * /*argv*/[]) { PCIDB pciDB; load_PCIDB(pciDB); PCM * m = PCM::getInstance(); if (!m->isSkxCompatible()) { cerr << "PCI tree display is currently not supported for processor model " << m->getCPUModel() << "\n"; } else { std::cout << "\n Display PCI tree information\n\n"; for (int bus = 0; bus < 256; ++bus) scanBus(bus, pciDB); } cerr << "Scanning all devices in group 0\n"; for (uint32 bus = 0; bus < 256; ++bus) { for (uint32 device = 0; device < 32; ++device) { for (uint32 function = 0; function < 8; ++function) { if (PciHandleType::exists(0, bus, device, function)) { PciHandleType h(0, bus, device, function); uint32 value = 0; h.read32(0, &value); const uint32 vendor = extract_bits_ui(value, 0, 15); const uint32 deviceID = extract_bits_ui(value, 16, 31); std::cout << "0:" << bus << ":" << device << ":" << function << " vendor 0x" << std::hex << vendor << " device 0x" << deviceID << std::dec << "\n"; } } } } return 0; } pcm-202307/src/pcm-memory.cpp000066400000000000000000001744771445420033100157700ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Patrick Lu // increased max sockets to 256 - Thomas Willhalm /*! \file pcm-memory.cpp \brief Example of using CPU counters: implements a performance counter monitoring utility for memory controller channels and DIMMs (ranks) + PMM memory traffic */ #include #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #include #include // for gettimeofday() #endif #include #include #include #include #include #include #include #include "cpucounters.h" #include "utils.h" #define PCM_DELAY_DEFAULT 1.0 // in seconds #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs #define DEFAULT_DISPLAY_COLUMNS 2 using namespace std; using namespace pcm; constexpr uint32 max_sockets = 256; uint32 max_imc_channels = ServerUncoreCounterState::maxChannels; const uint32 max_edc_channels = ServerUncoreCounterState::maxChannels; const uint32 max_imc_controllers = ServerUncoreCounterState::maxControllers; bool SPR_CXL = false; typedef struct memdata { float iMC_Rd_socket_chan[max_sockets][ServerUncoreCounterState::maxChannels]{}; float iMC_Wr_socket_chan[max_sockets][ServerUncoreCounterState::maxChannels]{}; float iMC_PMM_Rd_socket_chan[max_sockets][ServerUncoreCounterState::maxChannels]{}; float iMC_PMM_Wr_socket_chan[max_sockets][ServerUncoreCounterState::maxChannels]{}; float iMC_PMM_MemoryMode_Miss_socket_chan[max_sockets][ServerUncoreCounterState::maxChannels]{}; float iMC_Rd_socket[max_sockets]{}; float iMC_Wr_socket[max_sockets]{}; float iMC_PMM_Rd_socket[max_sockets]{}; float iMC_PMM_Wr_socket[max_sockets]{}; float CXLMEM_Rd_socket_port[max_sockets][ServerUncoreCounterState::maxCXLPorts]{}; float CXLMEM_Wr_socket_port[max_sockets][ServerUncoreCounterState::maxCXLPorts]{}; float CXLCACHE_Rd_socket_port[max_sockets][ServerUncoreCounterState::maxCXLPorts]{}; float CXLCACHE_Wr_socket_port[max_sockets][ServerUncoreCounterState::maxCXLPorts]{}; float iMC_PMM_MemoryMode_Miss_socket[max_sockets]{}; bool iMC_NM_hit_rate_supported{}; float iMC_PMM_MemoryMode_Hit_socket[max_sockets]{}; bool M2M_NM_read_hit_rate_supported{}; float iMC_NM_hit_rate[max_sockets]{}; float M2M_NM_read_hit_rate[max_sockets][max_imc_controllers]{}; float EDC_Rd_socket_chan[max_sockets][max_edc_channels]{}; float EDC_Wr_socket_chan[max_sockets][max_edc_channels]{}; float EDC_Rd_socket[max_sockets]{}; float EDC_Wr_socket[max_sockets]{}; uint64 partial_write[max_sockets]{}; ServerUncoreMemoryMetrics metrics{}; } memdata_t; bool anyPmem(const ServerUncoreMemoryMetrics & metrics) { return (metrics == Pmem) || (metrics == PmemMixedMode) || (metrics == PmemMemoryMode); } bool skipInactiveChannels = true; bool enforceFlush = false; void print_help(const string & prog_name) { cout << "\n Usage: \n " << prog_name << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -rank=X | /rank=X => monitor DIMM rank X. At most 2 out of 8 total ranks can be monitored simultaneously.\n"; cout << " -pmm | /pmm | -pmem | /pmem => monitor PMM memory bandwidth and DRAM cache hit rate in Memory Mode (default on systems with PMM support).\n"; cout << " -mm => monitor detailed PMM Memory Mode metrics per-socket.\n"; cout << " -mixed => monitor PMM mixed mode (AppDirect + Memory Mode).\n"; cout << " -partial => monitor partial writes instead of PMM (default on systems without PMM support).\n"; cout << " -nc | --nochannel | /nc => suppress output for individual channels.\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; cout << " -columns=X | /columns=X => Number of columns to display the NUMA Nodes, defaults to 2.\n"; cout << " -all | /all => Display all channels (even with no traffic)\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -u => update measurements instead of printing new ones\n"; print_enforce_flush_option_help(); #ifdef _MSC_VER cout << " --uninstallDriver | --installDriver=> (un)install driver\n"; #endif cout << " Examples:\n"; cout << " " << prog_name << " 1 => print counters every second without core and socket output\n"; cout << " " << prog_name << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << prog_name << " /csv 5 2>/dev/null => one sample every 5 seconds, and discard all diagnostic output\n"; cout << "\n"; } void printSocketBWHeader(uint32 no_columns, uint32 skt, const bool show_channel_output) { for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|---------------------------------------|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|-- Socket " << setw(2) << i << " --|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|---------------------------------------|"; } cout << "\n"; if (show_channel_output) { for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|-- Memory Channel Monitoring --|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|---------------------------------------|"; } cout << "\n"; } } void printSocketRankBWHeader(uint32 no_columns, uint32 skt) { for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|-------------------------------------------|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|-- Socket " << setw(2) << i << " --|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|-------------------------------------------|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|-- DIMM Rank Monitoring --|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|-------------------------------------------|"; } cout << "\n"; } void printSocketRankBWHeader_cvt(const uint32 numSockets, const uint32 num_imc_channels, const int rankA, const int rankB) { printDateForCSV(Header1); for (uint32 skt = 0 ; skt < (numSockets) ; ++skt) { for (uint32 channel = 0; channel < num_imc_channels; ++channel) { if (rankA >= 0) cout << "SKT" << skt << "," << "SKT" << skt << ","; if (rankB >= 0) cout << "SKT" << skt << "," << "SKT" << skt << ","; } } cout << endl; printDateForCSV(Header2); for (uint32 skt = 0 ; skt < (numSockets) ; ++skt) { for (uint32 channel = 0; channel < num_imc_channels; ++channel) { if (rankA >= 0) { cout << "Mem_Ch" << channel << "_R" << rankA << "_reads," << "Mem_Ch" << channel << "_R" << setw(1) << rankA << "_writes,"; } if (rankB >= 0) { cout << "Mem_Ch" << channel << "_R" << rankB << "_reads," << "Mem_Ch" << channel << "_R" << setw(1) << rankB << "_writes,"; } } } cout << endl; } void printSocketChannelBW(PCM *, memdata_t *md, uint32 no_columns, uint32 skt) { for (uint32 channel = 0; channel < max_imc_channels; ++channel) { // check all the sockets for bad channel "channel" unsigned bad_channels = 0; for (uint32 i=skt; i<(skt+no_columns); ++i) { if (md->iMC_Rd_socket_chan[i][channel] < 0.0 || md->iMC_Wr_socket_chan[i][channel] < 0.0) //If the channel read neg. value, the channel is not working; skip it. ++bad_channels; } if (bad_channels == no_columns) { // the channel is missing on all sockets in the row continue; } for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- Mem Ch " << setw(2) << channel << ": Reads (MB/s): " << setw(8) << md->iMC_Rd_socket_chan[i][channel] << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- Writes(MB/s): " << setw(8) << md->iMC_Wr_socket_chan[i][channel] << " --|"; } cout << "\n"; if (md->metrics == Pmem) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- PMM Reads(MB/s) : " << setw(8) << md->iMC_PMM_Rd_socket_chan[i][channel] << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- PMM Writes(MB/s) : " << setw(8) << md->iMC_PMM_Wr_socket_chan[i][channel] << " --|"; } cout << "\n"; } } } void printSocketChannelBW(uint32 no_columns, uint32 skt, uint32 num_imc_channels, const std::vector& uncState1, const std::vector& uncState2, uint64 elapsedTime, int rankA, int rankB) { for (uint32 channel = 0; channel < num_imc_channels; ++channel) { if(rankA >= 0) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- Mem Ch " << setw(2) << channel << " R " << setw(1) << rankA << ": Reads (MB/s): " << setw(8) << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::READ_RANK_A,uncState1[i],uncState2[i]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- Writes(MB/s): " << setw(8) << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::WRITE_RANK_A,uncState1[i],uncState2[i]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << " --|"; } cout << "\n"; } if(rankB >= 0) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- Mem Ch " << setw(2) << channel << " R " << setw(1) << rankB << ": Reads (MB/s): " << setw(8) << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::READ_RANK_B,uncState1[i],uncState2[i]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- Writes(MB/s): " << setw(8) << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::WRITE_RANK_B,uncState1[i],uncState2[i]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << " --|"; } cout << "\n"; } } } void printSocketChannelBW_cvt(const uint32 numSockets, const uint32 num_imc_channels, const std::vector& uncState1, const std::vector& uncState2, const uint64 elapsedTime, const int rankA, const int rankB) { printDateForCSV(Data); for (uint32 skt = 0 ; skt < numSockets; ++skt) { for (uint32 channel = 0 ; channel < num_imc_channels ; ++channel) { if(rankA >= 0) { cout << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::READ_RANK_A,uncState1[skt],uncState2[skt]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << "," << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::WRITE_RANK_A,uncState1[skt],uncState2[skt]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << ","; } if(rankB >= 0) { cout << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::READ_RANK_B,uncState1[skt],uncState2[skt]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << "," << (float) (getMCCounter(channel,ServerUncorePMUs::EventPosition::WRITE_RANK_B,uncState1[skt],uncState2[skt]) * 64 / 1000000.0 / (elapsedTime/1000.0)) << ","; } } } cout << endl; } uint32 getNumCXLPorts(PCM* m) { static int numPorts = -1; if (numPorts < 0) { for (uint32 s = 0; s < m->getNumSockets(); ++s) { numPorts = (std::max)(numPorts, (int)m->getNumCXLPorts(s)); } assert(numPorts >= 0); } return (uint32)numPorts; } void printSocketCXLBW(PCM* m, memdata_t* md, uint32 no_columns, uint32 skt) { uint32 numPorts = getNumCXLPorts(m); if (numPorts > 0) { for (uint32 i = skt; i < (no_columns + skt); ++i) { cout << "|---------------------------------------|"; } cout << "\n"; for (uint32 i = skt; i < (no_columns + skt); ++i) { cout << "|-- CXL Port Monitoring --|"; } cout << "\n"; for (uint32 i = skt; i < (no_columns + skt); ++i) { cout << "|---------------------------------------|"; } cout << "\n"; } for (uint32 port = 0; port < numPorts; ++port) { for (uint32 i = skt; i < (skt + no_columns); ++i) { cout << "|-- .mem --|"; } cout << "\n"; for (uint32 i = skt; i < (skt + no_columns); ++i) { cout << "|-- Writes(MB/s): " << setw(8) << md->CXLMEM_Wr_socket_port[i][port] << " --|"; } cout << "\n"; for (uint32 i = skt; i < (skt + no_columns); ++i) { cout << "|-- .cache --|"; } cout << "\n"; for (uint32 i = skt; i < (skt + no_columns); ++i) { cout << "|-- hst->dv(MB/s): " << setw(8) << md->CXLCACHE_Wr_socket_port[i][port] << " --|"; } cout << "\n"; } } float AD_BW(const memdata_t *md, const uint32 skt) { const auto totalPMM = md->iMC_PMM_Rd_socket[skt] + md->iMC_PMM_Wr_socket[skt]; return (max)(totalPMM - md->iMC_PMM_MemoryMode_Miss_socket[skt], float(0.0)); } float PMM_MM_Ratio(const memdata_t *md, const uint32 skt) { const auto dram = md->iMC_Rd_socket[skt] + md->iMC_Wr_socket[skt]; return md->iMC_PMM_MemoryMode_Miss_socket[skt] / dram; } void printSocketBWFooter(uint32 no_columns, uint32 skt, const memdata_t *md) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " Mem Read (MB/s) :" << setw(9) << md->iMC_Rd_socket[i] << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " Mem Write(MB/s) :" << setw(9) << md->iMC_Wr_socket[i] << " --|"; } cout << "\n"; if (anyPmem(md->metrics)) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " PMM Read (MB/s): " << setw(8) << md->iMC_PMM_Rd_socket[i] << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " PMM Write(MB/s): " << setw(8) << md->iMC_PMM_Wr_socket[i] << " --|"; } cout << "\n"; } if (md->metrics == PmemMixedMode) { for (uint32 i = skt; i < (skt + no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " PMM AD Bw(MB/s): " << setw(8) << AD_BW(md, i) << " --|"; } cout << "\n"; for (uint32 i = skt; i < (skt + no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " PMM MM Bw(MB/s): " << setw(8) << md->iMC_PMM_MemoryMode_Miss_socket[i] << " --|"; } cout << "\n"; for (uint32 i = skt; i < (skt + no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " PMM MM Bw/DRAM Bw:" << setw(8) << PMM_MM_Ratio(md, i) << " --|"; } cout << "\n"; } else if (md->metrics == Pmem && md->M2M_NM_read_hit_rate_supported) { for (uint32 ctrl = 0; ctrl < max_imc_controllers; ++ctrl) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << "." << ctrl << " NM read hit rate :" << setw(6) << md->M2M_NM_read_hit_rate[i][ctrl] << " --|"; } cout << "\n"; } } if (md->metrics == PmemMemoryMode && md->iMC_NM_hit_rate_supported) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " NM hit rate: " << setw(6) << md->iMC_NM_hit_rate[i] << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " NM hits (M/s): " << setw(7) << (md->iMC_PMM_MemoryMode_Hit_socket[i])/1000000. << " --|"; } cout << "\n"; for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " NM misses (M/s): " << setw(7) << (md->iMC_PMM_MemoryMode_Miss_socket[i])/1000000. << " --|"; } cout << "\n"; } if (md->metrics == PartialWrites) { for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " P. Write (T/s): " << dec << setw(10) << md->partial_write[i] << " --|"; } cout << "\n"; } for (uint32 i=skt; i<(skt+no_columns); ++i) { cout << "|-- NODE" << setw(2) << i << " Memory (MB/s): " << setw(11) << right << (md->iMC_Rd_socket[i]+md->iMC_Wr_socket[i]+ md->iMC_PMM_Rd_socket[i]+md->iMC_PMM_Wr_socket[i]) << " --|"; } cout << "\n"; for (uint32 i=skt; i<(no_columns+skt); ++i) { cout << "|---------------------------------------|"; } cout << "\n"; } void display_bandwidth(PCM *m, memdata_t *md, const uint32 no_columns, const bool show_channel_output, const bool print_update, const float CXL_Read_BW) { float sysReadDRAM = 0.0, sysWriteDRAM = 0.0, sysReadPMM = 0.0, sysWritePMM = 0.0; uint32 numSockets = m->getNumSockets(); uint32 skt = 0; cout.setf(ios::fixed); cout.precision(2); if (print_update) clear_screen(); while (skt < numSockets) { auto printHBM = [&]() { cout << "\ \r|---------------------------------------||---------------------------------------|\n\ \r|-- Processor socket " << skt << " --|\n\ \r|---------------------------------------||---------------------------------------|\n\ \r|-- DRAM Channel Monitoring --||-- HBM Channel Monitoring --|\n\ \r|---------------------------------------||---------------------------------------|\n\ \r"; const uint32 max_channels = (std::max)(max_edc_channels, max_imc_channels); if (show_channel_output) { float iMC_Rd, iMC_Wr, EDC_Rd, EDC_Wr; for (uint64 channel = 0; channel < max_channels; ++channel) { if (channel < max_imc_channels) { iMC_Rd = md->iMC_Rd_socket_chan[skt][channel]; iMC_Wr = md->iMC_Wr_socket_chan[skt][channel]; } else { iMC_Rd = -1.0; iMC_Wr = -1.0; } if (channel < max_edc_channels) { EDC_Rd = md->EDC_Rd_socket_chan[skt][channel]; EDC_Wr = md->EDC_Wr_socket_chan[skt][channel]; } else { EDC_Rd = -1.0; EDC_Wr = -1.0; } if (iMC_Rd >= 0.0 && iMC_Wr >= 0.0 && EDC_Rd >= 0.0 && EDC_Wr >= 0.0) cout << "|-- DRAM Ch " << setw(2) << channel << ": Reads (MB/s):" << setw(8) << iMC_Rd << " --||-- HBM Ch " << setw(2) << channel << ": Reads (MB/s):" << setw(9) << EDC_Rd << " --|\n|-- Writes(MB/s):" << setw(8) << iMC_Wr << " --||-- Writes(MB/s):" << setw(9) << EDC_Wr << " --|\n"; else if ((iMC_Rd < 0.0 || iMC_Wr < 0.0) && EDC_Rd >= 0.0 && EDC_Wr >= 0.0) cout << "|-- " << " --||-- HBM Ch " << setw(2) << channel << ": Reads (MB/s):" << setw(9) << EDC_Rd << " --|\n|-- " << " --||-- Writes(MB/s):" << setw(9) << EDC_Wr << " --|\n"; else if (iMC_Rd >= 0.0 && iMC_Wr >= 0.0 && (EDC_Rd < 0.0 || EDC_Wr < 0.0)) cout << "|-- DRAM Ch " << setw(2) << channel << ": Reads (MB/s):" << setw(8) << iMC_Rd << " --||-- " << " --|\n|-- Writes(MB/s):" << setw(8) << iMC_Wr << " --||-- " << " --|\n"; else continue; } } cout << "\ \r|-- DRAM Mem Read (MB/s):" << setw(11) << md->iMC_Rd_socket[skt] << " --||-- HBM Read (MB/s):" << setw(14+3) << md->EDC_Rd_socket[skt] << " --|\n\ \r|-- DRAM Mem Write (MB/s):" << setw(11) << md->iMC_Wr_socket[skt] << " --||-- HBM Write(MB/s):" << setw(14+3) << md->EDC_Wr_socket[skt] << " --|\n\ \r|-- DRAM Memory (MB/s) :" << setw(11) << md->iMC_Rd_socket[skt] + md->iMC_Wr_socket[skt] << " --||-- HBM (MB/s) :" << setw(14+3) << md->EDC_Rd_socket[skt] + md->EDC_Wr_socket[skt] << " --|\n\ \r|---------------------------------------||---------------------------------------|\n\ \r"; sysReadDRAM += (md->iMC_Rd_socket[skt] + md->EDC_Rd_socket[skt]); sysWriteDRAM += (md->iMC_Wr_socket[skt] + md->EDC_Wr_socket[skt]); skt += 1; }; auto printRow = [&skt,&show_channel_output,&m,&md,&sysReadDRAM,&sysWriteDRAM, &sysReadPMM, &sysWritePMM](const uint32 no_columns) { printSocketBWHeader(no_columns, skt, show_channel_output); if (show_channel_output) printSocketChannelBW(m, md, no_columns, skt); printSocketBWFooter(no_columns, skt, md); printSocketCXLBW(m, md, no_columns, skt); for (uint32 i = skt; i < (skt + no_columns); i++) { sysReadDRAM += md->iMC_Rd_socket[i]; sysWriteDRAM += md->iMC_Wr_socket[i]; sysReadPMM += md->iMC_PMM_Rd_socket[i]; sysWritePMM += md->iMC_PMM_Wr_socket[i]; } skt += no_columns; }; if (m->HBMmemoryTrafficMetricsAvailable()) { printHBM(); // no_columns is ignored, always 1 socket at a time } else if ((skt + no_columns) <= numSockets) // Full row { printRow(no_columns); } else //Display the remaining sockets in this row { printRow(numSockets - skt); } } { cout << "\ \r|---------------------------------------||---------------------------------------|\n"; if (anyPmem(md->metrics)) { cout << "\ \r|-- System DRAM Read Throughput(MB/s):" << setw(14) << sysReadDRAM << " --|\n\ \r|-- System DRAM Write Throughput(MB/s):" << setw(14) << sysWriteDRAM << " --|\n\ \r|-- System PMM Read Throughput(MB/s):" << setw(14) << sysReadPMM << " --|\n\ \r|-- System PMM Write Throughput(MB/s):" << setw(14) << sysWritePMM << " --|\n"; } if (SPR_CXL) { cout << "\ \r|-- System CXL Read Throughput(MB/s):" << setw(14) << CXL_Read_BW << " --|\n"; } cout << "\ \r|-- System Read Throughput(MB/s):" << setw(14) << sysReadDRAM+sysReadPMM << " --|\n\ \r|-- System Write Throughput(MB/s):" << setw(14) << sysWriteDRAM+sysWritePMM << " --|\n\ \r|-- System Memory Throughput(MB/s):" << setw(14) << sysReadDRAM+sysReadPMM+sysWriteDRAM+sysWritePMM << " --|\n\ \r|---------------------------------------||---------------------------------------|\n"; } } constexpr float CXLBWWrScalingFactor = 0.5; void display_bandwidth_csv(PCM *m, memdata_t *md, uint64 /*elapsedTime*/, const bool show_channel_output, const CsvOutputType outputType, const float CXL_Read_BW) { const uint32 numSockets = m->getNumSockets(); printDateForCSV(outputType); float sysReadDRAM = 0.0, sysWriteDRAM = 0.0, sysReadPMM = 0.0, sysWritePMM = 0.0; for (uint32 skt = 0; skt < numSockets; ++skt) { auto printSKT = [skt](int c = 1) { for (int i = 0; i < c; ++i) cout << "SKT" << skt << ','; }; if (show_channel_output) { for (uint64 channel = 0; channel < max_imc_channels; ++channel) { bool invalid_data = false; if (md->iMC_Rd_socket_chan[skt][channel] < 0.0 && md->iMC_Wr_socket_chan[skt][channel] < 0.0) //If the channel read neg. value, the channel is not working; skip it. invalid_data = true; choose(outputType, [printSKT]() { printSKT(2); }, [&channel]() { cout << "Ch" << channel << "Read," << "Ch" << channel << "Write,"; }, [&md, &skt, &channel, &invalid_data]() { if (invalid_data) cout << ",,"; else cout << setw(8) << md->iMC_Rd_socket_chan[skt][channel] << ',' << setw(8) << md->iMC_Wr_socket_chan[skt][channel] << ','; }); if (md->metrics == Pmem) { choose(outputType, [printSKT]() { printSKT(2); }, [&channel]() { cout << "Ch" << channel << "PMM_Read," << "Ch" << channel << "PMM_Write,"; }, [&skt, &md, &channel, &invalid_data]() { if (invalid_data) cout << ",,"; else cout << setw(8) << md->iMC_PMM_Rd_socket_chan[skt][channel] << ',' << setw(8) << md->iMC_PMM_Wr_socket_chan[skt][channel] << ','; }); } } } choose(outputType, [printSKT]() { printSKT(2); }, []() { cout << "Mem Read (MB/s),Mem Write (MB/s),"; }, [&md, &skt]() { cout << setw(8) << md->iMC_Rd_socket[skt] << ',' << setw(8) << md->iMC_Wr_socket[skt] << ','; }); if (anyPmem(md->metrics)) { choose(outputType, [printSKT]() { printSKT(2); }, []() { cout << "PMM_Read (MB/s), PMM_Write (MB/s),"; }, [&md, &skt]() { cout << setw(8) << md->iMC_PMM_Rd_socket[skt] << ',' << setw(8) << md->iMC_PMM_Wr_socket[skt] << ','; }); } if (md->metrics == PmemMemoryMode && md->iMC_NM_hit_rate_supported) { choose(outputType, [printSKT]() { printSKT(3); }, []() { cout << "NM hit rate,"; cout << "NM hits (M/s),"; cout << "NM misses (M/s),"; }, [&md, &skt]() { cout << setw(8) << md->iMC_NM_hit_rate[skt]<< ','; cout << setw(8) << md->iMC_PMM_MemoryMode_Hit_socket[skt]/1000000. << ','; cout << setw(8) << md->iMC_PMM_MemoryMode_Miss_socket[skt]/1000000. << ','; }); } if (md->metrics == Pmem && md->M2M_NM_read_hit_rate_supported) { for (uint32 c = 0; c < max_imc_controllers; ++c) { choose(outputType, [printSKT]() { printSKT(); }, [c]() { cout << "iMC" << c << " NM read hit rate,"; }, [&md, &skt, c]() { cout << setw(8) << md->M2M_NM_read_hit_rate[skt][c] << ','; }); } } if (md->metrics == PmemMixedMode) { choose(outputType, [printSKT]() { printSKT(3); }, []() { cout << "PMM_AD (MB/s), PMM_MM (MB/s), PMM_MM_Bw/DRAM_Bw,"; }, [&md, &skt]() { cout << setw(8) << AD_BW(md, skt) << ',' << setw(8) << md->iMC_PMM_MemoryMode_Miss_socket[skt] << ',' << setw(8) << PMM_MM_Ratio(md, skt) << ','; }); } if (m->HBMmemoryTrafficMetricsAvailable() == false) { if (md->metrics == PartialWrites) { choose(outputType, [printSKT]() { printSKT(); }, []() { cout << "P. Write (T/s),"; }, [&md, &skt]() { cout << setw(10) << dec << md->partial_write[skt] << ','; }); } } choose(outputType, [printSKT]() { printSKT(); }, []() { cout << "Memory (MB/s),"; }, [&]() { cout << setw(8) << md->iMC_Rd_socket[skt] + md->iMC_Wr_socket[skt] << ','; sysReadDRAM += md->iMC_Rd_socket[skt]; sysWriteDRAM += md->iMC_Wr_socket[skt]; sysReadPMM += md->iMC_PMM_Rd_socket[skt]; sysWritePMM += md->iMC_PMM_Wr_socket[skt]; }); if (m->HBMmemoryTrafficMetricsAvailable()) { if (show_channel_output) { for (uint64 channel = 0; channel < max_edc_channels; ++channel) { if (md->EDC_Rd_socket_chan[skt][channel] < 0.0 && md->EDC_Wr_socket_chan[skt][channel] < 0.0) //If the channel read neg. value, the channel is not working; skip it. continue; choose(outputType, [printSKT]() { printSKT(2); }, [&channel]() { cout << "EDC_Ch" << channel << "Read," << "EDC_Ch" << channel << "Write,"; }, [&md, &skt, &channel]() { cout << setw(8) << md->EDC_Rd_socket_chan[skt][channel] << ',' << setw(8) << md->EDC_Wr_socket_chan[skt][channel] << ','; }); } } choose(outputType, [printSKT]() { printSKT(3); }, []() { cout << "HBM Read (MB/s), HBM Write (MB/s), HBM (MB/s),"; }, [&]() { cout << setw(8) << md->EDC_Rd_socket[skt] << ',' << setw(8) << md->EDC_Wr_socket[skt] << ',' << setw(8) << md->EDC_Rd_socket[skt] + md->EDC_Wr_socket[skt] << ','; sysReadDRAM += md->EDC_Rd_socket[skt]; sysWriteDRAM += md->EDC_Wr_socket[skt]; }); } for (uint64 port = 0; port < m->getNumCXLPorts(skt); ++port) { choose(outputType, [printSKT]() { printSKT(2); }, [&port]() { cout << "CXL.mem_P" << port << "Write," << "CXL.cache_P" << port << "hst->dv,"; }, [&md, &skt, &port]() { cout << setw(8) << md->CXLMEM_Wr_socket_port[skt][port] << ',' << setw(8) << md->CXLCACHE_Wr_socket_port[skt][port] << ','; }); } } if (anyPmem(md->metrics)) { choose(outputType, []() { cout << "System,System,System,System,"; }, []() { cout << "DRAMRead,DRAMWrite,PMMREAD,PMMWrite,"; }, [&]() { cout << setw(10) << sysReadDRAM << ',' << setw(10) << sysWriteDRAM << ',' << setw(10) << sysReadPMM << ',' << setw(10) << sysWritePMM << ','; }); } if (SPR_CXL) { choose(outputType, []() { cout << "System,"; }, []() { cout << "CXLRead,"; }, [&]() { cout << setw(10) << CXL_Read_BW << ','; }); } choose(outputType, []() { cout << "System,System,System\n"; }, []() { cout << "Read,Write,Memory\n"; }, [&]() { cout << setw(10) << sysReadDRAM + sysReadPMM << ',' << setw(10) << sysWriteDRAM + sysWritePMM << ',' << setw(10) << sysReadDRAM + sysReadPMM + sysWriteDRAM + sysWritePMM << "\n"; }); } void calculate_bandwidth(PCM *m, const std::vector& uncState1, const std::vector& uncState2, const uint64 elapsedTime, const bool csv, bool & csvheader, uint32 no_columns, const ServerUncoreMemoryMetrics & metrics, const bool show_channel_output, const bool print_update, const uint64 SPR_CHA_CXL_Count) { //const uint32 num_imc_channels = m->getMCChannelsPerSocket(); //const uint32 num_edc_channels = m->getEDCChannelsPerSocket(); memdata_t md; md.metrics = metrics; const auto cpu_model = m->getCPUModel(); md.M2M_NM_read_hit_rate_supported = (cpu_model == PCM::SKX); md.iMC_NM_hit_rate_supported = (cpu_model == PCM::ICX); static bool mm_once = true; if (metrics == Pmem && md.M2M_NM_read_hit_rate_supported == false && md.iMC_NM_hit_rate_supported == true && mm_once) { cerr << "INFO: Use -mm option to monitor NM Memory Mode metrics\n"; mm_once = false; } static bool mm_once1 = true; if (metrics == PmemMemoryMode && md.M2M_NM_read_hit_rate_supported == true && md.iMC_NM_hit_rate_supported == false && mm_once1) { cerr << "INFO: Use -pmem option to monitor NM Memory Mode metrics\n"; mm_once1 = false; } for(uint32 skt = 0; skt < max_sockets; ++skt) { md.iMC_Rd_socket[skt] = 0.0; md.iMC_Wr_socket[skt] = 0.0; md.iMC_PMM_Rd_socket[skt] = 0.0; md.iMC_PMM_Wr_socket[skt] = 0.0; md.iMC_PMM_MemoryMode_Miss_socket[skt] = 0.0; md.iMC_PMM_MemoryMode_Hit_socket[skt] = 0.0; md.iMC_NM_hit_rate[skt] = 0.0; md.EDC_Rd_socket[skt] = 0.0; md.EDC_Wr_socket[skt] = 0.0; md.partial_write[skt] = 0; for (uint32 i = 0; i < max_imc_controllers; ++i) { md.M2M_NM_read_hit_rate[skt][i] = 0.; } for (size_t p = 0; p < ServerUncoreCounterState::maxCXLPorts; ++p) { md.CXLMEM_Rd_socket_port[skt][p] = 0.0; md.CXLMEM_Wr_socket_port[skt][p] = 0.0; md.CXLCACHE_Rd_socket_port[skt][p] = 0.0; md.CXLCACHE_Wr_socket_port[skt][p] = 0.0; } } auto toBW = [&elapsedTime](const uint64 nEvents) { return (float)(nEvents * 64 / 1000000.0 / (elapsedTime / 1000.0)); }; for(uint32 skt = 0; skt < m->getNumSockets(); ++skt) { const uint32 numChannels1 = (uint32)m->getMCChannels(skt, 0); // number of channels in the first controller if (m->HBMmemoryTrafficMetricsAvailable()) { const float scalingFactor = ((float) m->getHBMCASTransferSize()) / float(64.); for (uint32 channel = 0; channel < max_edc_channels; ++channel) { if (skipInactiveChannels && getEDCCounter(channel, ServerUncorePMUs::EventPosition::READ, uncState1[skt], uncState2[skt]) == 0.0 && getEDCCounter(channel, ServerUncorePMUs::EventPosition::WRITE, uncState1[skt], uncState2[skt]) == 0.0) { md.EDC_Rd_socket_chan[skt][channel] = -1.0; md.EDC_Wr_socket_chan[skt][channel] = -1.0; continue; } md.EDC_Rd_socket_chan[skt][channel] = scalingFactor * toBW(getEDCCounter(channel, ServerUncorePMUs::EventPosition::READ, uncState1[skt], uncState2[skt])); md.EDC_Wr_socket_chan[skt][channel] = scalingFactor * toBW(getEDCCounter(channel, ServerUncorePMUs::EventPosition::WRITE, uncState1[skt], uncState2[skt])); md.EDC_Rd_socket[skt] += md.EDC_Rd_socket_chan[skt][channel]; md.EDC_Wr_socket[skt] += md.EDC_Wr_socket_chan[skt][channel]; } } { for (uint32 channel = 0; channel < max_imc_channels; ++channel) { uint64 reads = 0, writes = 0, pmmReads = 0, pmmWrites = 0, pmmMemoryModeCleanMisses = 0, pmmMemoryModeDirtyMisses = 0; uint64 pmmMemoryModeHits = 0; reads = getMCCounter(channel, ServerUncorePMUs::EventPosition::READ, uncState1[skt], uncState2[skt]); writes = getMCCounter(channel, ServerUncorePMUs::EventPosition::WRITE, uncState1[skt], uncState2[skt]); if (metrics == Pmem) { pmmReads = getMCCounter(channel, ServerUncorePMUs::EventPosition::PMM_READ, uncState1[skt], uncState2[skt]); pmmWrites = getMCCounter(channel, ServerUncorePMUs::EventPosition::PMM_WRITE, uncState1[skt], uncState2[skt]); } else if (metrics == PmemMixedMode || metrics == PmemMemoryMode) { pmmMemoryModeCleanMisses = getMCCounter(channel, ServerUncorePMUs::EventPosition::PMM_MM_MISS_CLEAN, uncState1[skt], uncState2[skt]); pmmMemoryModeDirtyMisses = getMCCounter(channel, ServerUncorePMUs::EventPosition::PMM_MM_MISS_DIRTY, uncState1[skt], uncState2[skt]); } if (metrics == PmemMemoryMode) { pmmMemoryModeHits = getMCCounter(channel, ServerUncorePMUs::EventPosition::NM_HIT, uncState1[skt], uncState2[skt]); } if (skipInactiveChannels && (reads + writes == 0)) { if ((metrics != Pmem) || (pmmReads + pmmWrites == 0)) { if ((metrics != PmemMixedMode) || (pmmMemoryModeCleanMisses + pmmMemoryModeDirtyMisses == 0)) { md.iMC_Rd_socket_chan[skt][channel] = -1.0; md.iMC_Wr_socket_chan[skt][channel] = -1.0; continue; } } } if (metrics != PmemMemoryMode) { md.iMC_Rd_socket_chan[skt][channel] = toBW(reads); md.iMC_Wr_socket_chan[skt][channel] = toBW(writes); md.iMC_Rd_socket[skt] += md.iMC_Rd_socket_chan[skt][channel]; md.iMC_Wr_socket[skt] += md.iMC_Wr_socket_chan[skt][channel]; } if (metrics == Pmem) { md.iMC_PMM_Rd_socket_chan[skt][channel] = toBW(pmmReads); md.iMC_PMM_Wr_socket_chan[skt][channel] = toBW(pmmWrites); md.iMC_PMM_Rd_socket[skt] += md.iMC_PMM_Rd_socket_chan[skt][channel]; md.iMC_PMM_Wr_socket[skt] += md.iMC_PMM_Wr_socket_chan[skt][channel]; md.M2M_NM_read_hit_rate[skt][(channel < numChannels1) ? 0 : 1] += (float)reads; } else if (metrics == PmemMixedMode) { md.iMC_PMM_MemoryMode_Miss_socket_chan[skt][channel] = toBW(pmmMemoryModeCleanMisses + 2 * pmmMemoryModeDirtyMisses); md.iMC_PMM_MemoryMode_Miss_socket[skt] += md.iMC_PMM_MemoryMode_Miss_socket_chan[skt][channel]; } else if (metrics == PmemMemoryMode) { md.iMC_PMM_MemoryMode_Miss_socket[skt] += (float)((pmmMemoryModeCleanMisses + pmmMemoryModeDirtyMisses) / (elapsedTime / 1000.0)); md.iMC_PMM_MemoryMode_Hit_socket[skt] += (float)((pmmMemoryModeHits) / (elapsedTime / 1000.0)); } else { md.partial_write[skt] += (uint64)(getMCCounter(channel, ServerUncorePMUs::EventPosition::PARTIAL, uncState1[skt], uncState2[skt]) / (elapsedTime / 1000.0)); } } } if (metrics == PmemMemoryMode) { const int64 imcReads = getFreeRunningCounter(ServerUncoreCounterState::ImcReads, uncState1[skt], uncState2[skt]); if (imcReads >= 0) { md.iMC_Rd_socket[skt] += toBW(imcReads); } const int64 imcWrites = getFreeRunningCounter(ServerUncoreCounterState::ImcWrites, uncState1[skt], uncState2[skt]); if (imcWrites >= 0) { md.iMC_Wr_socket[skt] += toBW(imcWrites); } } if (metrics == PmemMixedMode || metrics == PmemMemoryMode) { const int64 pmmReads = getFreeRunningCounter(ServerUncoreCounterState::PMMReads, uncState1[skt], uncState2[skt]); if (pmmReads >= 0) { md.iMC_PMM_Rd_socket[skt] += toBW(pmmReads); } else for(uint32 c = 0; c < max_imc_controllers; ++c) { md.iMC_PMM_Rd_socket[skt] += toBW(getM2MCounter(c, ServerUncorePMUs::EventPosition::PMM_READ, uncState1[skt],uncState2[skt])); } const int64 pmmWrites = getFreeRunningCounter(ServerUncoreCounterState::PMMWrites, uncState1[skt], uncState2[skt]); if (pmmWrites >= 0) { md.iMC_PMM_Wr_socket[skt] += toBW(pmmWrites); } else for(uint32 c = 0; c < max_imc_controllers; ++c) { md.iMC_PMM_Wr_socket[skt] += toBW(getM2MCounter(c, ServerUncorePMUs::EventPosition::PMM_WRITE, uncState1[skt],uncState2[skt]));; } } if (metrics == Pmem) { for(uint32 c = 0; c < max_imc_controllers; ++c) { if(md.M2M_NM_read_hit_rate[skt][c] != 0.0) { md.M2M_NM_read_hit_rate[skt][c] = ((float)getM2MCounter(c, ServerUncorePMUs::EventPosition::NM_HIT, uncState1[skt],uncState2[skt]))/ md.M2M_NM_read_hit_rate[skt][c]; } } } const auto all = md.iMC_PMM_MemoryMode_Miss_socket[skt] + md.iMC_PMM_MemoryMode_Hit_socket[skt]; if (metrics == PmemMemoryMode && all != 0.0) { md.iMC_NM_hit_rate[skt] = md.iMC_PMM_MemoryMode_Hit_socket[skt] / all; } for (size_t p = 0; p < m->getNumCXLPorts(skt); ++p) { md.CXLMEM_Wr_socket_port[skt][p] = CXLBWWrScalingFactor * toBW(getCXLCMCounter((uint32)p, PCM::EventPosition::CXL_TxC_MEM, uncState1[skt], uncState2[skt])); md.CXLCACHE_Wr_socket_port[skt][p] = CXLBWWrScalingFactor * toBW(getCXLCMCounter((uint32)p, PCM::EventPosition::CXL_TxC_CACHE, uncState1[skt], uncState2[skt])); } } const auto CXL_Read_BW = toBW(SPR_CHA_CXL_Count); if (csv) { if (csvheader) { display_bandwidth_csv(m, &md, elapsedTime, show_channel_output, Header1, CXL_Read_BW); display_bandwidth_csv(m, &md, elapsedTime, show_channel_output, Header2, CXL_Read_BW); csvheader = false; } display_bandwidth_csv(m, &md, elapsedTime, show_channel_output, Data, CXL_Read_BW); } else { display_bandwidth(m, &md, no_columns, show_channel_output, print_update, CXL_Read_BW); } } void calculate_bandwidth_rank(PCM *m, const std::vector & uncState1, const std::vector& uncState2, const uint64 elapsedTime, const bool csv, bool &csvheader, const uint32 no_columns, const int rankA, const int rankB) { uint32 skt = 0; cout.setf(ios::fixed); cout.precision(2); uint32 numSockets = m->getNumSockets(); if (csv) { if (csvheader) { printSocketRankBWHeader_cvt(numSockets, max_imc_channels, rankA, rankB); csvheader = false; } printSocketChannelBW_cvt(numSockets, max_imc_channels, uncState1, uncState2, elapsedTime, rankA, rankB); } else { while(skt < numSockets) { auto printRow = [&skt, &uncState1, &uncState2, &elapsedTime, &rankA, &rankB](const uint32 no_columns) { printSocketRankBWHeader(no_columns, skt); printSocketChannelBW(no_columns, skt, max_imc_channels, uncState1, uncState2, elapsedTime, rankA, rankB); for (uint32 i = skt; i < (no_columns + skt); ++i) cout << "|-------------------------------------------|"; cout << "\n"; skt += no_columns; }; // Full row if ((skt + no_columns) <= numSockets) printRow(no_columns); else //Display the remaining sockets in this row printRow(numSockets - skt); } } } void readState(std::vector& state) { auto* pcm = PCM::getInstance(); assert(pcm); for (uint32 i = 0; i < pcm->getNumSockets(); ++i) state[i] = pcm->getServerUncoreCounterState(i); }; class CHAEventCollector { std::vector eventGroups; double delay; const char* sysCmd; const MainLoop& mainLoop; PCM* pcm; std::vector > MidStates; size_t curGroup = 0ULL; uint64 totalCount = 0ULL; CHAEventCollector() = delete; CHAEventCollector(const CHAEventCollector&) = delete; CHAEventCollector & operator = (const CHAEventCollector &) = delete; uint64 extractCHATotalCount(const std::vector& before, const std::vector& after) { uint64 result = 0; for (uint32 i = 0; i < pcm->getNumSockets(); ++i) { for (uint32 cbo = 0; cbo < pcm->getMaxNumOfCBoxes(); ++cbo) { for (uint32 ctr = 0; ctr < 4 && ctr < eventGroups[curGroup].size(); ++ctr) { result += getCBOCounter(cbo, ctr, before[i], after[i]); } } } return result; } void programGroup(const size_t group) { uint64 events[4] = { 0, 0, 0, 0 }; assert(group < eventGroups.size()); for (size_t i = 0; i < 4 && i < eventGroups[group].size(); ++i) { events[i] = eventGroups[group][i]; } pcm->programCboRaw(events, 0, 0); } public: CHAEventCollector(const double delay_, const char* sysCmd_, const MainLoop& mainLoop_, PCM* m) : sysCmd(sysCmd_), mainLoop(mainLoop_), pcm(m) { assert(pcm); switch (pcm->getCPUModel()) { case PCM::SPR: eventGroups = { { UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10C80B82) , // UNC_CHA_TOR_INSERTS.IA_MISS_CRDMORPH_CXL_ACC UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10c80782) , // UNC_CHA_TOR_INSERTS.IA_MISS_RFO_CXL_ACC UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10c81782) , // UNC_CHA_TOR_INSERTS.IA_MISS_DRD_CXL_ACC UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10C88782) // UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFRFO_CXL_ACC }, { UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10CCC782) , // UNC_CHA_TOR_INSERTS.IA_MISS_RFO_PREF_CXL_ACC UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10C89782) , // UNC_CHA_TOR_INSERTS.IA_MISS_DRD_PREF_CXL_ACC UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10CCD782) , // UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFDATA_CXL_ACC UNC_PMON_CTL_EVENT(0x35) + UNC_PMON_CTL_UMASK(0x01) + UNC_PMON_CTL_UMASK_EXT(0x10CCCF82) // UNC_CHA_TOR_INSERTS.IA_MISS_LLCPREFCODE_CXL_ACC } }; break; } assert(eventGroups.size() > 1); delay = delay_ / double(eventGroups.size()); MidStates.resize(eventGroups.size() - 1); for (auto& e : MidStates) { e.resize(pcm->getNumSockets()); } } void programFirstGroup() { programGroup(0); } void multiplexEvents(const std::vector& BeforeState) { for (curGroup = 0; curGroup < eventGroups.size() - 1; ++curGroup) { assert(curGroup < MidStates.size()); calibratedSleep(delay, sysCmd, mainLoop, pcm); readState(MidStates[curGroup]); // TODO: read only CHA counters (performance optmization) totalCount += extractCHATotalCount((curGroup > 0) ? MidStates[curGroup - 1] : BeforeState, MidStates[curGroup]); programGroup(curGroup + 1); readState(MidStates[curGroup]); // TODO: read only CHA counters (performance optmization) } calibratedSleep(delay, sysCmd, mainLoop, pcm); } uint64 getTotalCount(const std::vector& AfterState) { return eventGroups.size() * (totalCount + extractCHATotalCount(MidStates.back(), AfterState)); } void reset() { totalCount = 0; } }; PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream2; #ifdef PCM_FORCE_SILENT null_stream nullStream1; cout.rdbuf(&nullStream1); cerr.rdbuf(&nullStream2); #else check_and_set_silent(argc, argv, nullStream2); #endif set_signal_handlers(); cerr << "\n"; cerr << " Intel(r) Performance Counter Monitor: Memory Bandwidth Monitoring Utility " << PCM_VERSION << "\n"; cerr << "\n"; cerr << " This utility measures memory bandwidth per channel or per DIMM rank in real-time\n"; cerr << "\n"; double delay = -1.0; bool csv = false, csvheader = false, show_channel_output = true, print_update = false; uint32 no_columns = DEFAULT_DISPLAY_COLUMNS; // Default number of columns is 2 char * sysCmd = NULL; char ** sysArgv = NULL; int rankA = -1, rankB = -1; MainLoop mainLoop; string program = string(argv[0]); PCM * m = PCM::getInstance(); assert(m); if (m->getNumSockets() > max_sockets) { cerr << "Only systems with up to " << max_sockets << " sockets are supported! Program aborted\n"; exit(EXIT_FAILURE); } ServerUncoreMemoryMetrics metrics; metrics = m->PMMTrafficMetricsAvailable() ? Pmem : PartialWrites; if (argc > 1) do { argv++; argc--; string arg_value; if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_help(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = csvheader = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; csvheader = true; if (!arg_value.empty()) { m->setOutput(arg_value); } continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (extract_argument_value(*argv, {"-columns", "/columns"}, arg_value)) { if(arg_value.empty()) { continue; } no_columns = stoi(arg_value); if (no_columns == 0) no_columns = DEFAULT_DISPLAY_COLUMNS; if (no_columns > m->getNumSockets()) no_columns = m->getNumSockets(); continue; } else if (extract_argument_value(*argv, {"-rank", "/rank"}, arg_value)) { if(arg_value.empty()) { continue; } int rank = stoi(arg_value); if (rankA >= 0 && rankB >= 0) { cerr << "At most two DIMM ranks can be monitored \n"; exit(EXIT_FAILURE); } else { if(rank > 7) { cerr << "Invalid rank number " << rank << "\n"; exit(EXIT_FAILURE); } if(rankA < 0) rankA = rank; else if(rankB < 0) rankB = rank; metrics = PartialWrites; } continue; } else if (check_argument_equals(*argv, {"--nochannel", "/nc", "-nc"})) { show_channel_output = false; continue; } else if (check_argument_equals(*argv, {"-pmm", "/pmm", "-pmem", "/pmem"})) { metrics = Pmem; continue; } else if (check_argument_equals(*argv, {"-all", "/all"})) { skipInactiveChannels = false; continue; } else if (check_argument_equals(*argv, {"-mixed", "/mixed"})) { metrics = PmemMixedMode; continue; } else if (check_argument_equals(*argv, {"-mm", "/mm"})) { metrics = PmemMemoryMode; show_channel_output = false; continue; } else if (check_argument_equals(*argv, {"-partial", "/partial"})) { metrics = PartialWrites; continue; } else if (check_argument_equals(*argv, {"-u", "/u"})) { print_update = true; continue; } PCM_ENFORCE_FLUSH_OPTION #ifdef _MSC_VER else if (check_argument_equals(*argv, {"--uninstallDriver"})) { Driver tmpDrvObject; tmpDrvObject.uninstall(); cerr << "msr.sys driver has been uninstalled. You might need to reboot the system to make this effective.\n"; exit(EXIT_SUCCESS); } else if (check_argument_equals(*argv, {"--installDriver"})) { Driver tmpDrvObject = Driver(Driver::msrLocalPath()); if (!tmpDrvObject.start()) { tcerr << "Can not access CPU counters\n"; tcerr << "You must have a signed driver at " << tmpDrvObject.driverPath() << " and have administrator rights to run this program\n"; exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } #endif else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_help); continue; } } while (argc > 1); // end of command line parsing loop m->disableJKTWorkaround(); print_cpu_details(); if (!m->hasPCICFGUncore()) { cerr << "Unsupported processor model (" << m->getCPUModel() << ").\n"; if (m->memoryTrafficMetricsAvailable()) cerr << "For processor-level memory bandwidth statistics please use 'pcm' utility\n"; exit(EXIT_FAILURE); } if (anyPmem(metrics) && (m->PMMTrafficMetricsAvailable() == false)) { cerr << "PMM/Pmem traffic metrics are not available on your processor.\n"; exit(EXIT_FAILURE); } if (metrics == PmemMemoryMode && m->PMMMemoryModeMetricsAvailable() == false) { cerr << "PMM Memory Mode metrics are not available on your processor.\n"; exit(EXIT_FAILURE); } if (metrics == PmemMixedMode && m->PMMMixedModeMetricsAvailable() == false) { cerr << "PMM Mixed Mode metrics are not available on your processor.\n"; exit(EXIT_FAILURE); } if((rankA >= 0 || rankB >= 0) && anyPmem(metrics)) { cerr << "PMM/Pmem traffic metrics are not available on rank level\n"; exit(EXIT_FAILURE); } if((rankA >= 0 || rankB >= 0) && !show_channel_output) { cerr << "Rank level output requires channel output\n"; exit(EXIT_FAILURE); } PCM::ErrorCode status = m->programServerUncoreMemoryMetrics(metrics, rankA, rankB); m->checkError(status); max_imc_channels = (pcm::uint32)m->getMCChannelsPerSocket(); std::vector BeforeState(m->getNumSockets()); std::vector AfterState(m->getNumSockets()); uint64 BeforeTime = 0, AfterTime = 0; if ( (sysCmd != NULL) && (delay<=0.0) ) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } if (csv) { if( delay<=0.0 ) delay = PCM_DELAY_DEFAULT; } else { // for non-CSV mode delay < 1.0 does not make a lot of practical sense: // hard to read from the screen, or // in case delay is not provided in command line => set default if( ((delay<1.0) && (delay>0.0)) || (delay<=0.0) ) delay = PCM_DELAY_DEFAULT; } shared_ptr chaEventCollector; SPR_CXL = (PCM::SPR == m->getCPUModel()) && (getNumCXLPorts(m) > 0); if (SPR_CXL) { chaEventCollector = std::make_shared(delay, sysCmd, mainLoop, m); assert(chaEventCollector.get()); chaEventCollector->programFirstGroup(); } cerr << "Update every " << delay << " seconds\n"; if (csv) cerr << "Read/Write values expressed in (MB/s)" << endl; readState(BeforeState); uint64 SPR_CHA_CXL_Event_Count = 0; BeforeTime = m->getTickCount(); if( sysCmd != NULL ) { MySystem(sysCmd, sysArgv); } mainLoop([&]() { if (enforceFlush || !csv) cout << flush; if (chaEventCollector.get()) { chaEventCollector->multiplexEvents(BeforeState); } else { calibratedSleep(delay, sysCmd, mainLoop, m); } AfterTime = m->getTickCount(); readState(AfterState); if (chaEventCollector.get()) { SPR_CHA_CXL_Event_Count = chaEventCollector->getTotalCount(AfterState); chaEventCollector->reset(); chaEventCollector->programFirstGroup(); readState(AfterState); // TODO: re-read only CHA counters (performance optmization) } if (!csv) { //cout << "Time elapsed: " << dec << fixed << AfterTime-BeforeTime << " ms\n"; //cout << "Called sleep function for " << dec << fixed << delay_ms << " ms\n"; } if(rankA >= 0 || rankB >= 0) calculate_bandwidth_rank(m,BeforeState, AfterState, AfterTime - BeforeTime, csv, csvheader, no_columns, rankA, rankB); else calculate_bandwidth(m,BeforeState,AfterState,AfterTime-BeforeTime,csv,csvheader, no_columns, metrics, show_channel_output, print_update, SPR_CHA_CXL_Event_Count); swap(BeforeTime, AfterTime); swap(BeforeState, AfterState); if ( m->isBlocked() ) { // in case PCM was blocked after spawning child application: break monitoring loop here return false; } return true; }); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-mmio.cpp000066400000000000000000000121351445420033100153770ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012-2022, Intel Corporation // written by Roman Dementiev #include "cpucounters.h" #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #endif #include #include #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif using namespace pcm; #define MAX_BATCH_OPERATE_BYTES 1024 #define MAX_BATCH_READ_ROW_DISPLAY_BYTES 16 void print_usage(const char* progname) { std::cout << "Usage " << progname << " [-w value] [-q] [-d] address\n\n"; std::cout << " Reads/writes MMIO (memory mapped) register in the specified address\n"; std::cout << " -w value : write the value before reading \n"; std::cout << " -q : read/write 64-bit quad word (default is 32-bit double word)\n"; std::cout << " -d : output all numbers in dec (default is hex)\n"; std::cout << " -n size : number of bytes read from specified address(batch read mode), max bytes=" << MAX_BATCH_OPERATE_BYTES << "\n"; std::cout << " --version : print application version\n"; std::cout << "\n"; } template void doOp(const uint64 address, const uint64 offset, const uint32 batch_bytes, const bool write, T value, RD readOp, WR writeOp, const bool dec) { if (batch_bytes == 0) //single mode { if (!dec) std::cout << std::hex << std::showbase; constexpr auto bit = sizeof(T) * 8; if (write) { std::cout << " Writing " << value << " to " << std::dec << bit; if (!dec) std::cout << std::hex << std::showbase; std::cout <<"-bit MMIO register " << address << "\n"; writeOp(offset, value); } value = readOp(offset); std::cout << " Read value " << value << " from " << std::dec << bit; if (!dec) std::cout << std::hex << std::showbase; std::cout << "-bit MMIO register " << address << "\n\n"; } else //batch mode { uint32 i = 0, j= 0; std::cout << std::hex << " Dumping MMIO register range from 0x" << address << ", number of bytes=0x" << batch_bytes << "\n\n"; for(i = 0; i < batch_bytes; i+=MAX_BATCH_READ_ROW_DISPLAY_BYTES) { std::ostringstream row_disp_str(std::ostringstream::out); std::cout << " 0x" << (address + i) << ": "; for(j = 0; j < (MAX_BATCH_READ_ROW_DISPLAY_BYTES/sizeof(T)); j++) { value = readOp(offset + i + j*sizeof(T)); row_disp_str << "0x" << std::hex << std::setw(sizeof(T)*2) << std::setfill('0') << value << " "; } std::cout << row_disp_str.str() << ";\n"; } std::cout << "\n"; } } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); std::cout << "\n Intel(r) Performance Counter Monitor " << PCM_VERSION << "\n"; std::cout << "\n MMIO register read/write utility\n\n"; uint64 value = ~(0ULL); bool write = false; uint64 address = 0; bool dec = false; bool quad = false; uint32 batch_bytes = 0; int my_opt = -1; while ((my_opt = getopt(argc, argv, "w:dqn:")) != -1) { switch (my_opt) { case 'w': write = true; value = read_number(optarg); break; case 'd': dec = true; break; case 'q': quad = true; break; case 'n': batch_bytes = read_number(optarg); if (batch_bytes > MAX_BATCH_OPERATE_BYTES) { batch_bytes = MAX_BATCH_OPERATE_BYTES; } break; default: print_usage(argv[0]); return -1; } } if (optind >= argc) { print_usage(argv[0]); return -1; } address = read_number(argv[optind]); if (write == true) { batch_bytes = 0; //batch mode only support read. } try { constexpr uint64 rangeSize = 4096ULL; const uint64 baseAddr = address & (~(rangeSize - 1ULL)); // round down to 4K boundary const uint64 offset = address - baseAddr; if ((batch_bytes != 0) && (offset + batch_bytes > rangeSize)) { batch_bytes = (rangeSize - offset); //limit the boundary } MMIORange mmio(baseAddr, rangeSize, !write); using namespace std::placeholders; if (quad) { doOp(address, offset, batch_bytes, write, (uint64)value, std::bind(&MMIORange::read64, &mmio, _1), std::bind(&MMIORange::write64, &mmio, _1, _2), dec); } else { doOp(address, offset, batch_bytes, write, (uint32)value, std::bind(&MMIORange::read32, &mmio, _1), std::bind(&MMIORange::write32, &mmio, _1, _2), dec); } } catch (std::exception & e) { std::cerr << "Error accessing MMIO registers: " << e.what() << "\n"; std::cerr << "Please check if the program can access MMIO drivers.\n"; } return 0; } pcm-202307/src/pcm-msr.cpp000066400000000000000000000075751445420033100152530ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012-2022, Intel Corporation // written by Roman Dementiev #include "cpucounters.h" #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #endif #include #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif using namespace pcm; void print_usage(const char * progname) { std::cout << "Usage " << progname << " [-w value] [-c core] [-a] [-d] msr\n\n"; std::cout << " Reads/writes specified msr (model specific register) \n"; std::cout << " -w value : write the value before reading \n"; std::cout << " -c core : perform msr read/write on specified core (default is 0)\n"; std::cout << " -d : output all numbers in dec (default is hex)\n"; std::cout << " -a : perform msr read/write operations on all cores\n"; std::cout << " --version : print application version\n"; std::cout << "\n"; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) return 0; std::cout << "\n Intel(r) Performance Counter Monitor " << PCM_VERSION << "\n"; std::cout << "\n MSR read/write utility\n\n"; uint64 value = 0; bool write = false; int core = 0; int msr = -1; bool dec = false; int my_opt = -1; while ((my_opt = getopt(argc, argv, "w:c:da")) != -1) { switch (my_opt) { case 'w': write = true; value = read_number(optarg); break; case 'c': core = (int)read_number(optarg); break; case 'd': dec = true; break; case 'a': core = -1; break; default: print_usage(argv[0]); return -1; } } if (optind >= argc) { print_usage(argv[0]); return -1; } msr = (int)read_number(argv[optind]); #ifdef _MSC_VER // Increase the priority a bit to improve context switching delays on Windows SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); // WARNING: This driver code (msr.sys) is only for testing purposes, not for production use Driver drv = Driver(Driver::msrLocalPath()); // drv.stop(); // restart driver (usually not needed) if (!drv.start()) { tcerr << "Can not load MSR driver.\n"; tcerr << "You must have a signed driver at " << drv.driverPath() << " and have administrator rights to run this program\n"; return -1; } #endif auto doOne = [&dec, &write, &msr](int core, uint64 value) { try { MsrHandle h(core); if (!dec) std::cout << std::hex << std::showbase; if (write) { std::cout << " Writing " << value << " to MSR " << msr << " on core " << core << "\n"; if (h.write(msr, value) != 8) { std::cout << " Write error!\n"; } } value = 0; if (h.read(msr, &value) == 8) { std::cout << " Read value " << value << " from MSR " << msr << " on core " << core << "\n\n"; } else { std::cout << " Read error!\n"; } } catch (std::exception & e) { std::cerr << "Error accessing MSRs: " << e.what() << "\n"; std::cerr << "Please check if the program can access MSR drivers.\n"; } }; if (core >= 0) { doOne(core, value); } else { set_signal_handlers(); auto m = PCM::getInstance(); for (uint32 i = 0; i < m->getNumCores(); ++i) { if (m->isCoreOnline(i)) { doOne(i, value); } } } return 0; } pcm-202307/src/pcm-numa.cpp000066400000000000000000000226451445420033100154050ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev /*! \file pcm-numa.cpp \brief Example of using CPU counters: implements a performance counter monitoring utility for NUMA (remote and local memory accesses counting). Example for programming offcore response events */ #include #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #include #include // for gettimeofday() #endif #include #include #include #include #include #include #include #include "cpucounters.h" #include "utils.h" #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include #define PCM_DELAY_DEFAULT 1.0 // in seconds #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs using namespace std; using namespace pcm; void print_usage(const string & progname) { cout << "\n Usage: \n " << progname << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -pid PID | /pid PID => collect core metrics only for specified process ID\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; cout << " Examples:\n"; cout << " " << progname << " 1 => print counters every second without core and socket output\n"; cout << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << progname << " /csv 5 2>/dev/null => one sample every 5 seconds, and discard all diagnostic output\n"; cout << "\n"; } template void print_stats(const StateType & BeforeState, const StateType & AfterState, bool csv) { uint64 cycles = getCycles(BeforeState, AfterState); uint64 instr = getInstructionsRetired(BeforeState, AfterState); if (csv) { cout << double(instr) / double(cycles) << ","; cout << instr << ","; cout << cycles << ","; } else { cout << double(instr) / double(cycles) << " "; cout << unit_format(instr) << " "; cout << unit_format(cycles) << " "; } for (int i = 0; i < 2; ++i) if (!csv) cout << unit_format(getNumberOfCustomEvents(i, BeforeState, AfterState)) << " "; else cout << getNumberOfCustomEvents(i, BeforeState, AfterState) << ","; cout << "\n"; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream2; #ifdef PCM_FORCE_SILENT null_stream nullStream1; cout.rdbuf(&nullStream1); cerr.rdbuf(&nullStream2); #else check_and_set_silent(argc, argv, nullStream2); #endif set_signal_handlers(); cerr << "\n"; cerr << " Intel(r) Performance Counter Monitor: NUMA monitoring utility \n"; cerr << "\n"; double delay = -1.0; int pid{ -1 }; char * sysCmd = NULL; char ** sysArgv = NULL; bool csv = false; MainLoop mainLoop; string program = string(argv[0]); PCM * m = PCM::getInstance(); parsePID(argc, argv, pid); if (argc > 1) do { argv++; argc--; string arg_value; if (*argv == nullptr) { continue; } else if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; if (!arg_value.empty()) { m->setOutput(arg_value); } } else if (isPIDOption(argv)) { argv++; argc--; continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } while (argc > 1); // end of command line partsing loop EventSelectRegister def_event_select_reg; def_event_select_reg.value = 0; def_event_select_reg.fields.usr = 1; def_event_select_reg.fields.os = 1; def_event_select_reg.fields.enable = 1; PCM::ExtendedCustomCoreEventDescription conf; conf.fixedCfg = NULL; // default conf.nGPCounters = 2; try { m->setupCustomCoreEventsForNuma(conf); } catch (UnsupportedProcessorException& ) { cerr << "pcm-numa tool does not support your processor currently.\n"; exit(EXIT_FAILURE); } EventSelectRegister regs[4]; conf.gpCounterCfg = regs; for (int i = 0; i < 4; ++i) regs[i] = def_event_select_reg; regs[0].fields.event_select = m->getOCREventNr(0, 0).first; // OFFCORE_RESPONSE 0 event regs[0].fields.umask = m->getOCREventNr(0, 0).second; regs[1].fields.event_select = m->getOCREventNr(1, 0).first; // OFFCORE_RESPONSE 1 event regs[1].fields.umask = m->getOCREventNr(1, 0).second; print_pid_collection_message(pid); PCM::ErrorCode status = m->program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf, false, pid); m->checkError(status); print_cpu_details(); uint64 BeforeTime = 0, AfterTime = 0; SystemCounterState SysBeforeState, SysAfterState; const uint32 ncores = m->getNumCores(); vector BeforeState, AfterState; vector DummySocketStates; if ((sysCmd != NULL) && (delay <= 0.0)) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } if (csv) { if (delay <= 0.0) delay = PCM_DELAY_DEFAULT; } else { // for non-CSV mode delay < 1.0 does not make a lot of practical sense: // hard to read from the screen, or // in case delay is not provided in command line => set default if (((delay < 1.0) && (delay > 0.0)) || (delay <= 0.0)) delay = PCM_DELAY_DEFAULT; } cerr << "Update every " << delay << " seconds\n"; cout.precision(2); cout << fixed; BeforeTime = m->getTickCount(); m->getAllCounterStates(SysBeforeState, DummySocketStates, BeforeState); if (sysCmd != NULL) { MySystem(sysCmd, sysArgv); } mainLoop([&]() { if (!csv) cout << flush; calibratedSleep(delay, sysCmd, mainLoop, m); AfterTime = m->getTickCount(); m->getAllCounterStates(SysAfterState, DummySocketStates, AfterState); cout << "Time elapsed: " << dec << fixed << AfterTime - BeforeTime << " ms\n"; //cout << "Called sleep function for " << dec << fixed << delay_ms << " ms\n"; if (csv) cout << "Core,IPC,Instructions,Cycles,Local DRAM accesses,Remote DRAM accesses \n"; else cout << "Core | IPC | Instructions | Cycles | Local DRAM accesses | Remote DRAM Accesses \n"; for (uint32 i = 0; i < ncores; ++i) { if (csv) cout << i << ","; else cout << " " << setw(3) << i << " " << setw(2); print_stats(BeforeState[i], AfterState[i], csv); } if (csv) cout << "*,"; else { cout << "-------------------------------------------------------------------------------------------------------------------\n"; cout << " * "; } print_stats(SysBeforeState, SysAfterState, csv); cout << "\n"; swap(BeforeTime, AfterTime); swap(BeforeState, AfterState); swap(SysBeforeState, SysAfterState); if (m->isBlocked()) { // in case PCM was blocked after spawning child application: break monitoring loop here return false; } return true; }); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-pcicfg.cpp000066400000000000000000000107221445420033100156710ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2012, 2018-2022 Intel Corporation // written by Roman Dementiev #include "cpucounters.h" #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #endif #include #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif using namespace pcm; void print_usage(const char * progname) { std::cout << "Usage " << progname << " [-w value] [-d] [-i ID] [group bus device function] offset\n\n"; std::cout << " Reads/writes 32-bit PCICFG register \n"; std::cout << " -w value : write the value before reading \n"; std::cout << " -d : output all numbers in dec (default is hex)\n"; std::cout << " -i ID : specify Intel device ID instead of group bus device function\n"; std::cout << " --version : print application version\n"; std::cout << "\n"; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) return 0; std::cout << "\n Intel(r) Performance Counter Monitor " << PCM_VERSION << "\n"; std::cout << "\n PCICFG read/write utility\n\n"; #ifdef __linux__ #ifndef PCM_USE_PCI_MM_LINUX std::cout << "\n To access *extended* configuration space recompile with -DPCM_USE_PCI_MM_LINUX option.\n"; #endif #endif uint32 value = 0; bool write = false; bool dec = false; uint32 deviceID = 0; int my_opt = -1; while ((my_opt = getopt(argc, argv, "i:w:d")) != -1) { switch (my_opt) { case 'i': deviceID = (uint32)read_number(optarg); break; case 'w': write = true; value = (pcm::uint32)read_number(optarg); break; case 'd': dec = true; break; default: print_usage(argv[0]); return -1; } } if (optind + ((deviceID)?0:4) >= argc) { print_usage(argv[0]); return -1; } int group = -1; int bus = -1; int device = -1; int function = -1; int offset = -1; #ifdef _MSC_VER // Increase the priority a bit to improve context switching delays on Windows SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); // WARNING: This driver code (msr.sys) is only for testing purposes, not for production use Driver drv = Driver(Driver::msrLocalPath()); // drv.stop(); // restart driver (usually not needed) if (!drv.start()) { tcerr << "Can not load MSR driver.\n"; tcerr << "You must have a signed driver at " << drv.driverPath() << " and have administrator rights to run this program\n"; return -1; } #endif auto one = [&dec,&write](const uint32 & group, const uint32 & bus, const uint32 & device, const uint32 & function, const uint32 & offset, uint32 value) { try { PciHandleType h(group, bus, device, function); if (!dec) std::cout << std::hex << std::showbase; if (write) { std::cout << " Writing " << value << " to " << group << ":" << bus << ":" << device << ":" << function << "@" << offset << "\n"; h.write32(offset, value); } value = 0; h.read32(offset, &value); std::cout << " Read value " << value << " from " << group << ":" << bus << ":" << device << ":" << function << "@" << offset << "\n\n"; } catch (std::exception& e) { std::cerr << "Error accessing registers: " << e.what() << "\n"; std::cerr << "Please check if the program can access MSR/PCICFG drivers.\n"; } }; if (deviceID) { offset = (int)read_number(argv[optind]); forAllIntelDevices([&deviceID,&one,&offset, &value](const uint32 group, const uint32 bus, const uint32 device, const uint32 function, const uint32 device_id) { if (deviceID == device_id) { one(group, bus, device, function, offset, value); } }); } else { group = (int)read_number(argv[optind]); bus = (int)read_number(argv[optind + 1]); device = (int)read_number(argv[optind + 2]); function = (int)read_number(argv[optind + 3]); offset = (int)read_number(argv[optind + 4]); one(group, bus, device, function, offset, value); } return 0; } pcm-202307/src/pcm-pcie.cpp000066400000000000000000000234501445420033100153600ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // originally written by Patrick Lu // redesigned by Roman Sudarikov /*! \file pcm-pcie.cpp \brief Example of using uncore CBo counters: implements a performance counter monitoring utility for monitoring PCIe bandwidth */ #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #include #endif #include #include #include #include #include #include #include #include "pcm-pcie.h" #define PCM_DELAY_DEFAULT 1.0 // in seconds #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs using namespace std; bool events_printed = false; void print_events() { if(events_printed) { return; } cout << " PCIe event definitions (each event counts as a transfer): \n"; cout << " PCIe read events (PCI devices reading from memory - application writes to disk/network/PCIe device):\n"; cout << " PCIePRd - PCIe UC read transfer (partial cache line)\n"; cout << " PCIeRdCur* - PCIe read current transfer (full cache line)\n"; cout << " On Haswell Server PCIeRdCur counts both full/partial cache lines\n"; cout << " RFO* - Demand Data RFO\n"; cout << " CRd* - Demand Code Read\n"; cout << " DRd - Demand Data Read\n"; cout << " PCIeNSWr - PCIe Non-snoop write transfer (partial cache line)\n"; cout << " PCIe write events (PCI devices writing to memory - application reads from disk/network/PCIe device):\n"; cout << " PCIeWiLF - PCIe Write transfer (non-allocating) (full cache line)\n"; cout << " PCIeItoM - PCIe Write transfer (allocating) (full cache line)\n"; cout << " PCIeNSWr - PCIe Non-snoop write transfer (partial cache line)\n"; cout << " PCIeNSWrF - PCIe Non-snoop write transfer (full cache line)\n"; cout << " ItoM - PCIe write full cache line\n"; cout << " RFO - PCIe partial Write\n"; cout << " CPU MMIO events (CPU reading/writing to PCIe devices):\n"; cout << " UCRdF - read from uncacheable memory, including MMIO\n"; cout << " WCiL - streaming store (partial cache line), includes MOVDIRI\n\n"; cout << " WCiLF - streaming store (full cache line), includes MOVDIR64\n\n"; cout << " PRd - MMIO Read [Haswell Server only] (Partial Cache Line)\n"; cout << " WiL - MMIO Write (Full/Partial)\n\n"; cout << " * - NOTE: Depending on the configuration of your BIOS, this tool may report '0' if the message\n"; cout << " has not been selected.\n\n"; events_printed = true; } void print_usage(const string & progname) { cout << "\n Usage: \n " << progname << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; cout << " -B => Estimate PCIe B/W (in Bytes/sec) by multiplying\n"; cout << " the number of transfers by the cache line size (=64 bytes).\n"; cout << " -e => print additional PCIe LLC miss/hit statistics.\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; cout << " It overestimates the bandwidth under traffic with many partial cache line transfers.\n"; cout << "\n"; print_events(); cout << "\n"; cout << " Examples:\n"; cout << " " << progname << " 1 => print counters every second without core and socket output\n"; cout << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << progname << " /csv 5 2>/dev/null => one sample every 5 seconds, and discard all diagnostic output\n"; cout << "\n"; } IPlatform *IPlatform::getPlatform(PCM *m, bool csv, bool print_bandwidth, bool print_additional_info, uint32 delay) { switch (m->getCPUModel()) { case PCM::SPR: return new EagleStreamPlatform(m, csv, print_bandwidth, print_additional_info, delay); case PCM::ICX: case PCM::SNOWRIDGE: return new WhitleyPlatform(m, csv, print_bandwidth, print_additional_info, delay); case PCM::SKX: return new PurleyPlatform(m, csv, print_bandwidth, print_additional_info, delay); case PCM::BDX_DE: case PCM::BDX: case PCM::KNL: case PCM::HASWELLX: return new GrantleyPlatform(m, csv, print_bandwidth, print_additional_info, delay); case PCM::IVYTOWN: case PCM::JAKETOWN: return new BromolowPlatform(m, csv, print_bandwidth, print_additional_info, delay); default: return NULL; } } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream2; #ifdef PCM_FORCE_SILENT null_stream nullStream1; cout.rdbuf(&nullStream1); cerr.rdbuf(&nullStream2); #else check_and_set_silent(argc, argv, nullStream2); #endif set_signal_handlers(); cerr << "\n"; cerr << " Intel(r) Performance Counter Monitor: PCIe Bandwidth Monitoring Utility \n"; cerr << " This utility measures PCIe bandwidth in real-time\n"; cerr << "\n"; print_events(); double delay = -1.0; bool csv = false; bool print_bandwidth = false; bool print_additional_info = false; char * sysCmd = NULL; char ** sysArgv = NULL; MainLoop mainLoop; string program = string(argv[0]); PCM * m = PCM::getInstance(); if (argc > 1) do { argv++; argc--; string arg_value; if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; if (!arg_value.empty()) { m->setOutput(arg_value); } continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (check_argument_equals(*argv, {"-B", "/b"})) { print_bandwidth = true; continue; } else if (check_argument_equals(*argv, {"-e"})) { print_additional_info = true; continue; } else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } while(argc > 1); // end of command line partsing loop if ( (sysCmd != NULL) && (delay<=0.0) ) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } if (csv) { if ( delay<=0.0 ) delay = PCM_DELAY_DEFAULT; } else { // for non-CSV mode delay < 1.0 does not make a lot of practical sense: // hard to read from the screen, or // in case delay is not provided in command line => set default if ( ((delay < 1.0) && (delay > 0.0)) || (delay <= 0.0) ) { cerr << "For non-CSV mode delay < 1.0s does not make a lot of practical sense. Default delay 1s is used. Consider to use CSV mode for lower delay values\n"; delay = PCM_DELAY_DEFAULT; } } cerr << "Update every " << delay << " seconds\n"; // Delay in milliseconds unique_ptr platform(IPlatform::getPlatform(m, csv, print_bandwidth, print_additional_info, (uint)(delay * 1000))); if (!platform) { print_cpu_details(); cerr << "Jaketown, Ivytown, Haswell, Broadwell-DE, Skylake, Icelake, Snowridge and Sapphirerapids Server CPU is required for this tool! Program aborted\n"; exit(EXIT_FAILURE); } if ( sysCmd != NULL ) { MySystem(sysCmd, sysArgv); } // ================================== Begin Printing Output ================================== mainLoop([&]() { if (!csv) cout << flush; for(uint i=0; i < NUM_SAMPLES; i++) platform->getEvents(); platform->printHeader(); platform->printEvents(); platform->printAggregatedEvents(); platform->cleanup(); if (m->isBlocked()) return false; return true; }); // ================================== End Printing Output ================================== exit(EXIT_SUCCESS); } pcm-202307/src/pcm-pcie.h000066400000000000000000000656411445420033100150350ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation #pragma once //written by Roman Sudarikov #include #include "cpucounters.h" #include "utils.h" #include #include #include #include #include #if defined(_MSC_VER) typedef unsigned int uint; #endif using namespace std; using namespace pcm; #define NUM_SAMPLES (1) static void print(const vector &listNames, bool csv) { for(auto& name : listNames) if (csv) cout << "," << name; else cout << "| " << name << " "; } static uint getIdent (const string &s) { /* * We are adding "| " before and " " after the event name hence +5 to * strlen(eventNames). Rest of the logic is to center the event name. */ uint ident = 5 + (uint)s.size(); return (3 + ident / 2); } class IPlatform { void init(); public: IPlatform(PCM *m, bool csv, bool bandwidth, bool verbose); virtual void getEvents() = 0; virtual void printHeader() = 0; virtual void printEvents() = 0; virtual void printAggregatedEvents() = 0; virtual void cleanup() = 0; static IPlatform *getPlatform(PCM* m, bool csv, bool bandwidth, bool verbose, uint32 delay); virtual ~IPlatform() { } protected: PCM *m_pcm; bool m_csv; bool m_bandwidth; bool m_verbose; uint m_socketCount; enum eventFilter {TOTAL, MISS, HIT, fltLast}; vector filterNames, bwNames; }; void IPlatform::init() { print_cpu_details(); if (m_pcm->isSomeCoreOfflined()) { cerr << "Core offlining is not supported. Program aborted\n"; exit(EXIT_FAILURE); } } IPlatform::IPlatform(PCM *m, bool csv, bool bandwidth, bool verbose) : m_pcm(m), filterNames {"(Total)", "(Miss)", "(Hit)"}, bwNames {"PCIe Rd (B)", "PCIe Wr (B)"} { m_csv = csv; m_bandwidth = bandwidth; m_verbose = verbose; m_socketCount = m_pcm->getNumSockets(); init(); } /* * Common API to program, access and represent required Uncore counters. * The only difference is event opcodes and the way how bandwidth is calculated. */ class LegacyPlatform: public IPlatform { enum { before, after, total }; vector eventNames; vector eventGroups; uint32 m_delay; typedef vector > eventCount_t; array eventCount; virtual void getEvents() final; virtual void printHeader() final; virtual void printEvents() final; virtual void printAggregatedEvents() final; virtual void cleanup() final; void printBandwidth(uint socket, eventFilter filter); void printBandwidth(); void printSocketScopeEvent(uint socket, eventFilter filter, uint idx); void printSocketScopeEvents(uint socket, eventFilter filter); uint64 getEventCount (uint socket, uint idx); uint eventGroupOffset(eventGroup_t &eventGroup); void getEventGroup(eventGroup_t &eventGroup); void printAggregatedEvent(uint idx); public: LegacyPlatform(initializer_list events, initializer_list eventCodes, PCM *m, bool csv, bool bandwidth, bool verbose, uint32 delay) : IPlatform(m, csv, bandwidth, verbose), eventNames(events), eventGroups(eventCodes) { int eventsCount = 0; for (auto &group : eventGroups) eventsCount += (int)group.size(); // Delay for each multiplexing group. Counters will be scaled. m_delay = uint32(delay / eventGroups.size() / NUM_SAMPLES); eventSample.resize(m_socketCount); for (auto &e: eventSample) e.resize(eventsCount); for (auto &run : eventCount) { run.resize(m_socketCount); for (auto &events_ : run) events_.resize(eventsCount); } }; protected: vector> eventSample; virtual uint64 getReadBw(uint socket, eventFilter filter) = 0; virtual uint64 getWriteBw(uint socket, eventFilter filter) = 0; virtual uint64 getReadBw() = 0; virtual uint64 getWriteBw() = 0; virtual uint64 event(uint socket, eventFilter filter, uint idx) = 0; }; void LegacyPlatform::cleanup() { for(auto& socket : eventSample) fill(socket.begin(), socket.end(), 0); } inline uint64 LegacyPlatform::getEventCount (uint skt, uint idx) { return eventGroups.size() * (eventCount[after][skt][idx] - eventCount[before][skt][idx]); } uint LegacyPlatform::eventGroupOffset(eventGroup_t &eventGroup) { uint offset = 0; uint grpIdx = (uint)(&eventGroup - eventGroups.data()); for (auto iter = eventGroups.begin(); iter < eventGroups.begin() + grpIdx; iter++) offset += (uint)iter->size(); return offset; } void LegacyPlatform::getEventGroup(eventGroup_t &eventGroup) { m_pcm->programPCIeEventGroup(eventGroup); uint offset = eventGroupOffset(eventGroup); for (int run = before; run < total; run++) { for (uint skt = 0; skt < m_socketCount; ++skt) for (uint ctr = 0; ctr < eventGroup.size(); ++ctr) eventCount[run][skt][ctr + offset] = m_pcm->getPCIeCounterData(skt, ctr); if (run == before) MySleepMs(m_delay); } for(uint skt = 0; skt < m_socketCount; ++skt) for (uint idx = offset; idx < offset + eventGroup.size(); ++idx) eventSample[skt][idx] += getEventCount(skt, idx); } void LegacyPlatform::getEvents() { for (auto& evGroup : eventGroups) getEventGroup(evGroup); } void LegacyPlatform::printHeader() { cout << "Skt"; if (!m_csv) cout << ' '; print(eventNames, m_csv); if (m_bandwidth) print(bwNames, m_csv); cout << "\n"; } void LegacyPlatform::printBandwidth(uint skt, eventFilter filter) { typedef uint64 (LegacyPlatform::*bwFunc_t)(uint, eventFilter); vector bwFunc = { &LegacyPlatform::getReadBw, &LegacyPlatform::getWriteBw, }; if (!m_csv) for(auto& bw_f : bwFunc) { int ident = getIdent(bwNames[&bw_f - bwFunc.data()]); cout << setw(ident) << unit_format((this->*bw_f)(skt,filter)) << setw(5 + bwNames[&bw_f - bwFunc.data()].size() - ident) << ' '; } else for(auto& bw_f : bwFunc) cout << ',' << (this->*bw_f)(skt,filter); } void LegacyPlatform::printSocketScopeEvent(uint skt, eventFilter filter, uint idx) { uint64 value = event(skt, filter, idx); if (m_csv) cout << ',' << value; else { int ident = getIdent(eventNames[idx]); cout << setw(ident) << unit_format(value) << setw(5 + eventNames[idx].size() - ident) << ' '; } } void LegacyPlatform::printSocketScopeEvents(uint skt, eventFilter filter) { if (!m_csv) { int ident = (int)strlen("Skt |") / 2; cout << setw(ident) << skt << setw(ident) << ' '; } else cout << skt; for(uint idx = 0; idx < eventNames.size(); ++idx) printSocketScopeEvent(skt, filter, idx); if (m_bandwidth) printBandwidth(skt, filter); if(m_verbose) cout << filterNames[filter]; cout << "\n"; } void LegacyPlatform::printEvents() { for(uint skt =0; skt < m_socketCount; ++skt) if (!m_verbose) printSocketScopeEvents(skt, TOTAL); else for (uint flt = TOTAL; flt < fltLast; ++flt) printSocketScopeEvents(skt, static_cast(flt)); } void LegacyPlatform::printAggregatedEvent(uint idx) { uint64 value = 0; for(uint skt =0; skt < m_socketCount; ++skt) value += event(skt, TOTAL, idx); int ident = getIdent(eventNames[idx]); cout << setw(ident) << unit_format(value) << setw(5 + eventNames[idx].size() - ident) << ' '; } void LegacyPlatform::printBandwidth() { typedef uint64 (LegacyPlatform::*bwFunc_t)(); vector bwFunc = { &LegacyPlatform::getReadBw, &LegacyPlatform::getWriteBw, }; for(auto& bw_f : bwFunc) { int ident = getIdent(bwNames[&bw_f - bwFunc.data()]); cout << setw(ident) << unit_format((this->*bw_f)()) << setw(5 + bwNames[&bw_f - bwFunc.data()].size() - ident) << ' '; } } void LegacyPlatform::printAggregatedEvents() { if (!m_csv) { uint len = (uint)strlen("Skt "); for(auto& evt : eventNames) len += (5 + (uint)evt.size()); if (m_bandwidth) for(auto& bw : bwNames) len += (5 + (uint)bw.size()); while (len--) cout << '-'; cout << "\n"; int ident = (int)strlen("Skt |") /2 ; cout << setw(ident) << "*" << setw(ident) << ' '; for (uint idx = 0; idx < eventNames.size(); ++idx) printAggregatedEvent(idx); if (m_bandwidth) printBandwidth(); if (m_verbose) cout << "(Aggregate)\n\n"; else cout << "\n\n"; } } //SPR class EagleStreamPlatform: public LegacyPlatform { public: EagleStreamPlatform(PCM *m, bool csv, bool bandwidth, bool verbose, uint32 delay) : LegacyPlatform( {"PCIRdCur", "ItoM", "ItoMCacheNear", "UCRdF", "WiL", "WCiL", "WCiLF"}, { {0xC8F3FE00000435, 0xC8F3FD00000435, 0xCC43FE00000435, 0xCC43FD00000435}, {0xCD43FE00000435, 0xCD43FD00000435, 0xC877DE00000135, 0xC87FDE00000135}, {0xC86FFE00000135, 0xC867FE00000135,}, }, m, csv, bandwidth, verbose, delay) { }; private: enum eventIdx { PCIRdCur, ItoM, ItoMCacheNear, UCRdF, WiL, WCiL, WCiLF }; enum Events { PCIRdCur_miss, PCIRdCur_hit, ItoM_miss, ItoM_hit, ItoMCacheNear_miss, ItoMCacheNear_hit, UCRdF_miss, WiL_miss, WCiL_miss, WCiLF_miss, eventLast }; virtual uint64 getReadBw(uint socket, eventFilter filter); virtual uint64 getWriteBw(uint socket, eventFilter filter); virtual uint64 getReadBw(); virtual uint64 getWriteBw(); virtual uint64 event(uint socket, eventFilter filter, uint idx); }; uint64 EagleStreamPlatform::event(uint socket, eventFilter filter, uint idx) { uint64 event = 0; switch (idx) { case PCIRdCur: if(filter == TOTAL) event = eventSample[socket][PCIRdCur_miss] + eventSample[socket][PCIRdCur_hit]; else if (filter == MISS) event = eventSample[socket][PCIRdCur_miss]; else if (filter == HIT) event = eventSample[socket][PCIRdCur_hit]; break; case ItoM: if(filter == TOTAL) event = eventSample[socket][ItoM_miss] + eventSample[socket][ItoM_hit]; else if (filter == MISS) event = eventSample[socket][ItoM_miss]; else if (filter == HIT) event = eventSample[socket][ItoM_hit]; break; case ItoMCacheNear: if(filter == TOTAL) event = eventSample[socket][ItoMCacheNear_miss] + eventSample[socket][ItoMCacheNear_hit]; else if (filter == MISS) event = eventSample[socket][ItoMCacheNear_miss]; else if (filter == HIT) event = eventSample[socket][ItoMCacheNear_hit]; break; case UCRdF: if(filter == TOTAL || filter == MISS) event = eventSample[socket][UCRdF_miss]; break; case WiL: if(filter == TOTAL || filter == MISS) event = eventSample[socket][WiL_miss]; break; case WCiL: if(filter == TOTAL || filter == MISS) event = eventSample[socket][WCiL_miss]; break; case WCiLF: if(filter == TOTAL || filter == MISS) event = eventSample[socket][WCiLF_miss]; break; default: break; } return event; } uint64 EagleStreamPlatform::getReadBw(uint socket, eventFilter filter) { uint64 readBw = event(socket, filter, PCIRdCur); return (readBw * 64ULL); } uint64 EagleStreamPlatform::getWriteBw(uint socket, eventFilter filter) { uint64 writeBw = event(socket, filter, ItoM) + event(socket, filter, ItoMCacheNear); return (writeBw * 64ULL); } uint64 EagleStreamPlatform::getReadBw() { uint64 readBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) readBw += (event(socket, TOTAL, PCIRdCur)); return (readBw * 64ULL); } uint64 EagleStreamPlatform::getWriteBw() { uint64 writeBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) writeBw += (event(socket, TOTAL, ItoM) + event(socket, TOTAL, ItoMCacheNear)); return (writeBw * 64ULL); } //ICX class WhitleyPlatform: public LegacyPlatform { public: WhitleyPlatform(PCM *m, bool csv, bool bandwidth, bool verbose, uint32 delay) : LegacyPlatform( {"PCIRdCur", "ItoM", "ItoMCacheNear", "UCRdF","WiL"}, { {0xC8F3FE00000435, 0xC8F3FD00000435, 0xCC43FE00000435, 0xCC43FD00000435}, {0xCD43FE00000435, 0xCD43FD00000435, 0xC877DE00000135, 0xC87FDE00000135}, }, m, csv, bandwidth, verbose, delay) { }; private: enum eventIdx { PCIRdCur, ItoM, ItoMCacheNear, UCRdF, WiL, }; enum Events { PCIRdCur_miss, PCIRdCur_hit, ItoM_miss, ItoM_hit, ItoMCacheNear_miss, ItoMCacheNear_hit, UCRdF_miss, WiL_miss, eventLast }; virtual uint64 getReadBw(uint socket, eventFilter filter); virtual uint64 getWriteBw(uint socket, eventFilter filter); virtual uint64 getReadBw(); virtual uint64 getWriteBw(); virtual uint64 event(uint socket, eventFilter filter, uint idx); }; uint64 WhitleyPlatform::event(uint socket, eventFilter filter, uint idx) { uint64 event = 0; switch (idx) { case PCIRdCur: if(filter == TOTAL) event = eventSample[socket][PCIRdCur_miss] + eventSample[socket][PCIRdCur_hit]; else if (filter == MISS) event = eventSample[socket][PCIRdCur_miss]; else if (filter == HIT) event = eventSample[socket][PCIRdCur_hit]; break; case ItoM: if(filter == TOTAL) event = eventSample[socket][ItoM_miss] + eventSample[socket][ItoM_hit]; else if (filter == MISS) event = eventSample[socket][ItoM_miss]; else if (filter == HIT) event = eventSample[socket][ItoM_hit]; break; case ItoMCacheNear: if(filter == TOTAL) event = eventSample[socket][ItoMCacheNear_miss] + eventSample[socket][ItoMCacheNear_hit]; else if (filter == MISS) event = eventSample[socket][ItoMCacheNear_miss]; else if (filter == HIT) event = eventSample[socket][ItoMCacheNear_hit]; break; case UCRdF: if(filter == TOTAL || filter == MISS) event = eventSample[socket][UCRdF_miss]; break; case WiL: if(filter == TOTAL || filter == MISS) event = eventSample[socket][WiL_miss]; break; default: break; } return event; } uint64 WhitleyPlatform::getReadBw(uint socket, eventFilter filter) { uint64 readBw = event(socket, filter, PCIRdCur); return (readBw * 64ULL); } uint64 WhitleyPlatform::getWriteBw(uint socket, eventFilter filter) { uint64 writeBw = event(socket, filter, ItoM) + event(socket, filter, ItoMCacheNear); return (writeBw * 64ULL); } uint64 WhitleyPlatform::getReadBw() { uint64 readBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) readBw += (event(socket, TOTAL, PCIRdCur)); return (readBw * 64ULL); } uint64 WhitleyPlatform::getWriteBw() { uint64 writeBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) writeBw += (event(socket, TOTAL, ItoM) + event(socket, TOTAL, ItoMCacheNear)); return (writeBw * 64ULL); } // CLX, SKX class PurleyPlatform: public LegacyPlatform { public: PurleyPlatform(PCM *m, bool csv, bool bandwidth, bool verbose, uint32 delay) : LegacyPlatform( {"PCIRdCur", "RFO", "CRd", "DRd","ItoM", "PRd", "WiL"}, { {0x00043c33}, //PCIRdCur_miss {0x00043c37}, //PCIRdCur_hit {0x00040033}, //RFO_miss {0x00040037}, //RFO_hit {0x00040233}, //CRd_miss {0x00040237}, //CRd_hit {0x00040433}, //DRd_miss {0x00040437}, //DRd_hit {0x00049033}, //ItoM_miss {0x00049037}, //ItoM_hit {0x40040e33}, //PRd_miss {0x40040e37}, //PRd_hit {0x40041e33}, //WiL_miss {0x40041e37}, //WiL_hit }, m, csv, bandwidth, verbose, delay) { }; private: enum eventIdx { PCIRdCur, RFO, CRd, DRd, ItoM, PRd, WiL, }; enum Events { PCIRdCur_miss, PCIRdCur_hit, RFO_miss, RFO_hit, CRd_miss, CRd_hit, DRd_miss, DRd_hit, ItoM_miss, ItoM_hit, PRd_miss, PRd_hit, WiL_miss, WiL_hit, }; virtual uint64 getReadBw(uint socket, eventFilter filter); virtual uint64 getWriteBw(uint socket, eventFilter filter); virtual uint64 getReadBw(); virtual uint64 getWriteBw(); virtual uint64 event(uint socket, eventFilter filter, uint idx); }; uint64 PurleyPlatform::event(uint socket, eventFilter filter, uint idx) { uint64 event = 0; if(filter == TOTAL) event = eventSample[socket][2 * idx] + eventSample[socket][2 * idx + 1]; else if (filter == MISS) event = eventSample[socket][2 * idx]; else if (filter == HIT) event = eventSample[socket][2 * idx + 1]; return event; } uint64 PurleyPlatform::getReadBw(uint socket, eventFilter filter) { uint64 readBw = event(socket, filter, PCIRdCur) + event(socket, filter, RFO) + event(socket, filter, CRd) + event(socket, filter, DRd); return (readBw * 64ULL); } uint64 PurleyPlatform::getReadBw() { uint64 readBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) readBw += (event(socket, TOTAL, PCIRdCur) + event(socket, TOTAL, RFO) + event(socket, TOTAL, CRd) + event(socket, TOTAL, DRd)); return (readBw * 64ULL); } uint64 PurleyPlatform::getWriteBw(uint socket, eventFilter filter) { uint64 writeBw = event(socket, filter, RFO) + event(socket, filter, ItoM); return (writeBw * 64ULL); } uint64 PurleyPlatform::getWriteBw() { uint64 writeBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) writeBw += (event(socket, TOTAL, RFO) + event(socket, TOTAL, ItoM)); return (writeBw * 64ULL); } //BDX, HSX class GrantleyPlatform: public LegacyPlatform { public: GrantleyPlatform(PCM *m, bool csv, bool bandwidth, bool verbose, uint32 delay) : LegacyPlatform( {"PCIRdCur", "RFO", "CRd", "DRd","ItoM", "PRd", "WiL"}, { {0x19e10000}, //PCIRdCur_miss {0x19e00000}, //PCIRdCur_total {0x18030000}, //RFO_miss {0x18020000}, //RFO_total {0x18110000}, //CRd_miss {0x18100000}, //CRd_total {0x18210000}, //DRd_miss {0x18200000}, //DRd_total {0x1c830000}, //ItoM_miss {0x1c820000}, //ItoM_total {0x18710000}, //PRd_miss {0x18700000}, //PRd_total {0x18f10000}, //WiL_miss {0x18f00000}, //WiL_total }, m, csv, bandwidth, verbose, delay) { }; private: enum eventIdx { PCIRdCur, RFO, CRd, DRd, ItoM, PRd, WiL, }; enum Events { PCIRdCur_miss, PCIRdCur_total, RFO_miss, RFO_total, CRd_miss, CRd_total, DRd_miss, DRd_total, ItoM_miss, ItoM_total, PRd_miss, PRd_total, WiL_miss, WiL_total, }; virtual uint64 getReadBw(uint socket, eventFilter filter); virtual uint64 getWriteBw(uint socket, eventFilter filter); virtual uint64 getReadBw(); virtual uint64 getWriteBw(); virtual uint64 event(uint socket, eventFilter filter, uint idx); }; uint64 GrantleyPlatform::event(uint socket, eventFilter filter, uint idx) { uint64 event = 0; if(filter == HIT) if (eventSample[socket][2 * idx] < eventSample[socket][2 * idx + 1]) event = eventSample[socket][2 * idx + 1] - eventSample[socket][2 * idx]; else event = 0; else if (filter == MISS) event = eventSample[socket][2 * idx]; else if (filter == TOTAL) event = eventSample[socket][2 * idx + 1]; return event; } uint64 GrantleyPlatform::getReadBw(uint socket, eventFilter filter) { uint64 readBw = event(socket, filter, PCIRdCur) + event(socket, filter, RFO) + event(socket, filter, CRd) + event(socket, filter, DRd); return (readBw * 64ULL); } uint64 GrantleyPlatform::getReadBw() { uint64 readBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) readBw += (event(socket, TOTAL, PCIRdCur) + event(socket, TOTAL, RFO) + event(socket, TOTAL, CRd) + event(socket, TOTAL, DRd)); return (readBw * 64ULL); } uint64 GrantleyPlatform::getWriteBw(uint socket, eventFilter filter) { uint64 writeBw = event(socket, filter, RFO) + event(socket, filter, ItoM); return (writeBw * 64ULL); } uint64 GrantleyPlatform::getWriteBw() { uint64 writeBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) writeBw += (event(socket, TOTAL, RFO) + event(socket, TOTAL, ItoM)); return (writeBw * 64ULL); } //IVT, JKT class BromolowPlatform: public LegacyPlatform { public: BromolowPlatform(PCM *m, bool csv, bool bandwidth, bool verbose, uint32 delay) : LegacyPlatform( {"PCIeRdCur", "PCIeNSRd", "PCIeWiLF", "PCIeItoM","PCIeNSWr", "PCIeNSWrF"}, { {0x19e10000}, //PCIeRdCur_miss {0x19e00000}, //PCIeRdCur_total {0x1e410000}, //PCIeNSRd_miss {0x1e400000}, //PCIeNSRd_total {0x19410000}, //PCIeWiLF_miss {0x19400000}, //PCIeWiLF_total {0x19c10000}, //PCIeItoM_miss {0x19c00000}, //PCIeItoM_total {0x1e510000}, //PCIeNSWr_miss {0x1e500000}, //PCIeNSWr_total {0x1e610000}, //PCIeNSWrF_miss {0x1e600000}, //PCIeNSWrF_total }, m, csv, bandwidth, verbose, delay) { }; private: enum eventIdx { PCIeRdCur, PCIeNSRd, PCIeWiLF, PCIeItoM, PCIeNSWr, PCIeNSWrF, }; enum Events { PCIeRdCur_miss, PCIeRdCur_total, PCIeNSRd_miss, PCIeNSRd_total, PCIeWiLF_miss, PCIeWiLF_total, PCIeItoM_miss, PCIeItoM_total, PCIeNSWr_miss, PCIeNSWr_total, PCIeNSWrF_miss, PCIeNSWrF_total, }; virtual uint64 getReadBw(uint socket, eventFilter filter); virtual uint64 getWriteBw(uint socket, eventFilter filter); virtual uint64 getReadBw(); virtual uint64 getWriteBw(); virtual uint64 event(uint socket, eventFilter filter, uint idx); }; uint64 BromolowPlatform::event(uint socket, eventFilter filter, uint idx) { uint64 event = 0; if(filter == HIT) if (eventSample[socket][2 * idx] < eventSample[socket][2 * idx + 1]) event = eventSample[socket][2 * idx + 1] - eventSample[socket][2 * idx]; else event = 0; else if (filter == MISS) event = eventSample[socket][2 * idx]; else if (filter == TOTAL) event = eventSample[socket][2 * idx + 1]; return event; } uint64 BromolowPlatform::getReadBw(uint socket, eventFilter filter) { uint64 readBw = event(socket, filter, PCIeRdCur) + event(socket, filter, PCIeNSWr); return (readBw * 64ULL); } uint64 BromolowPlatform::getReadBw() { uint64 readBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) readBw += (event(socket, TOTAL, PCIeRdCur) + event(socket, TOTAL, PCIeNSWr)); return (readBw * 64ULL); } uint64 BromolowPlatform::getWriteBw(uint socket, eventFilter filter) { uint64 writeBw = event(socket, filter, PCIeWiLF) + event(socket, filter, PCIeItoM) + event(socket, filter, PCIeNSWr) + event(socket, filter, PCIeNSWrF); return (writeBw * 64ULL); } uint64 BromolowPlatform::getWriteBw() { uint64 writeBw = 0; for (uint socket = 0; socket < m_socketCount; socket++) writeBw += (event(socket, TOTAL, PCIeWiLF) + event(socket, TOTAL, PCIeItoM) + event(socket, TOTAL, PCIeNSWr) + event(socket, TOTAL, PCIeNSWrF)); return (writeBw * 64ULL); } pcm-202307/src/pcm-power.cpp000066400000000000000000000664771445420033100156140ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // added PPD cycles by Thomas Willhalm #include "cpucounters.h" #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #include #include // for gettimeofday() #endif #include #include #include #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include "utils.h" #define PCM_DELAY_DEFAULT 1.0 // in seconds #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs using namespace std; using namespace pcm; int getFirstRank(int imc_profile) { return imc_profile * 2; } int getSecondRank(int imc_profile) { return (imc_profile * 2) + 1; } double getCKEOffResidency(uint32 channel, uint32 rank, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after) { return double(getMCCounter(channel, (rank & 1) ? 2 : 0, before, after)) / double(getDRAMClocks(channel, before, after)); } int64 getCKEOffAverageCycles(uint32 channel, uint32 rank, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after) { uint64 div = getMCCounter(channel, (rank & 1) ? 3 : 1, before, after); if (div) return getMCCounter(channel, (rank & 1) ? 2 : 0, before, after) / div; return -1; } int64 getCyclesPerTransition(uint32 channel, uint32 rank, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after) { uint64 div = getMCCounter(channel, (rank & 1) ? 3 : 1, before, after); if (div) return getDRAMClocks(channel, before, after) / div; return -1; } uint64 getSelfRefreshCycles(uint32 channel, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after) { return getMCCounter(channel, 0, before, after); } uint64 getSelfRefreshTransitions(uint32 channel, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after) { return getMCCounter(channel, 1, before, after); } uint64 getPPDCycles(uint32 channel, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after) { return getMCCounter(channel, 2, before, after); } double getNormalizedPCUCounter(uint32 counter, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after) { return double(getPCUCounter(counter, before, after)) / double(getPCUClocks(before, after)); } double getNormalizedPCUCounter(uint32 counter, const ServerUncoreCounterState & before, const ServerUncoreCounterState & after, PCM * m) { const uint64 PCUClocks = (m->getPCUFrequency() * getInvariantTSC(before, after)) / m->getNominalFrequency(); // cout << "PCM Debug: PCU clocks " << PCUClocks << " PCU frequency: " << m->getPCUFrequency() << "\n"; return double(getPCUCounter(counter, before, after)) / double(PCUClocks); } int default_freq_band[3] = { 12, 20, 40 }; int freq_band[3]; void print_usage(const string & progname) { cout << "\n Usage: \n " << progname << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; // cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" // << " to a file, in case filename is provided\n"; cout << " [-m imc_profile] [-p pcu_profile] [-a freq_band0] [-b freq_band1] [-c freq_band2]\n\n"; cout << " Where: imc_profile, pcu_profile, freq_band0, freq_band1 and freq_band2 are the following:\n"; cout << " - profile (counter group) for IMC PMU. Possible values are: 0,1,2,3,4,-1 \n"; cout << " profile 0 - rank 0 and rank 1 residencies (default) \n"; cout << " profile 1 - rank 2 and rank 3 residencies \n"; cout << " profile 2 - rank 4 and rank 5 residencies \n"; cout << " profile 3 - rank 6 and rank 7 residencies \n"; cout << " profile 4 - self-refresh residencies \n"; cout << " profile -1 - omit IMC PMU output\n"; cout << " - profile (counter group) for PCU PMU. Possible values are: 0,1,2,3,4,5,-1 \n"; cout << " profile 0 - frequency residencies (default) \n"; cout << " profile 1 - core C-state residencies. The unit is the number of physical cores on the socket who were in C0, C3 or C6 during the measurement interval (e.g. 'C0 residency is 3.5' means on average 3.5 physical cores were resident in C0 state)\n"; cout << " profile 2 - Prochot (throttled) residencies and thermal frequency limit cycles \n"; cout << " profile 3 - {Thermal,Power,Clipped} frequency limit cycles \n"; cout << " profile 4 - {OS,Power,Clipped} frequency limit cycles \n"; cout << " profile 5 - frequency transition statistics \n"; cout << " profile 6 - package C-states residency and transition statistics \n"; cout << " profile 7 - UFS transition statistics (1) \n"; cout << " profile 8 - UFS transition statistics (2) \n"; cout << " profile -1 - omit PCU PMU output\n"; cout << " - frequency minimum for band 0 for PCU frequency residency profile [in 100MHz units] (default is " << default_freq_band[0] << "= " << 100 * default_freq_band[0] << "MHz)\n"; cout << " - frequency minimum for band 1 for PCU frequency residency profile [in 100MHz units] (default is " << default_freq_band[1] << "= " << 100 * default_freq_band[1] << "MHz)\n"; cout << " - frequency minimum for band 2 for PCU frequency residency profile [in 100MHz units] (default is " << default_freq_band[2] << "= " << 100 * default_freq_band[2] << "MHz)\n"; cout << "\n"; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream; check_and_set_silent(argc, argv, nullStream); set_signal_handlers(); cerr << "\n Intel(r) Performance Counter Monitor " << PCM_VERSION << "\n"; cerr << "\n Power Monitoring Utility\n"; int imc_profile = 0; int pcu_profile = 0; double delay = -1.0; char * sysCmd = NULL; char ** sysArgv = NULL; freq_band[0] = default_freq_band[0]; freq_band[1] = default_freq_band[1]; freq_band[2] = default_freq_band[2]; bool csv = false; MainLoop mainLoop; string program = string(argv[0]); PCM * m = PCM::getInstance(); if (argc > 1) do { argv++; argc--; string arg_value; if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; if (!arg_value.empty()) { m->setOutput(arg_value); } continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (check_argument_equals(*argv, {"-m"})) { argv++; argc--; imc_profile = atoi(*argv); continue; } else if (check_argument_equals(*argv, {"-p"})) { argv++; argc--; pcu_profile = atoi(*argv); continue; } else if (check_argument_equals(*argv, {"-a"})) { argv++; argc--; freq_band[0] = atoi(*argv); continue; } else if (check_argument_equals(*argv, {"-b"})) { argv++; argc--; freq_band[1] = atoi(*argv); continue; } else if (check_argument_equals(*argv, {"-c"})) { argv++; argc--; freq_band[2] = atoi(*argv); continue; } else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } while (argc > 1); // end of command line partsing loop m->disableJKTWorkaround(); const int cpu_model = m->getCPUModel(); if (!(m->hasPCICFGUncore())) { cerr << "Unsupported processor model (" << cpu_model << ").\n"; exit(EXIT_FAILURE); } EventSelectRegister regs[PERF_MAX_CUSTOM_COUNTERS]; PCM::ExtendedCustomCoreEventDescription conf; int32 nCorePowerLicenses = 0; std::vector licenseStr; switch (cpu_model) { case PCM::SKX: case PCM::ICX: { std::vector skxLicenseStr = { "Core cycles where the core was running with power-delivery for baseline license level 0. This includes non-AVX codes, SSE, AVX 128-bit, and low-current AVX 256-bit codes.", "Core cycles where the core was running with power-delivery for license level 1. This includes high current AVX 256-bit instructions as well as low current AVX 512-bit instructions.", "Core cycles where the core was running with power-delivery for license level 2 (introduced in Skylake Server michroarchtecture). This includes high current AVX 512-bit instructions." }; licenseStr = skxLicenseStr; regs[0].fields.event_select = 0x28; // CORE_POWER.LVL0_TURBO_LICENSE regs[0].fields.umask = 0x07; // CORE_POWER.LVL0_TURBO_LICENSE regs[1].fields.event_select = 0x28; // CORE_POWER.LVL1_TURBO_LICENSE regs[1].fields.umask = 0x18; // CORE_POWER.LVL1_TURBO_LICENSE regs[2].fields.event_select = 0x28; // CORE_POWER.LVL2_TURBO_LICENSE regs[2].fields.umask = 0x20; // CORE_POWER.LVL2_TURBO_LICENSE conf.nGPCounters = 3; nCorePowerLicenses = 3; conf.gpCounterCfg = regs; } break; } for (size_t l = 0; l < licenseStr.size(); ++l) { cout << "Core Power License " << std::to_string(l) << ": " << licenseStr[l] << "\n"; } if (conf.gpCounterCfg) { m->checkError(m->program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf)); } m->checkError(m->programServerUncorePowerMetrics(imc_profile, pcu_profile, freq_band)); const auto numSockets = m->getNumSockets(); std::vector BeforeState(numSockets); std::vector AfterState(numSockets); SystemCounterState dummySystemState; std::vector dummyCoreStates; std::vector beforeSocketState, afterSocketState; uint64 BeforeTime = 0, AfterTime = 0; cerr << dec << "\n"; cerr.precision(2); cerr << fixed; cout << dec << "\n"; cout.precision(2); cout << fixed; cerr << "\nMC counter group: " << imc_profile << "\n"; cerr << "PCU counter group: " << pcu_profile << "\n"; if (pcu_profile == 0) { if (cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX_DE || cpu_model == PCM::SKX) cerr << "Your processor does not support frequency band statistics\n"; else cerr << "Freq bands [0/1/2]: " << freq_band[0] * 100 << " MHz; " << freq_band[1] * 100 << " MHz; " << freq_band[2] * 100 << " MHz; \n"; } if (sysCmd != NULL) cerr << "Update every " << delay << " seconds\n"; if ((sysCmd != NULL) && (delay <= 0.0)) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } if (delay <= 0.0) delay = PCM_DELAY_DEFAULT; uint32 i = 0; for (i = 0; i < numSockets; ++i) BeforeState[i] = m->getServerUncoreCounterState(i); m->getAllCounterStates(dummySystemState, beforeSocketState, dummyCoreStates, false); BeforeTime = m->getTickCount(); if (sysCmd != NULL) { MySystem(sysCmd, sysArgv); } auto getPowerLicenseResidency = [nCorePowerLicenses](const int32 license, const SocketCounterState & before, const SocketCounterState& after) { uint64 all = 0; for (int32 l = 0; l < nCorePowerLicenses; ++l) { all += getNumberOfCustomEvents(l, before, after); } assert(license < nCorePowerLicenses); if (all > 0) return 100.0 * double(getNumberOfCustomEvents(license, before, after)) / double(all); return -1.; }; const auto uncoreFreqFactor = double(m->getNumOnlineSockets()) / double(m->getNumOnlineCores()); mainLoop([&]() { cout << "----------------------------------------------------------------------------------------------\n"; if (!csv) cout << flush; const auto delay_ms = calibratedSleep(delay, sysCmd, mainLoop, m); AfterTime = m->getTickCount(); for (i = 0; i < numSockets; ++i) AfterState[i] = m->getServerUncoreCounterState(i); m->getAllCounterStates(dummySystemState, afterSocketState, dummyCoreStates, false); cout << "Time elapsed: " << AfterTime - BeforeTime << " ms\n"; cout << "Called sleep function for " << delay_ms << " ms\n"; for (uint32 socket = 0; socket < numSockets; ++socket) { if (nCorePowerLicenses) { cout << "S" << socket << "; " << "Uncore Freq: " << getAverageUncoreFrequency(BeforeState[socket], AfterState[socket]) * uncoreFreqFactor / 1e9 << " Ghz; " "Core Freq: " << getActiveAverageFrequency(beforeSocketState[socket], afterSocketState[socket]) / 1e9 << " Ghz; "; for (int32 l = 0; l < nCorePowerLicenses; ++l) { cout << "Core Power License " << std::to_string(l) << ": " << getPowerLicenseResidency(l, beforeSocketState[socket], afterSocketState[socket]) << "%; "; } cout << "\n"; } for (uint32 port = 0; port < m->getQPILinksPerSocket(); ++port) { cout << "S" << socket << "P" << port << "; " + std::string(m->xPI()) + " Clocks: " << getQPIClocks(port, BeforeState[socket], AfterState[socket]) << "; L0p Tx Cycles: " << 100. * getNormalizedQPIL0pTxCycles(port, BeforeState[socket], AfterState[socket]) << "%" << "; L1 Cycles: " << 100. * getNormalizedQPIL1Cycles(port, BeforeState[socket], AfterState[socket]) << "%" << "\n"; } for (uint32 channel = 0; channel < m->getMCChannelsPerSocket(); ++channel) { if (imc_profile <= 3 && imc_profile >= 0) { cout << "S" << socket << "CH" << channel << "; DRAMClocks: " << getDRAMClocks(channel, BeforeState[socket], AfterState[socket]) << "; Rank" << getFirstRank(imc_profile) << " CKE Off Residency: " << setw(3) << 100. * getCKEOffResidency(channel, getFirstRank(imc_profile), BeforeState[socket], AfterState[socket]) << "%" << "; Rank" << getFirstRank(imc_profile) << " CKE Off Average Cycles: " << getCKEOffAverageCycles(channel, getFirstRank(imc_profile), BeforeState[socket], AfterState[socket]) << "; Rank" << getFirstRank(imc_profile) << " Cycles per transition: " << getCyclesPerTransition(channel, getFirstRank(imc_profile), BeforeState[socket], AfterState[socket]) << "\n"; cout << "S" << socket << "CH" << channel << "; DRAMClocks: " << getDRAMClocks(channel, BeforeState[socket], AfterState[socket]) << "; Rank" << getSecondRank(imc_profile) << " CKE Off Residency: " << setw(3) << 100. * getCKEOffResidency(channel, getSecondRank(imc_profile), BeforeState[socket], AfterState[socket]) << "%" << "; Rank" << getSecondRank(imc_profile) << " CKE Off Average Cycles: " << getCKEOffAverageCycles(channel, getSecondRank(imc_profile), BeforeState[socket], AfterState[socket]) << "; Rank" << getSecondRank(imc_profile) << " Cycles per transition: " << getCyclesPerTransition(channel, getSecondRank(imc_profile), BeforeState[socket], AfterState[socket]) << "\n"; } else if (imc_profile == 4) { cout << "S" << socket << "CH" << channel << "; DRAMClocks: " << getDRAMClocks(channel, BeforeState[socket], AfterState[socket]) << "; Self-refresh cycles: " << getSelfRefreshCycles(channel, BeforeState[socket], AfterState[socket]) << "; Self-refresh transitions: " << getSelfRefreshTransitions(channel, BeforeState[socket], AfterState[socket]) << "; PPD cycles: " << getPPDCycles(channel, BeforeState[socket], AfterState[socket]) << "\n"; } } switch (pcu_profile) { case 0: if (cpu_model == PCM::HASWELLX || cpu_model == PCM::BDX_DE || cpu_model == PCM::SKX) break; cout << "S" << socket << "; PCUClocks: " << getPCUClocks(BeforeState[socket], AfterState[socket]) << "; Freq band 0/1/2 cycles: " << 100. * getNormalizedPCUCounter(1, BeforeState[socket], AfterState[socket]) << "%" << "; " << 100. * getNormalizedPCUCounter(2, BeforeState[socket], AfterState[socket]) << "%" << "; " << 100. * getNormalizedPCUCounter(3, BeforeState[socket], AfterState[socket]) << "%" << "\n"; break; case 1: cout << "S" << socket << "; PCUClocks: " << getPCUClocks(BeforeState[socket], AfterState[socket]) << ((cpu_model == PCM::SKX)?"; core C0_1/C3/C6_7-state residency: ":"; core C0/C3/C6-state residency: ") << getNormalizedPCUCounter(1, BeforeState[socket], AfterState[socket]) << "; " << getNormalizedPCUCounter(2, BeforeState[socket], AfterState[socket]) << "; " << getNormalizedPCUCounter(3, BeforeState[socket], AfterState[socket]) << "\n"; break; case 2: cout << "S" << socket << "; PCUClocks: " << getPCUClocks(BeforeState[socket], AfterState[socket]) << "; Internal prochot cycles: " << getNormalizedPCUCounter(1, BeforeState[socket], AfterState[socket]) * 100. << " %" << "; External prochot cycles:" << getNormalizedPCUCounter(2, BeforeState[socket], AfterState[socket]) * 100. << " %" << "; Thermal freq limit cycles:" << getNormalizedPCUCounter(3, BeforeState[socket], AfterState[socket]) * 100. << " %" << "\n"; break; case 3: cout << "S" << socket << "; PCUClocks: " << getPCUClocks(BeforeState[socket], AfterState[socket]) << "; Thermal freq limit cycles: " << getNormalizedPCUCounter(1, BeforeState[socket], AfterState[socket]) * 100. << " %" << "; Power freq limit cycles:" << getNormalizedPCUCounter(2, BeforeState[socket], AfterState[socket]) * 100. << " %"; if(cpu_model != PCM::SKX && cpu_model != PCM::ICX && cpu_model != PCM::SNOWRIDGE && cpu_model != PCM::SPR) cout << "; Clipped freq limit cycles:" << getNormalizedPCUCounter(3, BeforeState[socket], AfterState[socket]) * 100. << " %"; cout << "\n"; break; case 4: if (cpu_model == PCM::SKX || cpu_model == PCM::ICX || cpu_model == PCM::SNOWRIDGE || cpu_model == PCM::SPR) { cout << "This PCU profile is not supported on your processor\n"; break; } cout << "S" << socket << "; PCUClocks: " << getPCUClocks(BeforeState[socket], AfterState[socket]) << "; OS freq limit cycles: " << getNormalizedPCUCounter(1, BeforeState[socket], AfterState[socket]) * 100. << " %" << "; Power freq limit cycles:" << getNormalizedPCUCounter(2, BeforeState[socket], AfterState[socket]) * 100. << " %" << "; Clipped freq limit cycles:" << getNormalizedPCUCounter(3, BeforeState[socket], AfterState[socket]) * 100. << " %" << "\n"; break; case 5: cout << "S" << socket << "; PCUClocks: " << getPCUClocks(BeforeState[socket], AfterState[socket]) << "; Frequency transition count: " << getPCUCounter(1, BeforeState[socket], AfterState[socket]) << " " << "; Cycles spent changing frequency: " << getNormalizedPCUCounter(2, BeforeState[socket], AfterState[socket], m) * 100. << " %"; if (PCM::HASWELLX == cpu_model) { cout << "; UFS transition count: " << getPCUCounter(3, BeforeState[socket], AfterState[socket]) << " "; cout << "; UFS transition cycles: " << getNormalizedPCUCounter(0, BeforeState[socket], AfterState[socket], m) * 100. << " %"; } cout << "\n"; break; case 6: cout << "S" << socket; if (cpu_model == PCM::HASWELLX || PCM::BDX_DE == cpu_model) cout << "; PC1e+ residency: " << getNormalizedPCUCounter(0, BeforeState[socket], AfterState[socket], m) * 100. << " %" "; PC1e+ transition count: " << getPCUCounter(1, BeforeState[socket], AfterState[socket]) << " "; switch (cpu_model) { case PCM::IVYTOWN: case PCM::HASWELLX: case PCM::BDX_DE: case PCM::SKX: case PCM::ICX: case PCM::SNOWRIDGE: case PCM::SPR: cout << "; PC2 residency: " << getPackageCStateResidency(2, BeforeState[socket], AfterState[socket]) * 100. << " %"; cout << "; PC2 transitions: " << getPCUCounter(2, BeforeState[socket], AfterState[socket]) << " "; cout << "; PC3 residency: " << getPackageCStateResidency(3, BeforeState[socket], AfterState[socket]) * 100. << " %"; cout << "; PC6 residency: " << getPackageCStateResidency(6, BeforeState[socket], AfterState[socket]) * 100. << " %"; cout << "; PC6 transitions: " << getPCUCounter(3, BeforeState[socket], AfterState[socket]) << " "; break; } cout << "\n"; break; case 7: if (PCM::HASWELLX == cpu_model || PCM::BDX_DE == cpu_model || PCM::BDX == cpu_model) { cout << "S" << socket << "; UFS_TRANSITIONS_PERF_P_LIMIT: " << getNormalizedPCUCounter(0, BeforeState[socket], AfterState[socket], m) * 100. << " %" << "; UFS_TRANSITIONS_IO_P_LIMIT: " << getNormalizedPCUCounter(1, BeforeState[socket], AfterState[socket], m) * 100. << " %" << "; UFS_TRANSITIONS_UP_RING_TRAFFIC: " << getNormalizedPCUCounter(2, BeforeState[socket], AfterState[socket], m) * 100. << " %" << "; UFS_TRANSITIONS_UP_STALL_CYCLES: " << getNormalizedPCUCounter(3, BeforeState[socket], AfterState[socket], m) * 100. << " %" << "\n"; } break; case 8: if (PCM::HASWELLX == cpu_model || PCM::BDX_DE == cpu_model || PCM::BDX == cpu_model) { cout << "S" << socket << "; UFS_TRANSITIONS_DOWN: " << getNormalizedPCUCounter(0, BeforeState[socket], AfterState[socket], m) * 100. << " %" << "\n"; } break; } cout << "S" << socket << "; Consumed energy units: " << getConsumedEnergy(BeforeState[socket], AfterState[socket]) << "; Consumed Joules: " << getConsumedJoules(BeforeState[socket], AfterState[socket]) << "; Watts: " << 1000. * getConsumedJoules(BeforeState[socket], AfterState[socket]) / double(AfterTime - BeforeTime) << "; Thermal headroom below TjMax: " << AfterState[socket].getPackageThermalHeadroom() << "\n"; cout << "S" << socket << "; Consumed DRAM energy units: " << getDRAMConsumedEnergy(BeforeState[socket], AfterState[socket]) << "; Consumed DRAM Joules: " << getDRAMConsumedJoules(BeforeState[socket], AfterState[socket]) << "; DRAM Watts: " << 1000. * getDRAMConsumedJoules(BeforeState[socket], AfterState[socket]) / double(AfterTime - BeforeTime) << "\n"; } swap(BeforeState, AfterState); swap(BeforeTime, AfterTime); swap(beforeSocketState, afterSocketState); if (m->isBlocked()) { cout << "----------------------------------------------------------------------------------------------\n"; // in case PCM was blocked after spawning child application: break monitoring loop here return false; } return true; }); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-raw.cpp000066400000000000000000003134211445420033100152310ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2023, Intel Corporation /*! \file pcm-raw.cpp \brief Example of using CPU counters: implements a performance counter monitoring utility with raw events interface */ #include #ifdef _MSC_VER #define strtok_r strtok_s #include #include "windows/windriver.h" #else #include #include #include // for gettimeofday() #endif #include #include #include #include #include #include #include #include #include #include #include "cpucounters.h" #include "utils.h" #if PCM_SIMDJSON_AVAILABLE #include "simdjson.h" #endif #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs #define MAX_CORES 4096 using namespace std; using namespace pcm; void print_usage(const string & progname) { cout << "\n Usage: \n " << progname << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -e event1 [-e event2] [-e event3] .. => list of custom events to monitor\n"; cout << " -pid PID | /pid PID => collect core metrics only for specified process ID\n"; cout << " -r | --reset | /reset => reset PMU configuration (at your own risk)\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; cout << " -json[=file.json] | /json[=file.json] => output json format to screen or\n" << " to a file, in case filename is provided\n"; cout << " -out filename | /out filename => write all output (stdout and stderr) to specified file\n"; cout << " event description example: -e core/config=0x30203,name=LD_BLOCKS.STORE_FORWARD/ -e core/fixed,config=0x333/ \n"; cout << " -e cha/config=0,name=UNC_CHA_CLOCKTICKS/ -e imc/fixed,name=DRAM_CLOCKS/\n"; #ifdef PCM_SIMDJSON_AVAILABLE cout << " -e NAME where the NAME is an event from https://github.com/intel/perfmon event lists\n"; cout << " -ep path | /ep path => path to event list directory (default is the current directory)\n"; #endif cout << " -yc | --yescores | /yc => enable specific cores to output\n"; cout << " -f | /f => enforce flushing each line for interactive output\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; cout << " -tr | /tr => transpose output (print single event data in a row)\n"; cout << " -ext | /ext => add headers to transposed output and extend printout to match it\n"; cout << " -single-header | /single-header => headers for transposed output are merged into single header\n"; cout << " -s | /s => print a sample separator line between samples in transposed output\n"; cout << " -v | /v => verbose mode (print additional diagnostic messages)\n"; cout << " -l => use locale for printing values, calls -tab for readability\n"; cout << " -tab => replace default comma separator with tab\n"; cout << " -el event_list.txt | /el event_list.txt => read event list from event_list.txt file, \n"; cout << " each line represents an event,\n"; cout << " event groups are separated by a semicolon\n"; cout << " -edp | /edp => 'edp' output mode\n"; print_help_force_rtm_abort_mode(41); cout << " Examples:\n"; cout << " " << progname << " 1 => print counters every second without core and socket output\n"; cout << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << progname << " /csv 5 2>/dev/null => one sample every 5 seconds, and discard all diagnostic output\n"; cout << "\n"; } bool verbose = false; double defaultDelay = 1.0; // in seconds PCM::RawEventConfig initCoreConfig() { return PCM::RawEventConfig{ {0,0,0, PCM::ExtendedCustomCoreEventDescription::invalidMsrValue(),PCM::ExtendedCustomCoreEventDescription::invalidMsrValue(), 0 }, "" }; } void printEvent(const std::string & pmuName, const bool fixed, const PCM::RawEventConfig & config) { cerr << "parsed " << (fixed ? "fixed " : "") << " " << pmuName << " event: \"" << hex << config.second << "\" : {" << hex << "0x" << config.first[0] << ", 0x" << config.first[1] << ", 0x" << config.first[2] << ", 0x" << config.first[3] << ", 0x" << config.first[4] << ", 0x" << config.first[5] << "}\n" << dec; } void lowerCase(std::string & str) { std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { #ifdef _MSC_VER return std::tolower(c, std::locale()); #else return std::tolower(c); // std::locale has some bad_cast issues in g++ #endif }); } enum AddEventStatus { OK, Failed, FailedTooManyEvents }; bool tooManyEvents(const std::string & pmuName, const int event_pos, const std::string& fullEventStr) { PCM* m = PCM::getInstance(); assert(m); const int maxCounters = (pmuName == "core" || pmuName == "atom") ? m->getMaxCustomCoreEvents() : ServerUncoreCounterState::maxCounters; if (event_pos >= maxCounters) { std::cerr << "ERROR: trying to add event " << fullEventStr << " at position " << event_pos << " of an event group, which exceeds the max num possible (" << maxCounters << ").\n"; return true; } return false; } #ifdef PCM_SIMDJSON_AVAILABLE using namespace simdjson; std::vector > JSONparsers; std::unordered_map PMUEventMapJSON; std::vector>> PMUEventMapsTSV; std::shared_ptr PMURegisterDeclarations; std::string eventFileLocationPrefix = "."; bool parse_tsv(const string &path) { bool col_names_parsed = false; int event_name_pos = -1; ifstream inFile; string line; inFile.open(path); std::unordered_map> PMUEventMap; while (getline(inFile, line)) { if (line.size() == 1 && line[0] == '\n') continue; // Trim whitespaces left/right // MOVE to utils auto ws_left_count = 0; for (size_t i = 0 ; i < line.size() ; i++) { if (line[i] == ' ') ws_left_count++; else break; } auto ws_right_count = 0; for (size_t i = line.size() - 1 ; i > 0 ; i--) { if (line[i] == ' ') ws_right_count++; else break; } line.erase(0, ws_left_count); line.erase(line.size() - ws_right_count, ws_right_count); if (line[0] == '#') continue; if (!col_names_parsed) { // Consider first row as Column name row std::vector col_names = split(line, '\t'); PMUEventMap["COL_NAMES"] = col_names; const auto event_name_it = std::find(col_names.begin(), col_names.end(), "EventName"); if (event_name_it == col_names.end()) { cerr << "ERROR: First row does not contain EventName\n"; inFile.close(); return false; } event_name_pos = (int)(event_name_it - col_names.begin()); col_names_parsed = true; continue; } std::vector entry = split(line, '\t'); std::string event_name = entry[event_name_pos]; PMUEventMap[event_name] = entry; } inFile.close(); PMUEventMapsTSV.push_back(PMUEventMap); return true; } bool initPMUEventMap() { static bool inited = false; if (inited == true) { return true; } inited = true; const auto mapfile = "mapfile.csv"; const auto mapfilePath = eventFileLocationPrefix + "/" + mapfile; std::ifstream in(mapfilePath); std::string line, item; if (!in.is_open()) { cerr << "ERROR: File " << mapfilePath << " can't be open. \n"; cerr << " Download it from https://raw.githubusercontent.com/intel/perfmon/main/" << mapfile << " \n"; return false; } int32 FMSPos = -1; int32 FilenamePos = -1; int32 EventTypetPos = -1; if (std::getline(in, line)) { auto header = split(line, ','); for (int32 i = 0; i < (int32)header.size(); ++i) { if (header[i] == "Family-model") { FMSPos = i; } else if (header[i] == "Filename") { FilenamePos = i; } else if (header[i] == "EventType") { EventTypetPos = i; } } } else { cerr << "Can't read first line from " << mapfile << " \n"; return false; } // cout << FMSPos << " " << FilenamePos << " " << EventTypetPos << "\n"; assert(FMSPos >= 0); assert(FilenamePos >= 0); assert(EventTypetPos >= 0); const std::string ourFMS = PCM::getInstance()->getCPUFamilyModelString(); // cout << "Our FMS: " << ourFMS << "\n"; std::multimap eventFiles; cerr << "Matched event files:\n"; while (std::getline(in, line)) { auto tokens = split(line, ','); assert(FMSPos < (int32)tokens.size()); assert(FilenamePos < (int32)tokens.size()); assert(EventTypetPos < (int32)tokens.size()); std::regex FMSRegex(tokens[FMSPos]); std::cmatch FMSMatch; if (std::regex_search(ourFMS.c_str(), FMSMatch, FMSRegex)) { cerr << tokens[FMSPos] << " " << tokens[EventTypetPos] << " " << tokens[FilenamePos] << "\n"; eventFiles.insert(std::make_pair(tokens[EventTypetPos], tokens[FilenamePos])); } } in.close(); if (eventFiles.empty()) { cerr << "ERROR: CPU " << ourFMS << " not found in " << mapfile << "\n"; return false; } for (const auto& evfile : eventFiles) { std::string path; auto printError = [&evfile]() { cerr << "Make sure you have downloaded " << evfile.second << " from https://raw.githubusercontent.com/intel/perfmon/main/" + evfile.second + " \n"; }; try { cerr << evfile.first << " " << evfile.second << "\n"; if (evfile.first == "core" || evfile.first == "uncore" || evfile.first == "uncore experimental") { const std::string path1 = eventFileLocationPrefix + evfile.second; const std::string path2 = eventFileLocationPrefix + evfile.second.substr(evfile.second.rfind('/')); if (std::ifstream(path1).good()) { path = path1; } else if (std::ifstream(path2).good()) { path = path2; } else { std::cerr << "ERROR: Can't open event file at location " << path1 << " or " << path2 << "\n"; printError(); return false; } if (path.find(".json") != std::string::npos) { JSONparsers.push_back(std::make_shared()); auto JSONObjects = JSONparsers.back()->load(path); if (JSONObjects["Header"].error() != NO_SUCH_FIELD) { JSONObjects = JSONObjects["Events"]; } for (simdjson::dom::object eventObj : JSONObjects) { // cout << "Event ----------------\n"; const std::string EventName{eventObj["EventName"].get_c_str()}; if (EventName.empty()) { cerr << "Did not find EventName in JSON object:\n"; for (const auto& keyValue : eventObj) { cout << "key: " << keyValue.key << " value: " << keyValue.value << "\n"; } } else { PMUEventMapJSON[EventName] = eventObj; } } } else if (path.find(".tsv") != std::string::npos) { if (!parse_tsv(path)) return false; } else { cerr << "ERROR: Could not determine Event file type (JSON/TSV)\n"; return false; } } } catch (std::exception& e) { cerr << "Error while opening and/or parsing " << path << " : " << e.what() << "\n"; printError(); return false; } } if (PMUEventMapJSON.empty() && PMUEventMapsTSV.empty()) { return false; } return true; } class EventMap { public: static bool isEvent(const std::string &eventStr) { if (PMUEventMapJSON.find(eventStr) != PMUEventMapJSON.end()) return true; for (const auto &EventMapTSV : PMUEventMapsTSV) { if (EventMapTSV.find(eventStr) != EventMapTSV.end()) return true; } return false; } static bool isField(const std::string &eventStr, const std::string event) { if (PMUEventMapJSON.find(eventStr) != PMUEventMapJSON.end()) { const auto eventObj = PMUEventMapJSON[eventStr]; const auto unitObj = eventObj[event]; return unitObj.error() != NO_SUCH_FIELD; } for (auto &EventMapTSV : PMUEventMapsTSV) { if (EventMapTSV.find(eventStr) != EventMapTSV.end()) { const auto &col_names = EventMapTSV["COL_NAMES"]; const auto event_name_it = std::find(col_names.begin(), col_names.end(), event); if (event_name_it != col_names.end()) { const size_t event_name_pos = event_name_it - col_names.begin(); return event_name_pos < EventMapTSV[eventStr].size(); } } } return false; } static std::string getField(const std::string &eventStr, const std::string &event) { std::string res; if (PMUEventMapJSON.find(eventStr) != PMUEventMapJSON.end()) { const auto eventObj = PMUEventMapJSON[eventStr]; const auto unitObj = eventObj[event]; return std::string(unitObj.get_c_str()); } for (auto &EventMapTSV : PMUEventMapsTSV) { if (EventMapTSV.find(eventStr) != EventMapTSV.end()) { const auto col_names = EventMapTSV["COL_NAMES"]; const auto event_name_it = std::find(col_names.begin(), col_names.end(), event); if (event_name_it != col_names.end()) { const auto event_name_pos = event_name_it - col_names.begin(); res = EventMapTSV[eventStr][event_name_pos]; } } } return res; } static void print_event(const std::string &eventStr) { if (PMUEventMapJSON.find(eventStr) != PMUEventMapJSON.end()) { const auto eventObj = PMUEventMapJSON[eventStr]; for (const auto & keyValue : eventObj) std::cout << keyValue.key << " : " << keyValue.value << "\n"; return; } for (auto &EventMapTSV : PMUEventMapsTSV) { if (EventMapTSV.find(eventStr) != EventMapTSV.end()) { const auto &col_names = EventMapTSV["COL_NAMES"]; const auto event = EventMapTSV[eventStr]; if (EventMapTSV.find(eventStr) != EventMapTSV.end()) { for (size_t i = 0 ; i < col_names.size() ; i++) std::cout << col_names[i] << " : " << event[i] << "\n"; return; } } } } }; AddEventStatus addEventFromDB(PCM::RawPMUConfigs& curPMUConfigs, string fullEventStr) { if (initPMUEventMap() == false) { cerr << "ERROR: PMU Event map can not be initialized\n"; return AddEventStatus::Failed; } // cerr << "Parsing event " << fullEventStr << "\n"; // cerr << "size: " << fullEventStr.size() << "\n"; while (fullEventStr.empty() == false && fullEventStr.back() == ' ') { fullEventStr.resize(fullEventStr.size() - 1); // remove trailing spaces } while (fullEventStr.empty() == false && fullEventStr.front() == ' ') { fullEventStr = fullEventStr.substr(1); // remove leading spaces } if (fullEventStr.empty()) { return AddEventStatus::OK; } const auto EventTokens = split(fullEventStr, ':'); assert(!EventTokens.empty()); auto mod = EventTokens.begin(); ++mod; const auto eventStr = EventTokens[0]; // cerr << "size: " << eventStr.size() << "\n"; PCM::RawEventConfig config = { {0,0,0,0,0}, "" }; std::string pmuName; auto unsupported = [&]() { cerr << "Unsupported event modifier: " << *mod << " in event " << fullEventStr << "\n"; }; if (eventStr == "MSR_EVENT") { while (mod != EventTokens.end()) { auto assignment = split(*mod, '='); for (auto& s : assignment) { lowerCase(s); } if (assignment.size() == 2 && assignment[0] == "msr") { config.first[PCM::MSREventPosition::index] = read_number(assignment[1].c_str()); } else if (assignment.size() == 2 && assignment[0] == "type") { if (assignment[1] == "static") { config.first[PCM::MSREventPosition::type] = PCM::MSRType::Static; } else if (assignment[1] == "freerun") { config.first[PCM::MSREventPosition::type] = PCM::MSRType::Freerun; } else { unsupported(); return AddEventStatus::Failed; } } else if (assignment.size() == 2 && assignment[0] == "scope") { if (assignment[1] == "package") { pmuName = "package_msr"; } else if (assignment[1] == "thread") { pmuName = "thread_msr"; } else { unsupported(); return AddEventStatus::Failed; } } else { unsupported(); return AddEventStatus::Failed; } ++mod; } if (pmuName.empty()) { cerr << "ERROR: scope is not defined in event " << fullEventStr << ". Possible values: package, thread\n"; return AddEventStatus::Failed; } config.second = fullEventStr; curPMUConfigs[pmuName].fixed.push_back(config); return AddEventStatus::OK; } if (!EventMap::isEvent(eventStr)) { cerr << "ERROR: event " << eventStr << " could not be found in event database. Ignoring the event.\n"; return AddEventStatus::OK; } bool fixed = false; static size_t offcoreEventIndex = 0; const std::string path = std::string("PMURegisterDeclarations/") + PCM::getInstance()->getCPUFamilyModelString() + ".json"; std::ifstream in(path); if (!in.is_open()) { const auto alt_path = std::string("/usr/share/pcm/") + path; in.open(alt_path); if (!in.is_open()) { const auto err_msg = std::string("event file ") + path + " or " + alt_path + " is not available."; throw std::invalid_argument(err_msg); } } in.close(); if (PMURegisterDeclarations.get() == nullptr) { // declaration not loaded yet try { JSONparsers.push_back(std::make_shared()); PMURegisterDeclarations = std::make_shared(); *PMURegisterDeclarations = JSONparsers.back()->load(path); } catch (std::exception& e) { cerr << "Error while opening and/or parsing " << path << " : " << e.what() << "\n"; return AddEventStatus::Failed; } } static std::map pmuNameMap = { {std::string("cbo"), std::string("cha")}, {std::string("upi"), std::string("xpi")}, {std::string("upi ll"), std::string("xpi")}, {std::string("qpi"), std::string("xpi")}, {std::string("qpi ll"), std::string("xpi")} }; if (!EventMap::isField(eventStr, "Unit")) { pmuName = "core"; config = initCoreConfig(); } else { std::string unit = EventMap::getField(eventStr, "Unit"); lowerCase(unit); // std::cout << eventStr << " is uncore event for unit " << unit << "\n"; pmuName = (pmuNameMap.find(unit) == pmuNameMap.end()) ? unit : pmuNameMap[unit]; } config.second = fullEventStr; if (1) { // cerr << "pmuName: " << pmuName << " full event "<< fullEventStr << " \n"; std::string CounterStr = EventMap::getField(eventStr, "Counter"); // cout << "Counter: " << CounterStr << "\n"; int fixedCounter = -1; fixed = (pcm_sscanf(CounterStr) >> s_expect("Fixed counter ") >> fixedCounter) ? true : false; if (!fixed){ bool counter_match=false; std::stringstream ss(CounterStr); // to get current event position int event_pos = curPMUConfigs[pmuName].programmable.size(); // loop through counter string and check if event pos matches any counter values for (int i = 0; ss >> i;) { if(event_pos == i) counter_match = true; if (ss.peek() == ',') ss.ignore(); } if(!counter_match) { std::cerr << "ERROR: position of " << fullEventStr << " event in the command is " << event_pos<<" but the supported counters are "<= config.first.size()) throw std::runtime_error("Config field value is out of bounds"); const auto width = uint64_t(fieldDescriptionObj["Width"]); assert(width <= 64); const uint64 mask = (width == 64) ? (~0ULL) : ((1ULL << width) - 1ULL); // 1 -> 1b, 2 -> 11b, 3 -> 111b config.first[cfg] &= ~(mask << position); // clear config.first[cfg] |= (value & mask) << position; }; auto PMUObj = (*PMURegisterDeclarations)[pmuName]; if (PMUObj.error() == NO_SUCH_FIELD) { cerr << "ERROR: PMU \"" << pmuName << "\" not found for event " << fullEventStr << " in " << path << ", ignoring the event.\n"; return AddEventStatus::OK; } simdjson::dom::object PMUDeclObj; if (fixed) { PMUDeclObj = (*PMURegisterDeclarations)[pmuName][std::string("fixed") + std::to_string(fixedCounter)].get_object(); } else { PMUDeclObj = (*PMURegisterDeclarations)[pmuName]["programmable"].get_object(); } auto& myPMUConfigs = fixed ? curPMUConfigs[pmuName].fixed : curPMUConfigs[pmuName].programmable; simdjson::dom::object MSRObject; auto setMSRValue = [&setConfig,&MSRObject,&config,&myPMUConfigs](const string & valueStr) { const auto value = read_number(valueStr.c_str()); const auto position = int64_t(MSRObject["Position"]); // update the first event setConfig(myPMUConfigs.empty() ? config : myPMUConfigs.front(), MSRObject, value, position); // update the current as well for display setConfig(config, MSRObject, value, position); }; for (const auto & registerKeyValue : PMUDeclObj) { // cout << "Setting " << registerKeyValue.key << " : " << registerKeyValue.value << "\n"; simdjson::dom::object fieldDescriptionObj = registerKeyValue.value; // cout << " config: " << uint64_t(fieldDescriptionObj["Config"]) << "\n"; // cout << " Position: " << uint64_t(fieldDescriptionObj["Position"]) << "\n"; const std::string fieldNameStr{ registerKeyValue.key.begin(), registerKeyValue.key.end() }; if (fieldNameStr == "MSRIndex") { string fieldValueStr = EventMap::getField(eventStr, fieldNameStr); // cout << "MSR field " << fieldNameStr << " value is " << fieldValueStr << " (" << read_number(fieldValueStr.c_str()) << ") offcore=" << offcore << "\n"; lowerCase(fieldValueStr); if (fieldValueStr == "0" || fieldValueStr == "0x00") { continue; } auto MSRIndexStr = fieldValueStr; if (offcore) { const auto MSRIndexes = split(MSRIndexStr, ','); if (offcoreEventIndex >= MSRIndexes.size()) { std::cerr << "ERROR: too many offcore events specified (max is " << MSRIndexes.size() << "). Ignoring " << fullEventStr << " event\n"; return AddEventStatus::OK; } MSRIndexStr = MSRIndexes[offcoreEventIndex]; } // cout << " MSR field " << fieldNameStr << " value is " << MSRIndexStr << " (" << read_number(MSRIndexStr.c_str()) << ") offcore=" << offcore << "\n"; MSRObject = registerKeyValue.value[MSRIndexStr]; const string msrValueStr = EventMap::getField(eventStr, "MSRValue"); setMSRValue(msrValueStr); continue; } const int64_t position = int64_t(fieldDescriptionObj["Position"]); if (position == -1) { continue; // field ignored } if (!EventMap::isField(eventStr, fieldNameStr)) { // cerr << fieldNameStr << " not found\n"; if (fieldDescriptionObj["DefaultValue"].error() == NO_SUCH_FIELD) { cerr << "ERROR: DefaultValue not provided for field \"" << fieldNameStr << "\" in " << path << "\n"; return AddEventStatus::Failed; } else { const auto cfg = uint64_t(fieldDescriptionObj["Config"]); if (cfg >= config.first.size()) throw std::runtime_error("Config field value is out of bounds"); config.first[cfg] |= uint64_t(fieldDescriptionObj["DefaultValue"]) << position; } } else { std::string fieldValueStr = EventMap::getField(eventStr, fieldNameStr); fieldValueStr.erase(std::remove(fieldValueStr.begin(), fieldValueStr.end(), '\"'), fieldValueStr.end()); if (offcore && fieldNameStr == "EventCode") { const auto offcoreCodes = split(fieldValueStr,','); if (offcoreEventIndex >= offcoreCodes.size()) { std::cerr << "ERROR: too many offcore events specified (max is " << offcoreCodes.size() << "). Ignoring " << fullEventStr << " event\n"; return AddEventStatus::OK; } fieldValueStr = offcoreCodes[offcoreEventIndex]; } // cout << " field " << fieldNameStr << " value is " << fieldValueStr << " (" << read_number(fieldValueStr.c_str()) << ") offcore=" << offcore << "\n"; setConfig(config, fieldDescriptionObj, read_number(fieldValueStr.c_str()), position); } } auto setField = [&PMUDeclObj, &config, &setConfig](const char* field, const uint64 value) { const auto pos = int64_t(PMUDeclObj[field]["Position"]); setConfig(config, PMUDeclObj[field], value, pos); }; std::regex CounterMaskRegex("c(0x[0-9a-fA-F]+|[[:digit:]]+)"); std::regex UmaskRegex("u(0x[0-9a-fA-F]+|[[:digit:]]+)"); std::regex EdgeDetectRegex("e(0x[0-9a-fA-F]+|[[:digit:]]+)"); while (mod != EventTokens.end()) { const auto assignment = split(*mod, '='); if (*mod == "SUP") { setField("User", 0); setField("OS", 1); } else if (*mod == "USER") { setField("User", 1); setField("OS", 0); } else if (*mod == "tx") { setField("InTX", 1); } else if (*mod == "cp") { setField("InTXCheckpointed", 1); } else if (*mod == "percore") { unsupported(); return AddEventStatus::OK; } else if (*mod == "perf_metrics") { setField("PerfMetrics", 1); } else if (std::regex_match(mod->c_str(), CounterMaskRegex)) { // Counter Mask modifier const std::string CounterMaskStr{ mod->begin() + 1, mod->end() }; setField("CounterMask", read_number(CounterMaskStr.c_str())); } else if (std::regex_match(mod->c_str(), EdgeDetectRegex)) { // Edge Detect modifier const std::string Str{ mod->begin() + 1, mod->end() }; setField("EdgeDetect", read_number(Str.c_str())); } else if (std::regex_match(mod->c_str(), UmaskRegex)) { // UMask modifier const std::string Str{ mod->begin() + 1, mod->end() }; setField("UMask", read_number(Str.c_str())); } else if (assignment.size() == 2 && assignment[0] == "request") { unsupported(); return AddEventStatus::OK; } else if (assignment.size() == 2 && assignment[0] == "response") { unsupported(); return AddEventStatus::OK; } else if (assignment.size() == 2 && assignment[0] == "filter0") { setField("Filter0", read_number(assignment[1].c_str())); } else if (assignment.size() == 2 && assignment[0] == "filter1") { setField("Filter1", read_number(assignment[1].c_str())); } else if (assignment.size() == 2 && assignment[0] == "t") { setField("Threshold", read_number(assignment[1].c_str())); } else if (assignment.size() == 2 && assignment[0] == "tid") { setField("TIDEnable", 1); setField("TID", read_number(assignment[1].c_str())); } else if (assignment.size() == 2 && assignment[0] == "umask_ext") { setField("UMaskExt", read_number(assignment[1].c_str())); } else if (assignment.size() == 2 && assignment[0] == "ocr_msr_val") { setMSRValue(assignment[1]); } else { unsupported(); return AddEventStatus::Failed; } ++mod; } if (offcore) { ++offcoreEventIndex; } myPMUConfigs.push_back(config); } catch (std::exception& e) { cerr << "Error while setting a register field for event " << fullEventStr << " : " << e.what() << "\n"; EventMap::print_event(eventStr); return AddEventStatus::Failed; } } /* for (const auto& keyValue : eventObj) { cout << keyValue.key << " : " << keyValue.value << "\n"; } */ printEvent(pmuName, fixed, config); return AddEventStatus::OK; } #endif AddEventStatus addEvent(PCM::RawPMUConfigs & curPMUConfigs, string eventStr) { if (eventStr.empty()) { return AddEventStatus::OK; } #ifdef PCM_SIMDJSON_AVAILABLE if (eventStr.find('/') == string::npos) { return addEventFromDB(curPMUConfigs, eventStr); } #endif PCM::RawEventConfig config = { {0,0,0,0,0}, "" }; const auto typeConfig = split(eventStr, '/'); if (typeConfig.size() < 2) { #ifndef PCM_SIMDJSON_AVAILABLE cerr << "WARNING: pcm-raw is compiled without simdjson library (check cmake output). Collecting events by names from json event lists is not supported.\n"; #endif cerr << "ERROR: wrong syntax in event description \"" << eventStr << "\"\n"; return AddEventStatus::Failed; } auto pmuName = typeConfig[0]; if (pmuName.empty()) { pmuName = "core"; } const auto configStr = typeConfig[1]; if (configStr.empty()) { cerr << "ERROR: empty config description in event description \"" << eventStr << "\"\n"; return AddEventStatus::Failed; } if (pmuName == "core" || pmuName == "atom") { config = initCoreConfig(); } const auto configArray = split(configStr, ','); bool fixed = false; for (const auto & item : configArray) { if (match(item, "config=", &config.first[0])) { // matched and initialized config 0 } else if (match(item, "config1=", &config.first[1])) { // matched and initialized config 1 } else if (match(item, "config2=", &config.first[2])) { // matched and initialized config 2 } else if (match(item, "config3=", &config.first[3])) { // matched and initialized config 3 } else if (match(item, "config4=", &config.first[4])) { // matched and initialized config 4 } else if (match(item, "config5=", &config.first[5])) { // matched and initialized config 5 } else if (match(item, "width=", &config.first[PCM::PCICFGEventPosition::width])) { // matched and initialized config 5 (width) } else if (pcm_sscanf(item) >> s_expect("name=") >> setw(255) >> config.second) { // matched and initialized name if (check_for_injections(config.second)) return AddEventStatus::Failed; } else if (item == "fixed") { fixed = true; } else { cerr << "ERROR: unknown token " << item << " in event description \"" << eventStr << "\"\n"; return AddEventStatus::Failed; } } printEvent(pmuName, fixed, config); if (fixed == false && tooManyEvents(pmuName, curPMUConfigs[pmuName].programmable.size(), eventStr)) { return AddEventStatus::FailedTooManyEvents; } if (fixed) curPMUConfigs[pmuName].fixed.push_back(config); else curPMUConfigs[pmuName].programmable.push_back(config); return AddEventStatus::OK; } bool addEvents(std::vector& PMUConfigs, string fn) { std::ifstream in(fn); std::string line, item; if (!in.is_open()) { cerr << "ERROR: File " << fn << " can't be open. \n"; return false; } PCM::RawPMUConfigs curConfig; auto doFinishGroup = [&curConfig, &PMUConfigs]() { if (!curConfig.empty()) { cerr << "Adding new group \n"; PMUConfigs.push_back(curConfig); curConfig.clear(); } }; while (std::getline(in, line)) { if (line.empty() || line[0] == '#') { continue; } const auto last = line[line.size() - 1]; bool finishGroup = false; if (last == ',') { line.resize(line.size() - 1); } else if (last == ';') { line.resize(line.size() - 1); finishGroup = true; } const auto status = addEvent(curConfig, line); switch (status) { case AddEventStatus::Failed: return false; case AddEventStatus::FailedTooManyEvents: cerr << "Failed to add event due to a too large group. Trying to split the event group.\n"; doFinishGroup(); if (addEvent(curConfig, line) != AddEventStatus::OK) { return false; } break; case AddEventStatus::OK: // all is fine break; } if (finishGroup) { doFinishGroup(); } } in.close(); doFinishGroup(); return true; } bool show_partial_core_output = false; bitset ycores; bool flushLine = false; bool transpose = false; bool extendPrintout = false; bool singleHeader = false; std::string separator = ","; const std::string jsonSeparator = "\":"; bool sampleSeparator = false; bool outputToJson = false; struct PrintOffset { const std::string entry; int start; int end; }; std::vector printOffsets; std::vector printedBlocks; int getPrintOffsetIdx(const std::string &value) { for (size_t i = 0 ; i < printOffsets.size() ; i++) { if (printOffsets[i].entry == value) return (int)i; } return -1; } void printNewLine(const CsvOutputType outputType) { if (outputType == Data) cout << "\n"; else if (outputType == Json) cout << "}\n"; } void printRowBeginCSV(const std::string & EventName, const CoreCounterState & BeforeState, const CoreCounterState & AfterState, PCM* m) { printDateForCSV(CsvOutputType::Data, separator); cout << EventName << separator << (1000ULL * getInvariantTSC(BeforeState, AfterState)) / m->getNominalFrequency() << separator << getInvariantTSC(BeforeState, AfterState); } void printRowBeginJson(const std::string & EventName, const CoreCounterState & BeforeState, const CoreCounterState & AfterState, PCM* m) { cout << "{\""; printDateForJson(separator, jsonSeparator); cout << "Event" << jsonSeparator << "\"" << EventName << "\"" << separator << "ms" << jsonSeparator << (1000ULL * getInvariantTSC(BeforeState, AfterState)) / m->getNominalFrequency() << separator << "InvariantTSC" << jsonSeparator << getInvariantTSC(BeforeState, AfterState); } void printRowBegin(const std::string & EventName, const CoreCounterState & BeforeState, const CoreCounterState & AfterState, PCM* m, const CsvOutputType outputType, PrintOffset& printOffset) { if (outputType == Data) { printRowBeginCSV(EventName, BeforeState, AfterState, m); for (int i = 0 ; i < printOffset.start ; i++) std::cout << separator; } else if (outputType == Json) { printRowBeginJson(EventName, BeforeState, AfterState, m); } } template void printRow(const std::string & EventName, MetricFunc metricFunc, const std::vector& BeforeState, const std::vector& AfterState, PCM* m, const CsvOutputType outputType, PrintOffset& printOffset, const pcm::TopologyEntry::CoreType & coreType, const std::string & pmuType) { printRowBegin(EventName, BeforeState[0], AfterState[0], m, outputType, printOffset); for (uint32 core = 0; core < m->getNumCores(); ++core) { if (extendPrintout && m->isHybrid() && m->getCoreType(core) != coreType) { continue; } if (!(show_partial_core_output && ycores.test(core) == false)) { if (outputType == Header1) { cout << separator << "SKT" << m->getSocketId(core) << "CORE" << core; printOffset.end++; } else if (outputType == Header2) cout << separator << pmuType; else if (outputType == Data) { cout << separator; if (m->isHybrid() == false || m->getCoreType(core) == coreType) { cout << metricFunc(BeforeState[core], AfterState[core]); } } else if (outputType == Header21) { cout << separator << pmuType << "_SKT" << m->getSocketId(core) << "_CORE" << core; printOffset.end++; } else if (outputType == Json) { cout << separator << pmuType << "_SKT" << m->getSocketId(core) << "_CORE" << core << jsonSeparator; if (m->isHybrid() == false || m->getCoreType(core) == coreType) { cout << metricFunc(BeforeState[core], AfterState[core]); } } else assert(!"unknown output type"); } } printNewLine(outputType); }; typedef uint64 (*UncoreMetricFunc)(const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after); typedef uint64(*UncoreFixedMetricFunc)(const uint32 u, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after); uint64 nullFixedMetricFunc(const uint32, const ServerUncoreCounterState&, const ServerUncoreCounterState&) { return ~0ULL; } const char* fixedCoreEventNames[] = { "InstructionsRetired" , "Cycles", "RefCycles", "TopDownSlots" }; const char* topdownEventNames[] = { "PERF_METRICS.FRONTEND_BOUND" , "PERF_METRICS.BAD_SPECULATION", "PERF_METRICS.BACKEND_BOUND", "PERF_METRICS.RETIRING" }; constexpr uint32 PerfMetricsConfig = 2; constexpr uint64 PerfMetricsMask = 1ULL; constexpr uint64 maxPerfMetricsValue = 255ULL; const char * getTypeString(uint64 typeID) { switch (typeID) { case PCM::MSRType::Freerun: return "freerun"; case PCM::MSRType::Static: return "static"; } return "unknownType"; } std::string getMSREventString(const uint64 & index, const std::string & type, const PCM::MSRType & msrType) { std::stringstream c; c << type << ":0x" << std::hex << index << ":" << getTypeString(msrType); return c.str(); } std::string getPCICFGEventString(const PCM::RawEventEncoding & eventEnc, const std::string& type) { std::stringstream c; c << type << ":0x" << std::hex << eventEnc[PCM::PCICFGEventPosition::deviceID] << ":0x" << eventEnc[PCM::PCICFGEventPosition::offset] << ":0x" << eventEnc[PCM::PCICFGEventPosition::width] << ":" << getTypeString(eventEnc[PCM::PCICFGEventPosition::type]); return c.str(); } enum MSRScope { Thread, Package }; void printTransposed(const PCM::RawPMUConfigs& curPMUConfigs, PCM* m, SystemCounterState& SysBeforeState, SystemCounterState& SysAfterState, vector& BeforeState, vector& AfterState, vector& BeforeUncoreState, vector& AfterUncoreState, vector& BeforeSocketState, vector& AfterSocketState, const CsvOutputType outputType, const bool& isLastGroup) { const bool is_header = (outputType == Header1 || outputType == Header2 || outputType == Header21); for (const auto & typeEvents : curPMUConfigs) { bool is_header_printed = false; const auto& type = typeEvents.first; const auto& events = typeEvents.second.programmable; const auto& fixedEvents = typeEvents.second.fixed; PrintOffset printOffset{type, 0, 0}; const auto print_idx = getPrintOffsetIdx(type); if (outputType == Header1 || outputType == Header21) { if (print_idx != -1) continue; // header already printed else { printOffset.start = (printOffsets.empty()) ? 0 : printOffsets.back().end; printOffset.end = printOffset.start; } } else if (outputType == Header2) { if (std::find(printedBlocks.begin(), printedBlocks.end(), type) != printedBlocks.end()) continue; printedBlocks.push_back(type); } else if (outputType == Data) { assert(printOffsets.empty() || print_idx >= 0); printOffset.start = (printOffsets.empty()) ? 0 : printOffsets[print_idx].start; printOffset.end = (printOffsets.empty()) ? 0 : printOffsets[print_idx].end; } auto printUncoreRows = [&](UncoreMetricFunc metricFunc, const uint32 maxUnit, const std::string &miscName = std::string(""), UncoreFixedMetricFunc fixedMetricFunc = nullFixedMetricFunc) { if (fixedEvents.size()) { printRowBegin(miscName, BeforeState[0], AfterState[0], m, outputType, printOffset); for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 u = 0; u < maxUnit; ++u) { if (outputType == Header1) { cout << separator << "SKT" << s << miscName << u; printOffset.end++; } else if (outputType == Header2) cout << separator << miscName ; else if (outputType == Data) cout << separator << fixedMetricFunc(u, BeforeUncoreState[s], AfterUncoreState[s]); else if (outputType == Header21) { cout << separator << type << "_SKT" << s << "_" << miscName << u; printOffset.end++; } else if (outputType == Json) { cout << separator << type << "_SKT" << s << "_" << miscName << u << jsonSeparator << fixedMetricFunc(u, BeforeUncoreState[s], AfterUncoreState[s]); } else assert(!"unknown output type"); } } if (is_header) is_header_printed = true; printNewLine(outputType); } uint32 i = 0; for (auto& event : events) { const std::string name = (event.second.empty()) ? (type + "Event" + std::to_string(i)) : event.second; if (is_header && is_header_printed) break; printRowBegin(name, BeforeState[0], AfterState[0], m, outputType, printOffset); for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 u = 0; u < maxUnit; ++u) { if (outputType == Header1) { cout << separator << "SKT_" << s << miscName << u; printOffset.end++; } else if (outputType == Header2) { cout << separator << miscName ; } else if (outputType == Data) { assert(metricFunc); cout << separator << metricFunc(u, i, BeforeUncoreState[s], AfterUncoreState[s]); } else if (outputType == Header21) { cout << separator << type << "_SKT" << s << "_" << miscName << u; printOffset.end++; } else if (outputType == Json) { assert(metricFunc); cout << separator << type << "_SKT" << s << "_" << miscName << u << jsonSeparator << metricFunc(u, i, BeforeUncoreState[s], AfterUncoreState[s]); } else { assert(!"unknown output type"); } } } if (is_header) is_header_printed = true; ++i; printNewLine(outputType); } }; auto printMSRRows = [&](const MSRScope& scope) { auto printMSR = [&](const PCM::RawEventConfig& event) -> bool { const auto index = event.first[PCM::MSREventPosition::index]; const auto msrType = (PCM::MSRType)event.first[PCM::MSREventPosition::type]; const std::string name = (event.second.empty()) ? getMSREventString(index, type, msrType) : event.second; if (is_header && is_header_printed) return false; printRowBegin(name, BeforeState[0], AfterState[0], m, outputType, printOffset); switch (scope) { case MSRScope::Package: for (uint32 s = 0; s < m->getNumSockets(); ++s) { if (outputType == Header1) { cout << separator << "SKT" << s ; printOffset.end++; } else if (outputType == Header2) { cout << separator << type ; } else if (outputType == Data) { cout << separator << getMSREvent(index, msrType, BeforeSocketState[s], AfterSocketState[s]); } else if (outputType == Header21) { cout << separator << type << "_SKT" << s ; printOffset.end++; } else if (outputType == Json) { cout << separator << type << "_SKT" << s << jsonSeparator << getMSREvent(index, msrType, BeforeSocketState[s], AfterSocketState[s]); } else { assert(!"unknown output type"); } } break; case MSRScope::Thread: for (uint32 core = 0; core < m->getNumCores(); ++core) { if (outputType == Header1) { cout << separator << "SKT" << m->getSocketId(core) << "CORE" << core; printOffset.end++; } else if (outputType == Header2) { cout << separator << type ; } else if (outputType == Data) { cout << separator << getMSREvent(index, msrType, BeforeState[core], AfterState[core]); } else if (outputType == Header21) { cout << separator << type << "_SKT" << m->getSocketId(core) << "_CORE" << core; printOffset.end++; } else if (outputType == Json) { cout << separator << type << "_SKT" << m->getSocketId(core) << "_CORE" << core << jsonSeparator << getMSREvent(index, msrType, BeforeState[core], AfterState[core]); } else { assert(!"unknown output type"); } } break; } if (is_header) is_header_printed = true; printNewLine(outputType); return true; }; for (const auto& event : events) { if (!printMSR(event)) { break; } } for (const auto& event : fixedEvents) { if (!printMSR(event)) { break; } } }; auto printCores = [&](const pcm::TopologyEntry::CoreType & coreType) { typedef uint64(*FuncType) (const CoreCounterState& before, const CoreCounterState& after); static FuncType funcFixed[] = { [](const CoreCounterState& before, const CoreCounterState& after) { return getInstructionsRetired(before, after); }, [](const CoreCounterState& before, const CoreCounterState& after) { return getCycles(before, after); }, [](const CoreCounterState& before, const CoreCounterState& after) { return getRefCycles(before, after); }, [](const CoreCounterState& before, const CoreCounterState& after) { return getAllSlotsRaw(before, after); } }; static FuncType funcTopDown[] = { [](const CoreCounterState& before, const CoreCounterState& after) { return uint64(getFrontendBound(before, after) * maxPerfMetricsValue); }, [](const CoreCounterState& before, const CoreCounterState& after) { return uint64(getBadSpeculation(before, after) * maxPerfMetricsValue); }, [](const CoreCounterState& before, const CoreCounterState& after) { return uint64(getBackendBound(before, after) * maxPerfMetricsValue); }, [](const CoreCounterState& before, const CoreCounterState& after) { return uint64(getRetiring(before, after) * maxPerfMetricsValue); } }; for (const auto& event : fixedEvents) { for (uint32 cnt = 0; cnt < 4; ++cnt) { if (extract_bits(event.first[0], 4U * cnt, 1U + 4U * cnt)) { if (is_header && is_header_printed) break; printRow(event.second.empty() ? fixedCoreEventNames[cnt] : event.second, funcFixed[cnt], BeforeState, AfterState, m, outputType, printOffset, coreType, type); if (is_header) is_header_printed = true; if (cnt == 3 && (event.first[PerfMetricsConfig] & PerfMetricsMask)) { for (uint32 t = 0; t < 4; ++t) { printRow(topdownEventNames[t], funcTopDown[t], BeforeState, AfterState, m, outputType, printOffset, coreType, type); } } } } } uint32 i = 0; for (const auto& event : events) { if (is_header && is_header_printed) break; const std::string name = (event.second.empty()) ? (type + "Event" + std::to_string(i)) : event.second; printRow(name, [&i](const CoreCounterState& before, const CoreCounterState& after) { return getNumberOfCustomEvents(i, before, after); }, BeforeState, AfterState, m, outputType, printOffset, coreType, type); ++i; if (is_header) is_header_printed = true; } }; if (type == "core") { printCores(pcm::TopologyEntry::Core); } else if (type == "atom") { printCores(pcm::TopologyEntry::Atom); } else if (type == "thread_msr") { printMSRRows(MSRScope::Thread); } else if (type == "package_msr") { printMSRRows(MSRScope::Package); } else if (type == "pcicfg") { auto printRegister = [&](const PCM::RawEventConfig& event) -> bool { const std::string name = (event.second.empty()) ? getPCICFGEventString(event.first, type) : event.second; const auto values = getPCICFGEvent(event.first, SysBeforeState, SysAfterState); if (is_header && is_header_printed) return false; printRowBegin(name, BeforeState[0], AfterState[0], m, outputType, printOffset); for (size_t r = 0; r < values.size(); ++r) { if (outputType == Header1) { cout << separator << "SYSTEM_" << r; printOffset.end++; } else if (outputType == Header2) { cout << separator << type; } else if (outputType == Data) { cout << separator << values[r]; } else if (outputType == Header21) { cout << separator << type << "_SYSTEM_" << r; printOffset.end++; } else if (outputType == Json) { cout << separator << type << "_SYSTEM_" << r << jsonSeparator << values[r]; } else { assert(!"unknown output type"); } } if (is_header) is_header_printed = true; printNewLine(outputType); return true; }; for (const auto& event : events) { if (!printRegister(event)) { break; } } for (const auto& event : fixedEvents) { if (!printRegister(event)) { break; } } } else if (type == "m3upi") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getQPILinksPerSocket(), "LINK"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getQPILinksPerSocket(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getM3UPICounter(u, i, before, after); }, (uint32) m->getQPILinksPerSocket(), "LINK"); }); } else if (type == "xpi" || type == "upi" || type == "qpi") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getQPILinksPerSocket(), "LINK"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getQPILinksPerSocket(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getXPICounter(u, i, before, after); }, (uint32) m->getQPILinksPerSocket(), "LINK"); }); } else if (type == "imc") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getMCChannelsPerSocket(), "CHAN"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getMCChannelsPerSocket(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getMCCounter(u, i, before, after); }, (uint32)m->getMCChannelsPerSocket(), "DRAMClocks", [](const uint32 u, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getDRAMClocks(u, before, after); }); }); } else if (type == "m2m") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getMCPerSocket(), "MC"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getMCPerSocket(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getM2MCounter(u, i, before, after); }, (uint32)m->getMCPerSocket(), "MC"); }); } else if (type == "pcu") { choose(outputType, [&]() { printUncoreRows(nullptr, 1U, ""); }, [&]() { printUncoreRows(nullptr, 1U, type); }, [&]() { printUncoreRows([](const uint32, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getPCUCounter(i, before, after); }, 1U, ""); }); } else if (type == "ubox") { choose(outputType, [&]() { printUncoreRows(nullptr, 1U, ""); }, [&]() { printUncoreRows(nullptr, 1U, type); }, [&]() { printUncoreRows([](const uint32, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getUBOXCounter(i, before, after); }, 1U, "UncoreClocks", [](const uint32, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getUncoreClocks(before, after); }); }); } else if (type == "cbo" || type == "cha") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfCBoxes(), "C"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfCBoxes(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getCBOCounter(u, i, before, after); }, (uint32)m->getMaxNumOfCBoxes(), "C"); }); } else if (type == "mdf") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfMDFs(), "MDF"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfMDFs(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getMDFCounter(u, i, before, after); }, (uint32)m->getMaxNumOfMDFs(), "MDF"); }); } else if (type == "irp") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfIIOStacks(), "IRP"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfIIOStacks(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getIRPCounter(u, i, before, after); }, (uint32)m->getMaxNumOfIIOStacks(), "IRP"); }); } else if (type == "iio") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfIIOStacks(), "IIO"); }, [&]() { printUncoreRows(nullptr, (uint32) m->getMaxNumOfIIOStacks(), type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getIIOCounter(u, i, before, after); }, (uint32)m->getMaxNumOfIIOStacks(), "IIO"); }); } else if (type == "cxlcm") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) ServerUncoreCounterState::maxCXLPorts, "CXLCM"); }, [&]() { printUncoreRows(nullptr, (uint32) ServerUncoreCounterState::maxCXLPorts, type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getCXLCMCounter(u, i, before, after); }, ServerUncoreCounterState::maxCXLPorts, "CXLCM"); }); } else if (type == "cxldp") { choose(outputType, [&]() { printUncoreRows(nullptr, (uint32) ServerUncoreCounterState::maxCXLPorts, "CXLDP"); }, [&]() { printUncoreRows(nullptr, (uint32) ServerUncoreCounterState::maxCXLPorts, type); }, [&]() { printUncoreRows([](const uint32 u, const uint32 i, const ServerUncoreCounterState& before, const ServerUncoreCounterState& after) { return getCXLDPCounter(u, i, before, after); }, ServerUncoreCounterState::maxCXLPorts, "CXLDP"); }); } else { std::cerr << "ERROR: unrecognized PMU type \"" << type << "\"\n"; } if (outputType == Header1 || outputType == Header21) printOffsets.push_back(printOffset); } if (sampleSeparator) { cout << (isLastGroup? "==========\n" : "----------\n"); } if (flushLine) { cout.flush(); } } void print(const PCM::RawPMUConfigs& curPMUConfigs, PCM* m, SystemCounterState& SysBeforeState, SystemCounterState& SysAfterState, vector& BeforeState, vector& AfterState, vector& BeforeUncoreState, vector& AfterUncoreState, vector& BeforeSocketState, vector& AfterSocketState, const CsvOutputType outputType) { printDateForCSV(outputType, separator); if (BeforeState.size() > 0 && AfterState.size() > 0) { choose(outputType, []() { cout << separator; }, []() { cout << "ms" << separator; }, [&]() { cout << (1000ULL * getInvariantTSC(BeforeState[0], AfterState[0])) / m->getNominalFrequency() << separator; }); } for (auto& typeEvents : curPMUConfigs) { const auto & type = typeEvents.first; const auto & events = typeEvents.second.programmable; const auto & fixedEvents = typeEvents.second.fixed; auto printCores = [&m, &BeforeState, &AfterState, &type, &fixedEvents, &events, &outputType](const pcm::TopologyEntry::CoreType & coreType) { for (uint32 core = 0; core < m->getNumCores(); ++core) { if (show_partial_core_output && ycores.test(core) == false) continue; if (m->isHybrid() && m->getCoreType(core) != coreType) { continue; } const uint64 fixedCtrValues[] = { getInstructionsRetired(BeforeState[core], AfterState[core]), getCycles(BeforeState[core], AfterState[core]), getRefCycles(BeforeState[core], AfterState[core]), getAllSlotsRaw(BeforeState[core], AfterState[core]) }; const uint64 topdownCtrValues[] = { uint64(getFrontendBound(BeforeState[core], AfterState[core]) * maxPerfMetricsValue), uint64(getBadSpeculation(BeforeState[core], AfterState[core]) * maxPerfMetricsValue), uint64(getBackendBound(BeforeState[core], AfterState[core]) * maxPerfMetricsValue), uint64(getRetiring(BeforeState[core], AfterState[core]) * maxPerfMetricsValue) }; for (const auto& event : fixedEvents) { auto print = [&](const std::string& metric, const uint64 value) { choose(outputType, [m, core]() { cout << "SKT" << m->getSocketId(core) << "CORE" << core << separator; }, [&metric]() { cout << metric << separator; }, [&value]() { cout << value << separator; }); }; for (uint32 cnt = 0; cnt < 4; ++cnt) { if (extract_bits(event.first[0], 4U * cnt, 1U + 4U * cnt)) { print(event.second.empty() ? fixedCoreEventNames[cnt] : event.second, fixedCtrValues[cnt]); if (cnt == 3 && (event.first[PerfMetricsConfig] & PerfMetricsMask)) { for (uint32 t = 0; t < 4; ++t) { print(topdownEventNames[t], topdownCtrValues[t]); } } } } } int i = 0; for (auto& event : events) { choose(outputType, [m, core]() { cout << "SKT" << m->getSocketId(core) << "CORE" << core << separator; }, [&event, &i, &type]() { if (event.second.empty()) cout << type << "Event" << i << separator; else cout << event.second << separator; }, [&]() { cout << getNumberOfCustomEvents(i, BeforeState[core], AfterState[core]) << separator; }); ++i; } } }; if (type == "core") { printCores(pcm::TopologyEntry::Core); } else if (type == "atom") { printCores(pcm::TopologyEntry::Atom); } else if (type == "m3upi") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 l = 0; l < m->getQPILinksPerSocket(); ++l) { int i = 0; for (auto& event : events) { choose(outputType, [s, l]() { cout << "SKT" << s << "LINK" << l << separator; }, [&event, &i]() { if (event.second.empty()) cout << "M3UPIEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getM3UPICounter(l, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "xpi" || type == "upi" || type == "qpi") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 l = 0; l < m->getQPILinksPerSocket(); ++l) { int i = 0; for (auto& event : events) { choose(outputType, [s, l]() { cout << "SKT" << s << "LINK" << l << separator; }, [&event, &i]() { if (event.second.empty()) cout << "XPIEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getXPICounter(l, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "imc") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 ch = 0; ch < m->getMCChannelsPerSocket(); ++ch) { if (fixedEvents.size()) { choose(outputType, [s, ch]() { cout << "SKT" << s << "CHAN" << ch << separator; }, [&fixedEvents]() { cout << "DRAMClocks" << fixedEvents[0].second << separator; }, [&]() { cout << getDRAMClocks(ch, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); } int i = 0; for (auto& event : events) { choose(outputType, [s, ch]() { cout << "SKT" << s << "CHAN" << ch << separator; }, [&event, &i]() { if (event.second.empty()) cout << "IMCEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getMCCounter(ch, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "m2m") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 mc = 0; mc < m->getMCPerSocket(); ++mc) { int i = 0; for (auto& event : events) { choose(outputType, [s, mc]() { cout << "SKT" << s << "MC" << mc << separator; }, [&event, &i]() { if (event.second.empty()) cout << "M2MEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getM2MCounter(mc, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "pcu") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { int i = 0; for (auto& event : events) { choose(outputType, [s]() { cout << "SKT" << s << separator; }, [&event, &i]() { if (event.second.empty()) cout << "PCUEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getPCUCounter(i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } else if (type == "package_msr") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { auto printMSR = [&](const PCM::RawEventConfig & event) { const auto index = event.first[PCM::MSREventPosition::index]; const auto msrType = (PCM::MSRType)event.first[PCM::MSREventPosition::type]; choose(outputType, [s]() { cout << "SKT" << s << separator; }, [&]() { if (event.second.empty()) cout << getMSREventString(index, type, msrType) << separator; else cout << event.second << separator; }, [&]() { cout << getMSREvent(index, msrType, BeforeSocketState[s], AfterSocketState[s]) << separator; }); }; for (const auto& event : events) { printMSR(event); } for (const auto& event : fixedEvents) { printMSR(event); } } } else if (type == "thread_msr") { for (uint32 core = 0; core < m->getNumCores(); ++core) { auto printMSR = [&](const PCM::RawEventConfig& event) { const auto index = event.first[PCM::MSREventPosition::index]; const auto msrType = (PCM::MSRType)event.first[PCM::MSREventPosition::type]; choose(outputType, [m, core]() { cout << "SKT" << m->getSocketId(core) << "CORE" << core << separator; }, [&]() { if (event.second.empty()) cout << getMSREventString(index, type, msrType) << separator; else cout << event.second << separator; }, [&]() { cout << getMSREvent(index, msrType, BeforeState[core], AfterState[core]) << separator; }); }; for (const auto& event : events) { printMSR(event); } for (const auto& event : fixedEvents) { printMSR(event); } } } else if (type == "pcicfg") { auto printPCICFG = [&](const PCM::RawEventConfig& event) { const auto values = getPCICFGEvent(event.first, SysBeforeState, SysAfterState); for (size_t r = 0; r < values.size(); ++r) { choose(outputType, [&r]() { cout << "SYSTEM_" << r << separator; }, [&]() { if (event.second.empty()) cout << getPCICFGEventString(event.first, type) << separator; else cout << event.second << separator; }, [&]() { cout << values[r] << separator; }); } }; for (const auto& event : events) { printPCICFG(event); } for (const auto& event : fixedEvents) { printPCICFG(event); } } else if (type == "ubox") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { if (fixedEvents.size()) { choose(outputType, [s]() { cout << "SKT" << s << separator; }, [&fixedEvents]() { cout << "UncoreClocks" << fixedEvents[0].second << separator; }, [&]() { cout << getUncoreClocks(BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); } int i = 0; for (auto& event : events) { choose(outputType, [s]() { cout << "SKT" << s << separator; }, [&event, &i]() { if (event.second.empty()) cout << "UBOXEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getUBOXCounter(i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } else if (type == "cbo" || type == "cha") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 cbo = 0; cbo < m->getMaxNumOfCBoxes(); ++cbo) { int i = 0; for (auto& event : events) { choose(outputType, [s, cbo]() { cout << "SKT" << s << "C" << cbo << separator; }, [&event, &i]() { if (event.second.empty()) cout << "CBOEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getCBOCounter(cbo, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "mdf") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 mdf = 0; mdf < m->getMaxNumOfMDFs(); ++mdf) { int i = 0; for (auto& event : events) { choose(outputType, [s, mdf]() { cout << "SKT" << s << "MDF" << mdf << separator; }, [&event, &i]() { if (event.second.empty()) cout << "MDFEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getMDFCounter(mdf, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "irp") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 stack = 0; stack < m->getMaxNumOfIIOStacks(); ++stack) { int i = 0; for (auto& event : events) { choose(outputType, [s, stack]() { cout << "SKT" << s << "IRP" << stack << separator; }, [&event, &i]() { if (event.second.empty()) cout << "IRPEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getIRPCounter(stack, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "iio") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 stack = 0; stack < m->getMaxNumOfIIOStacks(); ++stack) { int i = 0; for (auto& event : events) { choose(outputType, [s, stack]() { cout << "SKT" << s << "IIO" << stack << separator; }, [&event, &i]() { if (event.second.empty()) cout << "IIOEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getIIOCounter(stack, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "cxlcm") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 p = 0; p < ServerUncoreCounterState::maxCXLPorts; ++p) { int i = 0; for (auto& event : events) { choose(outputType, [s, p]() { cout << "SKT" << s << "CXLCM" << p << separator; }, [&event, &i]() { if (event.second.empty()) cout << "CXLCMEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getCXLCMCounter(p, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else if (type == "cxldp") { for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 p = 0; p < ServerUncoreCounterState::maxCXLPorts; ++p) { int i = 0; for (auto& event : events) { choose(outputType, [s, p]() { cout << "SKT" << s << "CXLDP" << p << separator; }, [&event, &i]() { if (event.second.empty()) cout << "CXLDPEvent" << i << separator; else cout << event.second << separator; }, [&]() { cout << getCXLDPCounter(p, i, BeforeUncoreState[s], AfterUncoreState[s]) << separator; }); ++i; } } } } else { std::cerr << "ERROR: unrecognized PMU type \"" << type << "\"\n"; } } if (flushLine) { cout << endl; } else { cout << "\n"; } } void printAll(const PCM::RawPMUConfigs& curPMUConfigs, PCM * m, SystemCounterState & SysBeforeState, SystemCounterState& SysAfterState, vector& BeforeState, vector& AfterState, vector& BeforeUncoreState, vector& AfterUncoreState, vector& BeforeSocketState, vector& AfterSocketState, std::vector& PMUConfigs, const bool & isLastGroup) { if (outputToJson) { printTransposed(curPMUConfigs, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Json, isLastGroup); return; } static bool displayHeader = true; if (!extendPrintout && transpose) displayHeader = false; if (transpose) { if (displayHeader) { // Need to go through all possible print on first run to form header. if (singleHeader) { // merge header 2 and 1, print and get all offsets cout << "Date" << separator << "Time" << separator << "Event" << separator; cout << "ms" << separator << "InvariantTSC"; for (auto &config : PMUConfigs) printTransposed(config, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Header21, isLastGroup); } else { // print 2 headers in 2 rows for (int i = 0 ; i < 4 ; i++) cout << separator; // print header_1 and get all offsets for (auto &config : PMUConfigs) printTransposed(config, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Header1, isLastGroup); cout << endl; // print header_2 cout << "Date" << separator << "Time" << separator << "Event" << separator; cout << "ms" << separator << "InvariantTSC"; for (auto &config : PMUConfigs) printTransposed(config, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Header2, isLastGroup); } cout << endl; } printTransposed(curPMUConfigs, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Data, isLastGroup); } else { if (displayHeader) { print(curPMUConfigs, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Header1); print(curPMUConfigs, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Header2); } print(curPMUConfigs, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, Data); } displayHeader = false; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); parseParam(argc, argv, "out", [](const char* p) { const string filename{ p }; if (!filename.empty()) { PCM::setOutput(filename, true); } }); null_stream nullStream2; #ifdef PCM_FORCE_SILENT null_stream nullStream1; std::cout.rdbuf(&nullStream1); std::cerr.rdbuf(&nullStream2); #else check_and_set_silent(argc, argv, nullStream2); #endif set_signal_handlers(); set_real_time_priority(true); cerr << "\n"; cerr << " Intel(r) Performance Counter Monitor: Raw Event Monitoring Utility \n"; cerr << "\n"; std::vector PMUConfigs(1); double delay = -1.0; int pid{-1}; char* sysCmd = NULL; char** sysArgv = NULL; MainLoop mainLoop; string program = string(argv[0]); bool forceRTMAbortMode = false; bool reset_pmu = false; PCM* m = PCM::getInstance(); parsePID(argc, argv, pid); #ifdef PCM_SIMDJSON_AVAILABLE parseParam(argc, argv, "ep", [](const char* p) { eventFileLocationPrefix = p;}); #endif if (argc > 1) do { argv++; argc--; string arg_value; if (*argv == nullptr) { continue; } else if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { if (!arg_value.empty()) { m->setOutput(arg_value); } } else if (check_argument_equals(*argv, {"-json", "/json"})) { separator = ",\""; outputToJson = true; } else if (extract_argument_value(*argv, {"-json", "/json"}, arg_value)) { separator = ",\""; outputToJson = true; if (!arg_value.empty()) { m->setOutput(arg_value); } continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (isPIDOption(argv)) { argv++; argc--; continue; } else if (check_argument_equals(*argv, {"-reset", "/reset", "-r"})) { reset_pmu = true; continue; } else if (check_argument_equals(*argv, {"-tr", "/tr"})) { transpose = true; continue; } else if (check_argument_equals(*argv, {"-ext", "/ext"})) { extendPrintout = true; continue; } else if (check_argument_equals(*argv, {"-single-header", "/single-header"})) { singleHeader = true; continue; } else if (check_argument_equals(*argv, {"-l"})) { std::cout.imbue(std::locale("")); separator = "\t"; continue; } else if (check_argument_equals(*argv, {"-tab"})) { separator = "\t"; continue; } else if (check_argument_equals(*argv, {"--yescores", "-yc", "/yc"})) { argv++; argc--; show_partial_core_output = true; if (*argv == NULL) { cerr << "Error: --yescores requires additional argument.\n"; exit(EXIT_FAILURE); } std::stringstream ss(*argv); while (ss.good()) { string s; int core_id; std::getline(ss, s, ','); if (s.empty()) continue; core_id = atoi(s.c_str()); if (core_id > MAX_CORES) { cerr << "Core ID:" << core_id << " exceed maximum range " << MAX_CORES << ", program abort\n"; exit(EXIT_FAILURE); } ycores.set(atoi(s.c_str()), true); } if (m->getNumCores() > MAX_CORES) { cerr << "Error: --yescores option is enabled, but #define MAX_CORES " << MAX_CORES << " is less than m->getNumCores() = " << m->getNumCores() << "\n"; cerr << "There is a potential to crash the system. Please increase MAX_CORES to at least " << m->getNumCores() << " and re-enable this option.\n"; exit(EXIT_FAILURE); } continue; } else if (check_argument_equals(*argv, {"-out", "/out"})) { argv++; argc--; continue; } else if (check_argument_equals(*argv, {"-ep", "/ep"})) { argv++; argc--; continue; } else if (check_argument_equals(*argv, {"-edp", "/edp"})) { sampleSeparator = true; defaultDelay = 0.2; transpose = true; m->printDetailedSystemTopology(); continue; } else if (check_argument_equals(*argv, {"-el", "/el"})) { argv++; argc--; const auto p = *argv; if (p == nullptr) { cerr << "ERROR: no parameter value provided for 'el' option\n"; exit(EXIT_FAILURE); } else if (addEvents(PMUConfigs, p) == false) { exit(EXIT_FAILURE); } continue; } else if (check_argument_equals(*argv, {"-e"})) { argv++; argc--; const auto p = *argv; if (p == nullptr) { cerr << "ERROR: no parameter value provided for 'e' option\n"; exit(EXIT_FAILURE); } else if (addEvent(PMUConfigs[0], p) != AddEventStatus::OK) { exit(EXIT_FAILURE); } continue; } else if (CheckAndForceRTMAbortMode(*argv, m)) { forceRTMAbortMode = true; continue; } else if (check_argument_equals(*argv, {"-f", "/f"})) { flushLine = true; continue; } else if (check_argument_equals(*argv, {"-s", "/s"})) { sampleSeparator = true; continue; } else if (check_argument_equals(*argv, {"-v", "/v"})) { verbose = true; continue; } else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } while (argc > 1); // end of command line parsing loop if (reset_pmu) { cerr << "\n Resetting PMU configuration\n"; m->resetPMU(); } print_cpu_details(); size_t nGroups = 0; for (const auto& group : PMUConfigs) { if (!group.empty()) ++nGroups; } for (size_t i = 0; i < PMUConfigs.size(); ++i) { if (PMUConfigs[i].empty()) { // erase empty groups PMUConfigs.erase(PMUConfigs.begin() + i); --i; } } assert(PMUConfigs.size() == nGroups); if (nGroups == 0) { cerr << "No events specified. Exiting.\n"; exit(EXIT_FAILURE); } cerr << "Collecting " << nGroups << " event group(s)\n"; if (nGroups > 1) { transpose = true; cerr << "Enforcing transposed event output because the number of event groups > 1\n"; } print_pid_collection_message(pid); auto programPMUs = [&m, &pid](const PCM::RawPMUConfigs & config) { if (verbose) { for (const auto & pmuConfig: config) { for (const auto & e : pmuConfig.second.fixed) { cerr << "Programming " << pmuConfig.first << " fixed event: " << e.second << "\n"; } for (const auto & e : pmuConfig.second.programmable) { cerr << "Programming " << pmuConfig.first << " programmable event: " << e.second << "\n"; } } } PCM::ErrorCode status = m->program(config, !verbose, pid); m->checkError(status); }; SystemCounterState SysBeforeState, SysAfterState; vector BeforeState, AfterState; vector BeforeSocketState, AfterSocketState; vector BeforeUncoreState, AfterUncoreState; BeforeUncoreState.resize(m->getNumSockets()); AfterUncoreState.resize(m->getNumSockets()); if ((sysCmd != NULL) && (delay <= 0.0)) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } if (delay <= 0.0) delay = defaultDelay; cerr << "Update every " << delay << " seconds\n"; std::cout.precision(2); std::cout << std::fixed; if (sysCmd != NULL) { MySystem(sysCmd, sysArgv); } auto programAndReadGroup = [&](const PCM::RawPMUConfigs & group) { if (forceRTMAbortMode) { m->enableForceRTMAbortMode(true); } programPMUs(group); m->globalFreezeUncoreCounters(); m->getAllCounterStates(SysBeforeState, BeforeSocketState, BeforeState); for (uint32 s = 0; s < m->getNumSockets(); ++s) { BeforeUncoreState[s] = m->getServerUncoreCounterState(s); } m->globalUnfreezeUncoreCounters(); }; if (nGroups == 1) { programAndReadGroup(PMUConfigs[0]); } mainLoop([&]() { size_t groupNr = 0; for (const auto & group : PMUConfigs) { ++groupNr; if (nGroups > 1) { programAndReadGroup(group); } calibratedSleep(delay, sysCmd, mainLoop, m); m->globalFreezeUncoreCounters(); m->getAllCounterStates(SysAfterState, AfterSocketState, AfterState); for (uint32 s = 0; s < m->getNumSockets(); ++s) { AfterUncoreState[s] = m->getServerUncoreCounterState(s); } m->globalUnfreezeUncoreCounters(); //cout << "Time elapsed: " << dec << fixed << AfterTime - BeforeTime << " ms\n"; //cout << "Called sleep function for " << dec << fixed << delay_ms << " ms\n"; printAll(group, m, SysBeforeState, SysAfterState, BeforeState, AfterState, BeforeUncoreState, AfterUncoreState, BeforeSocketState, AfterSocketState, PMUConfigs, groupNr == nGroups); if (nGroups == 1) { std::swap(BeforeState, AfterState); std::swap(BeforeSocketState, AfterSocketState); std::swap(BeforeUncoreState, AfterUncoreState); std::swap(SysBeforeState, SysAfterState); } } if (m->isBlocked()) { // in case PCM was blocked after spawning child application: break monitoring loop here return false; } return true; }); exit(EXIT_SUCCESS); } pcm-202307/src/pcm-sensor-server.cpp000066400000000000000000004073341445420033100172640ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2016-2022, Intel Corporation // Use port allocated for PCM in prometheus: // https://github.com/prometheus/prometheus/wiki/Default-port-allocations constexpr unsigned int DEFAULT_HTTP_PORT = 9738; constexpr unsigned int DEFAULT_HTTPS_PORT = DEFAULT_HTTP_PORT; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cpucounters.h" #include "debug.h" #include "topology.h" #include "dashboard.h" #define PCMWebServerVersion "0.1" #if defined (USE_SSL) # include # include # define CERT_FILE_NAME "./server.pem" # define KEY_FILE_NAME "./server.pem" #endif // USE_SSL #include #include #include "threadpool.h" using namespace pcm; std::string const HTTP_EOL( "\r\n" ); std::string const PROM_EOL( "\n" ); class Indent { public: explicit Indent( std::string const & is = std::string(" ") ) : indstr_(is), indent_(""), len_(0), indstrlen_(is.length()) { } Indent() = delete; Indent(Indent const &) = default; Indent & operator = (Indent const &) = default; ~Indent() = default; friend std::stringstream& operator <<( std::stringstream& stream, Indent in ); void printIndentationString(std::stringstream& s) { s << indent_; } // We only need post inc und pre dec Indent& operator--() { if ( len_ > 0 ) --len_; else throw std::runtime_error("Indent: Decremented len_ too often!"); indent_.erase( len_ * indstrlen_ ); return *this; } Indent operator++(int) { Indent copy( *this ); ++len_; indent_ += indstr_; // add one more indstr_ return copy; } private: std::string indstr_; std::string indent_; size_t len_; size_t const indstrlen_; }; std::stringstream& operator <<( std::stringstream& stream, Indent in ) { in.printIndentationString( stream ); return stream; } class datetime { public: datetime() { std::time_t t = std::time( nullptr ); const auto gt = std::gmtime( &t ); if (gt == nullptr) throw std::runtime_error("std::gmtime returned nullptr"); now = *gt; } datetime( std::tm t ) : now( t ) {} ~datetime() = default; datetime( datetime const& ) = default; datetime & operator = ( datetime const& ) = default; public: void printDateTimeString( std::ostream& os ) const { std::stringstream str(""); char timeBuffer[64]; std::fill(timeBuffer, timeBuffer + 64, 0); str.imbue( std::locale::classic() ); if ( strftime( timeBuffer, 63, "%a, %d %b %Y %T GMT", &now ) ) str << timeBuffer; else throw std::runtime_error("Error writing to timeBuffer, too small?"); os << str.str(); } std::string toString() const { std::stringstream str(""); char timeBuffer[64]; std::fill(timeBuffer, timeBuffer + 64, 0); str.imbue( std::locale::classic() ); if ( strftime( timeBuffer, 63, "%a, %d %b %Y %T GMT", &now ) ) str << timeBuffer; else throw std::runtime_error("Error writing to timeBuffer, too small?"); return str.str(); } private: std::tm now; }; std::ostream& operator<<( std::ostream& os, datetime const & dt ) { dt.printDateTimeString(os); return os; } class date { public: date() { now = std::time(nullptr); } ~date() = default; date( date const& ) = default; date & operator = ( date const& ) = default; public: void printDate( std::ostream& os ) const { char buf[64]; const auto t = std::localtime(&now); assert(t); std::strftime( buf, 64, "%F", t); os << buf; } private: std::time_t now; }; std::ostream& operator<<( std::ostream& os, date const & d ) { d.printDate(os); return os; } /* Not used right now std::string read_ndctl_info( std::ofstream& logfile ) { int pipes[2]; if ( pipe( pipes ) == -1 ) { logfile << date() << ": ERROR Cannot create pipe, errno = " << errno << ", strerror: " << strerror(errno) << ". Exit 50.\n"; exit(50); } std::stringstream ndctl; if ( fork() == 0 ) { // child, writes to pipe, close read-end close( pipes[0] ); dup2( pipes[1], fileno(stdout) ); execl( "/usr/bin/ndctl", "ndctl", "list", (char*)NULL ); } else { // parent, reads from pipe, close write-end close( pipes[1] ); char buf[2049]; std::fill(buf, buf + 2049, 0); ssize_t len = 0; while( (len = read( pipes[0], buf, 2048 )) > 0 ) { buf[len] = '\0'; ndctl << buf; } close( pipes[0] ); if ( len < 0 ) { logfile << ": ERROR Read from ndctl pipe failed. errno = " << errno << ". strerror(errno) = " << strerror(errno) << ". Exit 52.\n"; exit(52); } logfile << datetime() << ": INFO Read JSON from ndctl pipe: " << ndctl.str() << ".\n"; } return ndctl.str(); } */ class HTTPServer; class SignalHandler { public: static SignalHandler* getInstance() { static SignalHandler instance; return &instance; } static void handleSignal( int signum ); void setSocket( int s ) { networkSocket_ = s; } void setHTTPServer( HTTPServer* hs ) { httpServer_ = hs; } void ignoreSignal( int signum ) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigaction( signum, &sa, 0 ); } void installHandler( void (*handler)(int), int signum ) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_handler = handler; sa.sa_flags = 0; sigaction( signum, &sa, 0 ); } SignalHandler( SignalHandler const & ) = delete; void operator=( SignalHandler const & ) = delete; ~SignalHandler() = default; private: SignalHandler() = default; private: static int networkSocket_; static HTTPServer* httpServer_; }; int SignalHandler::networkSocket_ = 0; HTTPServer* SignalHandler::httpServer_ = nullptr; class JSONPrinter : Visitor { public: enum LineEndAction { NewLineOnly = 0, DelimiterOnly, DelimiterAndNewLine, LineEndAction_Spare = 255 }; JSONPrinter( std::pair,std::shared_ptr> aggregatorPair ) : indentation(" "), aggPair_( aggregatorPair ) { if ( nullptr == aggPair_.second.get() ) throw std::runtime_error("BUG: second Aggregator == nullptr!"); DBG(2, "Constructor: before=", std::hex, aggPair_.first.get(), ", after=", std::hex, aggPair_.second.get() ); } JSONPrinter( JSONPrinter const & ) = delete; JSONPrinter & operator = ( JSONPrinter const & ) = delete; JSONPrinter() = delete; CoreCounterState const getCoreCounter( std::shared_ptr ag, uint32 tid ) const { CoreCounterState ccs; if ( nullptr == ag.get() ) return ccs; return std::move( ag->coreCounterStates()[tid] ); } SocketCounterState const getSocketCounter( std::shared_ptr ag, uint32 sid ) const { SocketCounterState socs; if ( nullptr == ag.get() ) return socs; return std::move( ag->socketCounterStates()[sid] ); } SystemCounterState getSystemCounter( std::shared_ptr ag ) const { SystemCounterState sycs; if ( nullptr == ag.get() ) return sycs; return std::move( ag->systemCounterState() ); } virtual void dispatch( HyperThread* ht ) override { printCounter( "Object", "HyperThread" ); printCounter( "Thread ID", ht->threadID() ); printCounter( "OS ID", ht->osID() ); CoreCounterState before = getCoreCounter( aggPair_.first, ht->osID() ); CoreCounterState after = getCoreCounter( aggPair_.second, ht->osID() ); printBasicCounterState( before, after ); } virtual void dispatch( ServerUncore* su ) override { printCounter( "Object", "ServerUncore" ); SocketCounterState before = getSocketCounter( aggPair_.first, su->socketID() ); SocketCounterState after = getSocketCounter( aggPair_.second, su->socketID() ); printUncoreCounterState( before, after ); } virtual void dispatch( ClientUncore* ) override { printCounter( "Object", "ClientUncore" ); } virtual void dispatch( Core* c ) override { printCounter( "Object", "Core" ); auto vec = c->threads(); printCounter( "Number of threads", vec.size() ); startObject( "Threads", BEGIN_LIST ); iterateVectorAndCallAccept( vec ); endObject( JSONPrinter::LineEndAction::DelimiterAndNewLine, END_LIST ); printCounter( "Tile ID", c->tileID() ); printCounter( "Core ID", c->coreID() ); printCounter( "Socket ID", c->socketID() ); } virtual void dispatch( SystemRoot const & s ) override { using namespace std::chrono; auto interval = duration_cast( aggPair_.second->dispatchedAt() - aggPair_.first->dispatchedAt() ).count(); startObject( "", BEGIN_OBJECT ); printCounter( "Interval us", interval ); printCounter( "Object", "SystemRoot" ); auto vec = s.sockets(); printCounter( "Number of sockets", vec.size() ); startObject( "Sockets", BEGIN_LIST ); iterateVectorAndCallAccept( vec ); endObject( JSONPrinter::LineEndAction::DelimiterAndNewLine, END_LIST ); SystemCounterState before = getSystemCounter( aggPair_.first ); SystemCounterState after = getSystemCounter( aggPair_.second ); startObject( "QPI/UPI Links", BEGIN_OBJECT ); printSystemCounterState( before, after ); endObject( JSONPrinter::LineEndAction::DelimiterAndNewLine, END_OBJECT ); startObject( "Core Aggregate", BEGIN_OBJECT ); printBasicCounterState( before, after ); endObject( JSONPrinter::LineEndAction::DelimiterAndNewLine, END_OBJECT ); startObject( "Uncore Aggregate", BEGIN_OBJECT ); printUncoreCounterState( before, after ); endObject( JSONPrinter::LineEndAction::NewLineOnly, END_OBJECT ); endObject( JSONPrinter::LineEndAction::NewLineOnly, END_OBJECT ); } virtual void dispatch( Socket* s ) override { printCounter( "Object", "Socket" ); printCounter( "Socket ID", s->socketID() ); auto vec = s->cores(); printCounter( "Number of cores", vec.size() ); startObject( "Cores", BEGIN_LIST ); iterateVectorAndCallAccept( vec ); endObject( JSONPrinter::LineEndAction::DelimiterAndNewLine, END_LIST ); startObject( "Uncore", BEGIN_OBJECT ); s->uncore()->accept( *this ); endObject( JSONPrinter::LineEndAction::DelimiterAndNewLine, END_OBJECT ); startObject( "Core Aggregate", BEGIN_OBJECT ); SocketCounterState before = getSocketCounter( aggPair_.first, s->socketID() ); SocketCounterState after = getSocketCounter( aggPair_.second, s->socketID() ); printBasicCounterState( before, after ); endObject( JSONPrinter::LineEndAction::NewLineOnly, END_OBJECT ); } std::string str( void ) { return ss.str(); } private: void printBasicCounterState( BasicCounterState const& before, BasicCounterState const& after ) { startObject( "Core Counters", BEGIN_OBJECT ); printCounter( "Instructions Retired Any", getInstructionsRetired( before, after ) ); printCounter( "Clock Unhalted Thread", getCycles ( before, after ) ); printCounter( "Clock Unhalted Ref", getRefCycles ( before, after ) ); printCounter( "L3 Cache Misses", getL3CacheMisses ( before, after ) ); printCounter( "L3 Cache Hits", getL3CacheHits ( before, after ) ); printCounter( "L2 Cache Misses", getL2CacheMisses ( before, after ) ); printCounter( "L2 Cache Hits", getL2CacheHits ( before, after ) ); printCounter( "L3 Cache Occupancy", getL3CacheOccupancy ( after ) ); printCounter( "Invariant TSC", getInvariantTSC ( before, after ) ); printCounter( "SMI Count", getSMICount ( before, after ) ); endObject( JSONPrinter::DelimiterAndNewLine, END_OBJECT ); //DBG( 2, "Invariant TSC before=", before.InvariantTSC, ", after=", after.InvariantTSC, ", difference=", after.InvariantTSC-before.InvariantTSC ); startObject( "Energy Counters", BEGIN_OBJECT ); printCounter( "Thermal Headroom", after.getThermalHeadroom() ); uint32 i = 0; for ( ; i < ( PCM::MAX_C_STATE ); ++i ) { std::stringstream s; s << "CStateResidency[" << i << "]"; printCounter( s.str(), getCoreCStateResidency( i, before, after ) ); } // Here i == PCM::MAX_STATE so no need to type so many characters ;-) std::stringstream s; s << "CStateResidency[" << i << "]"; printCounter( s.str(), getCoreCStateResidency( i, before, after ) ); endObject( JSONPrinter::DelimiterAndNewLine, END_OBJECT ); startObject( "Core Memory Bandwidth Counters", BEGIN_OBJECT ); printCounter( "Local Memory Bandwidth", getLocalMemoryBW( before, after ) ); printCounter( "Remote Memory Bandwidth", getRemoteMemoryBW( before, after ) ); endObject( JSONPrinter::NewLineOnly, END_OBJECT ); } void printUncoreCounterState( SocketCounterState const& before, SocketCounterState const& after ) { startObject( "Uncore Counters", BEGIN_OBJECT ); printCounter( "DRAM Writes", getBytesWrittenToMC ( before, after ) ); printCounter( "DRAM Reads", getBytesReadFromMC ( before, after ) ); printCounter( "Persistent Memory Writes", getBytesWrittenToPMM ( before, after ) ); printCounter( "Persistent Memory Reads", getBytesReadFromPMM ( before, after ) ); printCounter( "Embedded DRAM Writes", getBytesWrittenToEDC ( before, after ) ); printCounter( "Embedded DRAM Reads", getBytesReadFromEDC ( before, after ) ); printCounter( "Memory Controller IO Requests", getIORequestBytesFromMC( before, after ) ); printCounter( "Package Joules Consumed", getConsumedJoules ( before, after ) ); printCounter( "DRAM Joules Consumed", getDRAMConsumedJoules ( before, after ) ); uint32 i = 0; for ( ; i < ( PCM::MAX_C_STATE ); ++i ) { std::stringstream s; s << "CStateResidency[" << i << "]"; printCounter( s.str(), getPackageCStateResidency( i, before, after ) ); } // Here i == PCM::MAX_STATE so no need to type so many characters ;-) std::stringstream s; s << "CStateResidency[" << i << "]"; printCounter( s.str(), getPackageCStateResidency( i, before, after ) ); endObject( JSONPrinter::NewLineOnly, END_OBJECT ); } void printSystemCounterState( SystemCounterState const& before, SystemCounterState const& after ) { PCM* pcm = PCM::getInstance(); uint32 sockets = pcm->getNumSockets(); uint32 links = pcm->getQPILinksPerSocket(); for ( uint32 i=0; i < sockets; ++i ) { startObject( std::string( "QPI Counters Socket " ) + std::to_string( i ), BEGIN_OBJECT ); printCounter( std::string( "CXL Write Cache" ), getCXLWriteCacheBytes (i, before, after ) ); printCounter( std::string( "CXL Write Mem" ), getCXLWriteMemBytes (i, before, after ) ); for ( uint32 j=0; j < links; ++j ) { printCounter( std::string( "Incoming Data Traffic On Link " ) + std::to_string( j ), getIncomingQPILinkBytes ( i, j, before, after ) ); printCounter( std::string( "Outgoing Data And Non-Data Traffic On Link " ) + std::to_string( j ), getOutgoingQPILinkBytes ( i, j, before, after ) ); printCounter( std::string( "Utilization Incoming Data Traffic On Link " ) + std::to_string( j ), getIncomingQPILinkUtilization( i, j, before, after ) ); printCounter( std::string( "Utilization Outgoing Data And Non-Data Traffic On Link " ) + std::to_string( j ), getOutgoingQPILinkUtilization( i, j, before, after ) ); } endObject( JSONPrinter::DelimiterAndNewLine, END_OBJECT ); } } template void printCounter( std::string const & name, Counter c ); template void iterateVectorAndCallAccept( Vector const& v ); void startObject(std::string const& s, char const ch ) { std::string name; if ( s.size() != 0 ) name = "\"" + s + "\" : "; ss << (indentation++) << name << ch << HTTP_EOL; } void endObject( enum JSONPrinter::LineEndAction lea, char const ch ) { // look 3 chars back, if it is a ',' then delete it. // make read same as write position - 3 std::stringstream::pos_type oldReadPos = ss.tellg(); ss.seekg( -3, std::ios_base::end ); if ( ss.peek() == ',' ) { ss.seekp( ss.tellg() ); // Make write same as read position ss << HTTP_EOL; } ss.seekg( oldReadPos );// Just making sure the readpointer is set back to where it was ss << (--indentation) << ch; if ( lea == LineEndAction::NewLineOnly ) ss << HTTP_EOL; else if ( lea == LineEndAction::DelimiterAndNewLine ) ss << "," << HTTP_EOL; else if ( lea == LineEndAction::DelimiterOnly ) ss << ","; else throw std::runtime_error( "Unknown LineEndAction enum" ); } void insertListDelimiter() { ss << "," << HTTP_EOL; } private: Indent indentation; std::pair,std::shared_ptr> aggPair_; const char BEGIN_OBJECT = '{'; const char END_OBJECT = '}'; const char BEGIN_LIST = '['; const char END_LIST = ']'; }; template void JSONPrinter::printCounter( std::string const & name, Counter c ) { if ( std::is_same::value || std::is_same::value ) ss << indentation << "\"" << name << "\" : \"" << c << "\"," << HTTP_EOL; else ss << indentation << "\"" << name << "\" : " << c << "," << HTTP_EOL; } template void JSONPrinter::iterateVectorAndCallAccept(Vector const& v) { for ( auto* vecElem: v ) { // Inside a list objects are not named startObject( "", BEGIN_OBJECT ); vecElem->accept( *this ); endObject( JSONPrinter::DelimiterAndNewLine, END_OBJECT ); } }; class PrometheusPrinter : Visitor { public: PrometheusPrinter( std::pair,std::shared_ptr> aggregatorPair ) : aggPair_( aggregatorPair ) { if ( nullptr == aggPair_.second.get() ) throw std::runtime_error("BUG: second Aggregator == nullptr!"); DBG(2, "Constructor: before=", std::hex, aggPair_.first.get(), ", after=", std::hex, aggPair_.second.get() ); } PrometheusPrinter( PrometheusPrinter const & ) = delete; PrometheusPrinter & operator = ( PrometheusPrinter const & ) = delete; PrometheusPrinter() = delete; CoreCounterState const getCoreCounter( std::shared_ptr ag, uint32 tid ) const { CoreCounterState ccs; if ( nullptr == ag.get() ) return ccs; return std::move( ag->coreCounterStates()[tid] ); } SocketCounterState const getSocketCounter( std::shared_ptr ag, uint32 sid ) const { SocketCounterState socs; if ( nullptr == ag.get() ) return socs; return std::move( ag->socketCounterStates()[sid] ); } SystemCounterState getSystemCounter( std::shared_ptr ag ) const { SystemCounterState sycs; if ( nullptr == ag.get() ) return sycs; return std::move( ag->systemCounterState() ); } virtual void dispatch( HyperThread* ht ) override { addToHierarchy( "thread=\"" + std::to_string( ht->threadID() ) + "\"" ); printCounter( "OS ID", ht->osID() ); CoreCounterState before = getCoreCounter( aggPair_.first, ht->osID() ); CoreCounterState after = getCoreCounter( aggPair_.second, ht->osID() ); printBasicCounterState( before, after ); removeFromHierarchy(); } virtual void dispatch( ServerUncore* su ) override { printComment( std::string( "Uncore Counters Socket " ) + std::to_string( su->socketID() ) ); SocketCounterState before = getSocketCounter( aggPair_.first, su->socketID() ); SocketCounterState after = getSocketCounter( aggPair_.second, su->socketID() ); printUncoreCounterState( before, after ); } virtual void dispatch( ClientUncore* ) override { } virtual void dispatch( Core* c ) override { addToHierarchy( std::string( "core=\"" ) + std::to_string( c->coreID() ) + "\"" ); auto vec = c->threads(); iterateVectorAndCallAccept( vec ); // Useless? //printCounter( "Tile ID", c->tileID() ); //printCounter( "Core ID", c->coreID() ); //printCounter( "Socket ID", c->socketID() ); removeFromHierarchy(); } virtual void dispatch( SystemRoot const & s ) override { using namespace std::chrono; auto interval = duration_cast( aggPair_.second->dispatchedAt() - aggPair_.first->dispatchedAt() ).count(); printCounter( "Measurement Interval in us", interval ); auto vec = s.sockets(); printCounter( "Number of sockets", vec.size() ); iterateVectorAndCallAccept( vec ); SystemCounterState before = getSystemCounter( aggPair_.first ); SystemCounterState after = getSystemCounter( aggPair_.second ); addToHierarchy( "aggregate=\"system\"" ); PCM* pcm = PCM::getInstance(); if ( pcm->isServerCPU() && pcm->getNumSockets() >= 2 ) { printComment( "UPI/QPI Counters" ); printSystemCounterState( before, after ); } printComment( "Core Counters Aggregate System" ); printBasicCounterState ( before, after ); printComment( "Uncore Counters Aggregate System" ); printUncoreCounterState( before, after ); removeFromHierarchy(); // aggregate=system } virtual void dispatch( Socket* s ) override { addToHierarchy( std::string( "socket=\"" ) + std::to_string( s->socketID() ) + "\"" ); printComment( std::string( "Core Counters Socket " ) + std::to_string( s->socketID() ) ); auto vec = s->cores(); iterateVectorAndCallAccept( vec ); // Uncore writes the comment for the socket uncore counters s->uncore()->accept( *this ); addToHierarchy( "aggregate=\"socket\"" ); printComment( std::string( "Core Counters Aggregate Socket " ) + std::to_string( s->socketID() ) ); SocketCounterState before = getSocketCounter( aggPair_.first, s->socketID() ); SocketCounterState after = getSocketCounter( aggPair_.second, s->socketID() ); printBasicCounterState( before, after ); removeFromHierarchy(); // aggregate=socket removeFromHierarchy(); // socket=x } std::string str( void ) { return ss.str(); } private: void printBasicCounterState( BasicCounterState const& before, BasicCounterState const& after ) { addToHierarchy( "source=\"core\"" ); printCounter( "Instructions Retired Any", getInstructionsRetired( before, after ) ); printCounter( "Clock Unhalted Thread", getCycles ( before, after ) ); printCounter( "Clock Unhalted Ref", getRefCycles ( before, after ) ); printCounter( "L3 Cache Misses", getL3CacheMisses ( before, after ) ); printCounter( "L3 Cache Hits", getL3CacheHits ( before, after ) ); printCounter( "L2 Cache Misses", getL2CacheMisses ( before, after ) ); printCounter( "L2 Cache Hits", getL2CacheHits ( before, after ) ); printCounter( "L3 Cache Occupancy", getL3CacheOccupancy ( after ) ); printCounter( "Invariant TSC", getInvariantTSC ( before, after ) ); printCounter( "SMI Count", getSMICount ( before, after ) ); //DBG( 2, "Invariant TSC before=", before.InvariantTSC, ", after=", after.InvariantTSC, ", difference=", after.InvariantTSC-before.InvariantTSC ); printCounter( "Thermal Headroom", after.getThermalHeadroom() ); uint32 i = 0; for ( ; i <= ( PCM::MAX_C_STATE ); ++i ) { std::stringstream s; s << "index=\"" << i << "\""; addToHierarchy( s.str() ); printCounter( "CStateResidency", getCoreCStateResidency( i, before, after ) ); // need a raw CStateResidency metric because the precision is lost to unacceptable levels when trying // to compute CStateResidency for the last second using the existing CStateResidency metric printCounter( "RawCStateResidency", getCoreCStateResidency( i, after ) ); removeFromHierarchy(); } printCounter( "Local Memory Bandwidth", getLocalMemoryBW( before, after ) ); printCounter( "Remote Memory Bandwidth", getRemoteMemoryBW( before, after ) ); removeFromHierarchy(); } void printUncoreCounterState( SocketCounterState const& before, SocketCounterState const& after ) { addToHierarchy( "source=\"uncore\"" ); printCounter( "DRAM Writes", getBytesWrittenToMC ( before, after ) ); printCounter( "DRAM Reads", getBytesReadFromMC ( before, after ) ); printCounter( "Persistent Memory Writes", getBytesWrittenToPMM ( before, after ) ); printCounter( "Persistent Memory Reads", getBytesReadFromPMM ( before, after ) ); printCounter( "Embedded DRAM Writes", getBytesWrittenToEDC ( before, after ) ); printCounter( "Embedded DRAM Reads", getBytesReadFromEDC ( before, after ) ); printCounter( "Memory Controller IO Requests", getIORequestBytesFromMC( before, after ) ); printCounter( "Package Joules Consumed", getConsumedJoules ( before, after ) ); printCounter( "DRAM Joules Consumed", getDRAMConsumedJoules ( before, after ) ); uint32 i = 0; for ( ; i <= ( PCM::MAX_C_STATE ); ++i ) { std::stringstream s; s << "index=\"" << i << "\""; addToHierarchy( s.str() ); printCounter( "CStateResidency", getPackageCStateResidency( i, before, after ) ); // need a CStateResidency raw metric because the precision is lost to unacceptable levels when trying // to compute CStateResidency for the last second using the existing CStateResidency metric printCounter( "RawCStateResidency", getPackageCStateResidency( i, after ) ); removeFromHierarchy(); } removeFromHierarchy(); } void printSystemCounterState( SystemCounterState const& before, SystemCounterState const& after ) { addToHierarchy( "source=\"uncore\"" ); PCM* pcm = PCM::getInstance(); uint32 sockets = pcm->getNumSockets(); uint32 links = pcm->getQPILinksPerSocket(); for ( uint32 i=0; i < sockets; ++i ) { addToHierarchy( std::string( "socket=\"" ) + std::to_string( i ) + "\"" ); printCounter( std::string( "CXL Write Cache" ), getCXLWriteCacheBytes (i, before, after ) ); printCounter( std::string( "CXL Write Mem" ), getCXLWriteMemBytes (i, before, after ) ); for ( uint32 j=0; j < links; ++j ) { printCounter( std::string( "Incoming Data Traffic On Link " ) + std::to_string( j ), getIncomingQPILinkBytes ( i, j, before, after ) ); printCounter( std::string( "Outgoing Data And Non-Data Traffic On Link " ) + std::to_string( j ), getOutgoingQPILinkBytes ( i, j, before, after ) ); printCounter( std::string( "Utilization Incoming Data Traffic On Link " ) + std::to_string( j ), getIncomingQPILinkUtilization( i, j, before, after ) ); printCounter( std::string( "Utilization Outgoing Data And Non-Data Traffic On Link " ) + std::to_string( j ), getOutgoingQPILinkUtilization( i, j, before, after ) ); } removeFromHierarchy(); } removeFromHierarchy(); } std::string replaceIllegalCharsWithUnderbar( std::string const& s ) { size_t pos = 0; std::string str(s); while ( ( pos = str.find( '-', pos ) ) != std::string::npos ) { str.replace( pos, 1, "_" ); } pos = 0; while ( ( pos = str.find( ' ', pos ) ) != std::string::npos ) { str.replace( pos, 1, "_" ); } return str; } void addToHierarchy( std::string const& s ) { hierarchy_.push_back( s ); } void removeFromHierarchy() { hierarchy_.pop_back(); } std::string printHierarchy() { std::string s(" "); if (hierarchy_.size() == 0 ) return s; s = "{"; for(const auto & level : hierarchy_ ) { s += level + ','; } s.pop_back(); s += "} "; return s; } template void printCounter( std::string const & name, Counter c ); void printComment( std::string const &comment ) { ss << "# " << comment << PROM_EOL; } template void iterateVectorAndCallAccept( Vector const& v ); private: std::pair,std::shared_ptr> aggPair_; std::vector hierarchy_; }; template void PrometheusPrinter::printCounter( std::string const & name, Counter c ) { ss << replaceIllegalCharsWithUnderbar(name) << printHierarchy() << c << PROM_EOL; } template void PrometheusPrinter::iterateVectorAndCallAccept(Vector const& v) { for ( auto* vecElem: v ) { vecElem->accept( *this ); } }; template > class basic_socketbuf : public std::basic_streambuf { public: using Base = std::basic_streambuf; using char_type = typename Base::char_type; using int_type = typename Base::int_type; using traits_type = typename Base::traits_type; basic_socketbuf(): socketFD_(0) { // According to http://en.cppreference.com/w/cpp/io/basic_streambuf // epptr and egptr point beyond the buffer, so start + SIZE Base::setp( outputBuffer_, outputBuffer_ + SIZE ); Base::setg( inputBuffer_, inputBuffer_, inputBuffer_ ); // Default timeout of 10 seconds and 0 microseconds timeout_ = { 10, 0 }; #if defined (USE_SSL) ssl_ = nullptr; #endif } virtual ~basic_socketbuf() { basic_socketbuf::sync(); #if defined (USE_SSL) if ( nullptr != ssl_ ) { SSL_free( ssl_ ); } #endif if ( 0 != socketFD_ ) ::close( socketFD_ ); } int socket() { return socketFD_; } void setSocket( int socketFD ) { socketFD_ = socketFD; if( 0 == socketFD ) // avoid work with 0 socket after closure socket and set value to 0 return; // When receiving the socket descriptor, set the timeout const auto res = setsockopt( socketFD_, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_, sizeof(struct timeval) ); if (res != 0) { std::cerr << "setsockopt failed while setting timeout value, " << strerror( errno ) << "\n"; } } void setTimeout( struct timeval t ) { timeout_ = t; const auto res = setsockopt( socketFD_, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout_, sizeof(struct timeval) ); if (res != 0) { std::cerr << "setsockopt failed while setting timeout value, " << strerror( errno ) << "\n"; } } #if defined (USE_SSL) SSL* ssl() { return ssl_; } void setSSL( SSL* ssl ) { if ( nullptr != ssl_ ) throw std::runtime_error( "You can set the SSL pointer only once" ); if ( nullptr == ssl ) throw std::runtime_error( "Trying to set a nullptr as ssl" ); ssl_ = ssl; } #endif protected: int_type writeToSocket() { size_t bytesToSend; ssize_t bytesSent; bytesToSend = (char*)Base::pptr() - (char*)Base::pbase(); #if defined (USE_SSL) if ( nullptr == ssl_ ) { #endif bytesSent= ::send( socketFD_, (void*)outputBuffer_, bytesToSend, MSG_NOSIGNAL ); if ( -1 == bytesSent ) { std::cerr << strerror( errno ) << "\n"; return traits_type::eof(); } #if defined (USE_SSL) } else { while( 1 ) { // openSSL has no support for setting the MSG_NOSIGNAL during send // but we ignore sigpipe so we should be fine bytesSent = SSL_write( ssl_, (void*)outputBuffer_, bytesToSend ); if ( 0 >= bytesSent ) { int sslError = SSL_get_error( ssl_, bytesSent ); switch ( sslError ) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: // retry continue; // Should continue in the while loop and attempt to write again // break; case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SYSCALL: case SSL_ERROR_SSL: default: return traits_type::eof(); } } else { // Valid write break; // out of the while loop } } } #endif Base::pbump( -bytesSent ); return bytesSent; } int sync() override { if ( 0 == socketFD_ ) // Socket is closed already return 0; int_type ret = writeToSocket(); if ( traits_type::eof() == ret ) return -1; return 0; } virtual int_type overflow( int_type ch ) { // send data in buffer and reset it if ( traits_type::eof() != ch ) { *Base::pptr() = ch; Base::pbump(1); } int_type bytesWritten = 0; if ( traits_type::eof() == (bytesWritten = writeToSocket()) ) { return traits_type::eof(); } return bytesWritten; // Anything but traits_type::eof() to signal ok. } virtual int_type underflow() { std::fill(inputBuffer_, inputBuffer_ + SIZE, 0); ssize_t bytesReceived; #if defined (USE_SSL) if ( nullptr == ssl_ ) { #endif DBG( 3, "Socketbuf: Read from socket:" ); bytesReceived = ::read( socketFD_, static_cast(inputBuffer_), SIZE * sizeof( char_type ) ); if ( 0 == bytesReceived ) { // Client closed the socket normally, we will do the same ::close( socketFD_ ); return traits_type::eof(); } if ( -1 == bytesReceived ) { if ( errno ) DBG( 3, "Errno: ", errno, ", (", strerror( errno ) , ")" ); ::close( socketFD_ ); Base::setg( nullptr, nullptr, nullptr ); return traits_type::eof(); } DBG( 3, "Bytes received: ", bytesReceived ); debug::dyn_hex_table_output( 3, std::cout, bytesReceived, inputBuffer_ ); DBG( 3, "End", std::dec ); #if defined (USE_SSL) } else { while (1) { bytesReceived = SSL_read( ssl_, static_cast(inputBuffer_), SIZE * sizeof( char_type ) ); if ( 0 >= bytesReceived ) { int sslError = SSL_get_error( ssl_, bytesReceived ); switch ( sslError ) { case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: // retry continue; // Should continue in the while loop and attempt to read again break; case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_SYSCALL: case SSL_ERROR_SSL: default: Base::setg( nullptr, nullptr, nullptr ); return traits_type::eof(); } } else { // Valid read break; // out of the while loop } } } #endif // In case the number of bytes read is not the size of the buffer, we have to set // egptr to start plus the number of bytes received Base::setg( inputBuffer_, inputBuffer_, inputBuffer_ + bytesReceived ); return *inputBuffer_; } protected: CharT outputBuffer_[SIZE]; CharT inputBuffer_[SIZE]; int socketFD_; struct timeval timeout_; #if defined (USE_SSL) SSL* ssl_; #endif }; template > class basic_socketstream : public std::basic_iostream { public: using Base = std::basic_iostream; using stream_type = typename std::basic_iostream; using buf_type = basic_socketbuf<16385, CharT, Traits>; using traits_type = typename Base::traits_type; public: basic_socketstream() : stream_type( &socketBuffer_ ) {} #if defined (USE_SSL) basic_socketstream( int socketFD, SSL* ssl ) : stream_type( &socketBuffer_ ) { #else basic_socketstream( int socketFD ) : stream_type( &socketBuffer_ ) { #endif DBG( 3,"socketFD = ", socketFD ); if ( 0 == socketFD ) { DBG( 3,"Trying to set socketFD to 0 which is not allowed!" ); throw std::runtime_error( "Trying to set socketFD to 0 on basic_socketstream level which is not allowed." ); } socketBuffer_.setSocket( socketFD ); #if defined (USE_SSL) if ( nullptr != ssl ) socketBuffer_.setSSL( ssl ); else #endif { CharT ch = Base::peek(); // for SSLv2 bit 7 is set and for SSLv3 and up the first ClientHello Message is 0x16 if ( ( ch & 0x80 ) || ( ch == 0x16 ) ) { ::close( socketFD ); throw std::runtime_error( "Client tries to initiate https" ); } } } virtual ~basic_socketstream() {} public: // For clients only, servers will have to create a socketstream // by providing a socket descriptor in the constructor int open( std::string& hostname, uint16_t port ) { if ( hostname.empty() ) return -1; if ( port == 0 ) return -2; struct addrinfo* address; int retval = 0; retval = getaddrinfo( hostname.c_str(), nullptr, nullptr, &address ); if ( 0 != retval ) { perror( "getaddrinfo" ); return -3; } int sockfd = socket( address->ai_family, address->ai_socktype, address->ai_protocol ); if ( -1 == sockfd ) { freeaddrinfo( address ); return -4; } retval = connect( sockfd, address->ai_addr, address->ai_addrlen ); if ( -1 == retval ) { ::close( sockfd ); freeaddrinfo( address ); return -5; } freeaddrinfo( address ); socketBuffer_.setSocket( sockfd ); } // might be useful in the future so leaving it in // std::string getLine() { // if ( !socketBuffer_.socket() ) // throw std::runtime_error( "The socket is not or no longer open!" ); // std::string result; // CharT chr; // while( '\n' != ( chr = Base::get()) ) { // result += chr; // } // result += chr; // return result; // } void putLine( std::string& line ) { if ( !socketBuffer_.socket() ) throw std::runtime_error( "The socket is not or no longer open!" ); DBG( 3, "socketstream::putLine: putting \"", line, "\" into the socket." ); Base::write( line.c_str(), line.size() ); } void close() { const auto s = socketBuffer_.socket(); if ( 0 != s ) ::close(s); socketBuffer_.setSocket( 0 ); } protected: buf_type socketBuffer_; }; typedef basic_socketstream socketstream; typedef basic_socketstream wsocketstream; class Server { public: Server() = delete; Server( const std::string & listenIP, uint16_t port ) noexcept( false ) : listenIP_(listenIP), port_( port ) { serverSocket_ = initializeServerSocket(); SignalHandler* shi = SignalHandler::getInstance(); shi->setSocket( serverSocket_ ); shi->ignoreSignal( SIGPIPE ); // Sorry Dennis Ritchie, we do not care about this, we always check return codes shi->installHandler( SignalHandler::handleSignal, SIGTERM ); shi->installHandler( SignalHandler::handleSignal, SIGINT ); } Server( Server const & ) = delete; Server & operator = ( Server const & ) = delete; virtual ~Server() = default; public: virtual void run() = 0; private: int initializeServerSocket() { if ( port_ == 0 ) throw std::runtime_error( "Server Constructor: No port specified." ); int sockfd = ::socket( AF_INET, SOCK_STREAM, 0 ); if ( -1 == sockfd ) throw std::runtime_error( "Server Constructor: Can´t create socket" ); int retval = 0; struct sockaddr_in serv; serv.sin_family = AF_INET; serv.sin_port = htons( port_ ); if ( listenIP_.empty() ) serv.sin_addr.s_addr = INADDR_ANY; else { if ( 1 != ::inet_pton( AF_INET, listenIP_.c_str(), &(serv.sin_addr) ) ) { ::close(sockfd); throw std::runtime_error( "Server Constructor: Cannot convert IP string" ); } } socklen_t len = sizeof( struct sockaddr_in ); retval = ::bind( sockfd, reinterpret_cast(&serv), len ); if ( 0 != retval ) { ::close( sockfd ); throw std::runtime_error( std::string("Server Constructor: Cannot bind to port ") + std::to_string(port_) ); } retval = listen( sockfd, 64 ); if ( 0 != retval ) { ::close( sockfd ); throw std::runtime_error( "Server Constructor: Cannot listen on socket" ); } // Here everything should be fine, return socket fd return sockfd; } protected: std::string listenIP_; WorkQueue wq_; int serverSocket_; uint16_t port_; }; enum HTTPRequestMethod { GET = 1, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH, HTTPRequestMethod_Spare = 255 // To save some space for future methods }; enum HTTPProtocol { HTTP_0_9 = 1, HTTP_1_0, HTTP_1_1, HTTP_2_0, HTTPProtocol_Spare = 255 }; enum HTTPResponseCode { RC_100_Continue = 100, RC_101_SwitchingProtocols, RC_102_Processing, RC_200_OK = 200, RC_201_Created, RC_202_Accepted, RC_203_NonAuthorativeInformation, RC_204_NoContent, RC_205_ResetContent, RC_206_PartialContent, RC_207_MultiStatus, RC_208_AlreadyReported, RC_226_IMUsed = 226, RC_300_MultipleChoices = 300, RC_301_MovedPermanently, RC_302_Found, RC_303_SeeOther, RC_304_NotModified, RC_305_UseProxy, RC_307_TemporaryRedirect = 307, RC_308_PermanentRedirect, RC_400_BadRequest = 400, RC_401_Unauthorized, RC_402_PaymentRequired, RC_403_Forbidden, RC_404_NotFound, RC_405_MethodNotAllowed, RC_406_NotAcceptable, RC_407_ProxyAuthenticationRequired, RC_408_RequestTimeout, RC_409_Conflict, RC_410_Gone, RC_411_LengthRequired, RC_412_PreconditionFailed, RC_413_PayloadTooLarge, RC_414_RequestURITooLong, RC_415_UnsupportedMediaType, RC_416_RequestRangeNotSatisfiable, RC_417_ExpectationFailed, RC_418_ImATeapot, RC_421_MisdirectedRequest = 421, RC_422_UnprocessableEntity, RC_423_Locked, RC_424_FailedDependency, RC_426_UpgradeRequired = 426, RC_428_PreconditionRequired = 428, RC_429_TooManyRequests, RC_431_RequestHeaderFieldsTooLarge = 431, RC_444_ConnectionClosedWithoutResponse = 444, RC_451_UnavailableForLegalReasons = 451, RC_499_ClientClosedRequest = 499, RC_500_InternalServerError, RC_501_NotImplemented, RC_502_BadGateway, RC_503_ServiceUnavailable, RC_504_GatewayTimeout, RC_505_HTTPVersionNotSupported, RC_506_VariantAlsoNegotiates, RC_507_InsufficientStorage, RC_508_LoopDetected, RC_510_NotExtended = 510, RC_511_NetworkAuthenticationRequired, RC_599_NetworkConnectTimeoutError = 599, HTTPReponseCode_Spare = 1000 // Filler }; enum HTTPRequestHasBody { No = 0, Optional = 1, Required = 2 }; class HTTPMethodProperties { private: // Embedded declaration, no need for this info outside of this container class struct HTTPMethodProperty { enum HTTPRequestMethod method_; std::string methodName_; enum HTTPRequestHasBody requestHasBody_; bool responseHasBody_; }; public: static enum HTTPRequestMethod getMethodAsEnum( std::string const& rms ) { static HTTPMethodProperties props_; struct HTTPMethodProperty const& prop = props_.findProperty( rms ); return prop.method_; } static std::string const& getMethodAsString( enum HTTPRequestMethod rme ) { static HTTPMethodProperties props_; struct HTTPMethodProperty const& prop = props_.findProperty( rme ); return prop.methodName_; } static enum HTTPRequestHasBody requestHasBody( enum HTTPRequestMethod rme ) { static HTTPMethodProperties props_; struct HTTPMethodProperty const& prop = props_.findProperty( rme ); return prop.requestHasBody_; } static bool responseHasBody( enum HTTPRequestMethod rme ) { static HTTPMethodProperties props_; struct HTTPMethodProperty const& prop = props_.findProperty( rme ); return prop.responseHasBody_; } private: struct HTTPMethodProperty const& findProperty( std::string rm ) { for( auto& prop : httpMethodProperties ) if ( prop.methodName_ == rm ) return prop; throw std::runtime_error( "HTTPMethodProperties::findProperty: HTTPRequestMethod as string not found." ); } struct HTTPMethodProperty const& findProperty( enum HTTPRequestMethod rm ) { for( auto& prop : httpMethodProperties ) if ( prop.method_ == rm ) return prop; throw std::runtime_error( "HTTPMethodProperties::findProperty: HTTPRequestMethod as enum not found." ); } std::vector const httpMethodProperties = { { GET, "GET", HTTPRequestHasBody::No, true }, { HEAD, "HEAD", HTTPRequestHasBody::No, false }, { POST, "POST", HTTPRequestHasBody::Required, true }, { PUT, "PUT", HTTPRequestHasBody::Required, true }, { DELETE, "DELETE", HTTPRequestHasBody::No, true }, { CONNECT, "CONNECT", HTTPRequestHasBody::Required, true }, { OPTIONS, "OPTIONS", HTTPRequestHasBody::Optional, true }, { TRACE, "TRACE", HTTPRequestHasBody::No, true }, { PATCH, "PATCH", HTTPRequestHasBody::Required, true } }; }; enum HeaderType { ServerSet = -2, Invalid = -1, Unspecified = 0, String = 1, Integer = 2, Float = 3, Date = 4, Range = 5, True = 7, // Only allowed value is "true", all lowercase Email = 8, ETag = 9, DateOrETag = 10, Parameters = 11, Url = 12, HostPort = 13, ProtoHostPort = 14, DateOrSeconds = 15, NoCache = 16, IP = 17, Character = 18, OnOff = 19, ContainsOtherHeaders = 20, StarOrFQURL = 21, CustomHeader = 22, HeaderType_Spare = 127 // Reserving some values }; class HTTPHeaderProperties { private: struct HTTPHeaderProperty { HTTPHeaderProperty( std::string name, enum HeaderType ht, bool w = false, bool l = false, char lsc = ',' ) : name_( name ), type_( ht ), canBeWeighted_( w ), canBeAList_( l ), listSeparatorChar_( lsc ) {} std::string name_; enum HeaderType type_; bool canBeWeighted_; bool canBeAList_; char listSeparatorChar_; }; std::unordered_map> const headerTypeToString_ = { { ServerSet, "ServerSet" }, { Invalid, "Invalid" }, { Unspecified, "Unspecified" }, { String, "String" }, { Integer, "Integer" }, { Float, "Float" }, { Date, "Date" }, { Range, "Range" }, { True, "True" }, { Email, "Email" }, { ETag, "ETag" }, { DateOrETag, "DateOrETag" }, { Parameters, "Parameters" }, { Url, "Url" }, { HostPort, "HostPort" }, { ProtoHostPort, "ProtoHostPort" }, { DateOrSeconds, "DateOrSeconds" }, { NoCache, "NoCache" }, { IP, "IP" }, { Character, "Character" }, { OnOff, "OnOff" }, { ContainsOtherHeaders, "ContainsOtherHeaders" }, { StarOrFQURL, "StarOrFQURL" }, { CustomHeader, "CustomHeader" } }; public: static enum HeaderType headerType( std::string const & str ) { static HTTPHeaderProperties props; for ( auto& prop : props.httpHeaderProperties ) { if ( prop.name_ == str ) return prop.type_; } return CustomHeader; } static char listSeparatorChar( std::string const & headerName ) { static HTTPHeaderProperties props; for ( auto& prop : props.httpHeaderProperties ) { if ( prop.name_ == headerName ) return prop.listSeparatorChar_; } return ','; } static std::string const& headerTypeAsString( enum HeaderType ht ) { static HTTPHeaderProperties props; return props.headerTypeToString_.at( ht ); } private: // Contains most if not all headers from RFC2616 RFC7230 and RFC7231 // This is a mix of request and response headers! // Please add if you find that headers are missing std::vector const httpHeaderProperties = { { "Accept", HeaderType::String, true, true }, { "Accept-Charset", HeaderType::String, true, true }, { "Accept-Encoding", HeaderType::String, true, true }, { "Accept-Language", HeaderType::String, true, true }, { "Accept-Ranges", HeaderType::String, false, false }, { "Access-Control-Allow-Credentials", HeaderType::True, false, false }, { "Access-Control-Allow-Headers", HeaderType::String, false, true }, { "Access-Control-Allow-Methods", HeaderType::String, false, true }, { "Access-Control-Allow-Origin", HeaderType::StarOrFQURL, false, false }, { "Access-Control-Expose-Headers", HeaderType::String, false, true }, { "Access-Control-Max-Age", HeaderType::Integer, false, false }, { "Access-Control-Request-Headers", HeaderType::String, false, true }, { "Access-Control-Request-Method", HeaderType::String, false, false }, { "Age", HeaderType::Integer, false ,false }, { "Allow", HeaderType::String, false, true }, { "Authorization", HeaderType::String, false, false }, { "Cache-Control", HeaderType::String, false, true }, { "Connection", HeaderType::String, false, false }, { "Content-Disposition", HeaderType::String, false, false }, { "Content-Encoding", HeaderType::String, false, true }, { "Content-Language", HeaderType::String, false, true }, { "Content-Length", HeaderType::Integer, false, false }, { "Content-Location", HeaderType::Url, false, false }, { "Content-Range", HeaderType::Range, false, true }, { "Content-Security-Policy", HeaderType::String, false, false }, { "Content-Security-Policy-Report-Only", HeaderType::String, false, false }, { "Content-Type", HeaderType::String, false, false }, { "Cookie", HeaderType::Parameters, false, false }, { "Cookie2", HeaderType::String, false, false }, // Obsolete by RFC 6265 { "DNT", HeaderType::Integer, false, false }, { "Date", HeaderType::Date, false, false }, { "ETag", HeaderType::ETag, false, false }, { "Expect", HeaderType::String, false, false }, { "Expires", HeaderType::Date, false, false }, { "Forwarded", HeaderType::String, false, false }, { "From", HeaderType::Email, false, false }, { "Host", HeaderType::HostPort, false, false }, { "If-Match", HeaderType::ETag, false, true }, { "If-Modified-Since", HeaderType::Date, false, false }, { "If-None-Match", HeaderType::ETag, false, true }, { "If-Range", HeaderType::DateOrETag, false ,false }, { "If-Unmodified-Since", HeaderType::Date, false, false }, { "Keep-Alive", HeaderType::Parameters, false, true }, { "Large-Allocation", HeaderType::Integer, false, false }, // Not Standard yet { "Last-Modified", HeaderType::Date,false ,false }, { "Location", HeaderType::Url, false ,false }, { "Origin", HeaderType::ProtoHostPort, false, false }, { "Pragma", HeaderType::NoCache, false, false }, { "Proxy-Authenticate", HeaderType::String, false ,false }, { "Proxy-Authorization", HeaderType::String, false, false }, { "Public-Key-Pins", HeaderType::Parameters, false, false }, { "Public-Key-Pins-Report-Only", HeaderType::Parameters, false, false }, { "Range", HeaderType::Range, false, true }, { "Referer", HeaderType::Url, false, false }, { "Referrer-Policy", HeaderType::String, false, false }, { "Retry-After", HeaderType::DateOrSeconds, false, false }, { "Server", HeaderType::String, false, false }, { "Set-Cookie", HeaderType::Parameters, false, false }, { "Set-Cookie2", HeaderType::Parameters, false, false }, // Obsolete { "SourceMap", HeaderType::Url, false, false }, { "Strict-Transport-Security", HeaderType::Parameters, false, false }, { "TE", HeaderType::String, true, true }, { "Tk", HeaderType::Character, false, false }, { "Trailer", HeaderType::ContainsOtherHeaders, false, false }, { "Transfer-Encoding", HeaderType::String, false, true }, { "Upgrade-Insecure-Requests", HeaderType::Integer }, { "User-Agent", HeaderType::String, false, false }, { "Vary", HeaderType::String, false, true }, { "Via", HeaderType::String, false, true }, { "WWW-Authenticate", HeaderType::String, false, false }, { "Warning", HeaderType::String, false, false }, { "X-Content-Type-Options", HeaderType::String, false, false }, { "X-DNS-Prefetch-Control", HeaderType::OnOff, false, false }, { "X-Forwarded-For", HeaderType::IP, false, true }, { "X-Forwarded-Host", HeaderType::String, false, false }, { "X-Forwarded-Proto", HeaderType::String, false, false }, { "X-Frame-Options", HeaderType::String, false, false }, { "X-XSS-Protection", HeaderType::String, false, false } // { "", HeaderType:: , , }, // Default LCS is ',', no need to add it }; }; // This URL class tries to follow RFC 3986 // the updates in 6874 and 8820 are not taken into account struct URL { public: URL() : scheme_( "" ), user_( "" ), passwd_( "" ), host_( "" ), path_( "" ), fragment_( "" ), port_( 0 ), hasScheme_( false ), hasUser_ ( false ), hasPasswd_( false ), hasHost_( false ), hasPort_( false ), hasQuery_( false ), hasFragment_( false ), pathIsStar_( false ) {} URL( URL const & ) = default; ~URL() = default; URL& operator=( URL const & ) = default; private: int charToNumber( std::string::value_type c ) const { if ( 'A' <= c && 'F' >= c ) return (int)(c - 'A') + 10; if ( 'a' <= c && 'f' >= c ) return (int)(c - 'a') + 10; if ( '0' <= c && '9' >= c ) return (int)(c - '0'); std::stringstream s; s << "'" << c << "' is not a hexadecimal digit!"; throw std::runtime_error( s.str() ); } public: // Following https://en.wikipedia.org/wiki/Percent-encoding std::string percentEncode( const std::string& s ) const { std::stringstream r; for ( std::string::value_type c : s ) { // skip alpha and unreserved characters if ( isalnum( c ) || '-' == c || '_' == c || '.' == c || '~' == c ) { r << c; continue; } r << '%' << std::setw(2) << std::uppercase << std::hex << int( (unsigned char)c ) << std::nouppercase; } return r.str(); } std::string percentDecode( const std::string& s ) const { std::stringstream r; // cppcheck-suppress StlMissingComparison for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci ) { std::string::value_type c = *ci; int n = 0; if ( '%' == c ) { if ( ++ci == s.end() ) throw std::runtime_error( "Malformed URL, percent found but no next char" ); n += charToNumber(*ci); n *= 16; if ( ++ci == s.end() ) throw std::runtime_error( "Malformed URL, percent found but no next next char" ); // better error message needed :-) n += charToNumber(*ci); r << (unsigned char)n; continue; } r << c; } return r.str(); } static URL parse( std::string fullURL ) { DBG( 3, "fullURL: '", fullURL, "'" ); URL url; size_t pathBeginPos = 0; size_t pathEndPos = std::string::npos; size_t questionMarkPos = 0; size_t numberPos = 0; if ( fullURL.empty() ) { url.path_ = '/'; return url; } if ( fullURL.size() == 1 && fullURL[0] == '*' ) { url.path_ = fullURL; url.pathIsStar_ = true; return url; } questionMarkPos = fullURL.find( '?' ); numberPos = fullURL.find( '#' ); if ( fullURL[0] == '/' ) { pathBeginPos = 0; } else { // If first character is not a / then the first colon is end of scheme size_t schemeColonPos = fullURL.find( ':' ); if ( std::string::npos != schemeColonPos && 0 != schemeColonPos ) { std::string scheme; scheme = fullURL.substr( 0, schemeColonPos ); std::string validSchemeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-."; DBG( 3, "scheme: '", scheme, "'" ); if ( scheme.find_first_not_of( validSchemeChars ) != std::string::npos ) throw std::runtime_error( "Scheme contains invalid characters" ); url.scheme_ = scheme; url.hasScheme_ = true; } else throw std::runtime_error( "Does not start with / and no scheme" ); size_t authorityPos = fullURL.find( "//", schemeColonPos+1 ); size_t authorityEndPos; std::string authority; if ( std::string::npos != authorityPos ) { if ( (schemeColonPos+1) != authorityPos ) throw std::runtime_error( "Something between : and //" ); pathBeginPos = fullURL.find( '/', authorityPos+2 ); authorityEndPos = std::min( { pathBeginPos, questionMarkPos, numberPos } ); authority = fullURL.substr( authorityPos+2, authorityEndPos - (authorityPos + 2) ); DBG( 3, "authority: '", authority, "'" ); const size_t atPos = authority.find( '@' ); bool atFound = (atPos != std::string::npos); if ( atFound ) { if ( atPos == 0 ) throw std::runtime_error( "'@' found in the first column, username would be empty" ); // User (+passwd) found user : passwd @ host size_t passwdColonPos = authority.rfind( ':', atPos ); size_t userEndPos = std::string::npos; DBG( 3, "1 userEndPos '", userEndPos, "'" ); if ( passwdColonPos != std::string::npos ) { std::string passwd = authority.substr( passwdColonPos+1, atPos-(passwdColonPos+1) ); DBG( 3, "passwd: '", passwd, "', passwdColonPos: ", passwdColonPos ); userEndPos = passwdColonPos; DBG( 3, "2a userEndPos '", userEndPos, "'" ); // passwd is possibly percent encoded FIXME url.passwd_ = url.percentDecode( passwd ); url.hasPasswd_ = true; } else { userEndPos = atPos; DBG( 3, "2b userEndPos '", userEndPos, "'" ); } DBG( 3, "3 userEndPos '", userEndPos, "'" ); std::string user = authority.substr( 0, userEndPos ); DBG( 3, "user: '", user, "'" ); if ( !user.empty() ) { // user is possibly percent encoded FIXME url.user_ = url.percentDecode( user ); url.hasUser_ = true; // delete user/pass including the at authority.erase( 0, atPos+1 ); } else { throw std::runtime_error( "User not found before @ sign" ); } } // Instead of all the logic it is easier to work on substrings // authority now at most contains hostname possibly in ipv6 notation plus port bool angleBracketOpenFound = (authority[0] == '['); size_t angleBracketClosePos; bool angleBracketCloseFound = false; if ( angleBracketOpenFound ) { angleBracketClosePos = authority.find( ']', 0 ); angleBracketCloseFound = (angleBracketClosePos != std::string::npos); if ( !angleBracketCloseFound ) throw std::runtime_error( "No matching IPv6 ']' found." ); url.host_ = authority.substr( 0, angleBracketClosePos ); url.hasHost_ = true; DBG( 3, "angleBracketCloseFound: host: '", url.host_, "'" ); authority.erase( 0, angleBracketClosePos+1 ); } if ( !authority.empty() ) { // authority now at most has host and port, port is now the definitive separator // (can't be part of the ipv6 address anymore) size_t portColonPos = authority.rfind( ':' ); bool portColonFound = (portColonPos != std::string::npos); if ( portColonFound ) { if ( portColonPos == 0 && !url.hasHost_ ) throw std::runtime_error( "No hostname found" ); if ( portColonPos != 0 ) { url.host_ = authority.substr( 0, portColonPos ); DBG( 3, "portColonFound: host: '", url.host_, "'" ); url.hasHost_ = true; } size_t port = 0; std::string portString = authority.substr( portColonPos+1 ); DBG( 3, "portString: '", portString, "'" ); if ( portString.empty() ) // Use the default port number, use scheme and the /etc/services file port = 0; // FIXME else { size_t pos = 0; try { port = std::stoull( portString, &pos ); } catch ( std::invalid_argument& e ) { DBG( 3, "invalid_argument exception caught in stoull: ", e.what() ); DBG( 3, "number of characters processed: ", pos ); } catch ( std::out_of_range& e ) { DBG( 3, "out_of_range exception caught in stoull: ", e.what() ); DBG( 3, "errno: ", errno, strerror(errno) ); } } if ( port >= 65536 ) throw std::runtime_error( "URL::parse: port too large" ); url.port_ = (unsigned short)port; url.hasPort_ = true; DBG( 3, "port: ", port ); } else { url.host_ = authority; url.hasHost_ = true; DBG( 3, "portColonNotFound: host: '", url.host_, "'" ); } } else if ( !url.hasHost_ ) throw std::runtime_error( "No hostname found" ); } else { throw std::runtime_error( "// not found" ); } } pathEndPos = std::min( {questionMarkPos, numberPos} ); if ( std::string::npos != pathBeginPos ) { url.path_ = fullURL.substr( pathBeginPos, pathEndPos - pathBeginPos ); } else { url.path_ = ""; } DBG( 3, "path: '", url.path_, "'" ); if ( std::string::npos != questionMarkPos ) { // Why am i not checking numberPos for validity? std::string queryString = fullURL.substr( questionMarkPos+1, numberPos-(questionMarkPos+1) ); DBG( 3, "queryString: '", queryString, "'" ); if ( queryString.empty() ) { url.hasQuery_ = false; throw std::runtime_error( "Invalid URL: query not found after question mark" ); } else { url.hasQuery_ = true; size_t ampPos = 0; while ( !queryString.empty() ) { ampPos = queryString.find( '&' ); std::string query = queryString.substr( 0, ampPos ); DBG( 3, "query: '", query, "'" ); size_t equalsPos = query.find( '=' ); if ( std::string::npos == equalsPos ) throw std::runtime_error( "Did not find a '=' in the query" ); std::string one, two; one = url.percentDecode( query.substr( 0, equalsPos ) ); DBG( 3, "one: '", one, "'" ); two = url.percentDecode( query.substr( equalsPos+1 ) ); DBG( 3, "two: '", two, "'" ); url.arguments_.push_back( std::make_pair( one ,two ) ); // npos + 1 == 0... ouch if ( std::string::npos == ampPos ) queryString.erase( 0, ampPos ); else queryString.erase( 0, ampPos+1 ); } } } if ( std::string::npos != numberPos ) { url.hasFragment_ = true; url.fragment_ = fullURL.substr( numberPos+1 ); DBG( 3, "path: '", url.path_, "'" ); } // Now make sure the URL does not contain %xx values size_t percentPos = url.path_.find( '%' ); if ( std::string::npos != percentPos ) { // throwing an error mentioning a dev issue throw std::runtime_error( std::string("DEV: Some URL component still contains percent encoded values, please report the URL: ") + url.path_ ); } // Done! return url; } void printURL( std::ostream& os ) const { DBG( 3, "URL::printURL: debug level 3 to see more" ); std::stringstream ss; DBG( 3, " hasScheme_: ", hasScheme_, ", scheme_: ", scheme_ ); if ( hasScheme_ ) { ss << scheme_ << ':'; } DBG( 3, " hasHost_: ", hasHost_, ", host_: ", host_ ); if ( hasHost_ ) { ss << "//"; DBG( 3, " hasUser_: ", hasUser_, ", user_: ", user_ ); if ( hasUser_ ) { ss << percentEncode( user_ ); } DBG( 3, " hasPasswd_: ", hasPasswd_, ", passwd_: ", passwd_ ); if ( hasPasswd_ ) { ss << ':' << percentEncode( passwd_ ); } DBG( 3, " hasUser_: ", hasUser_, ", user_: ", user_ ); if ( hasUser_ ) { ss << '@'; } DBG( 3, " hasHost_: ", hasHost_, ", host_: ", host_ ); if ( hasHost_ ) { ss << host_; } DBG( 3, " hasPort_: ", hasPort_, ", port_: ", port_ ); if ( hasPort_ ) { ss << ':' << port_; } } DBG( 3, " path_: '", path_, "'" ); ss << (path_.empty() ? "/" : path_); if ( hasQuery_ ) { DBG( 3, " hasQuery_: ", hasQuery_ ); ss << '?'; size_t i; for ( i = 0; i < (arguments_.size()-1); ++i ) { DBG( 3, " query[", i, "]: ", arguments_[i].first, " ==> ", arguments_[i].second ); ss << percentEncode( arguments_[i].first ) << '=' << percentEncode( arguments_[i].second ) << "&"; } DBG( 3, " query[", i, "]: ", arguments_[i].first, " ==> ", arguments_[i].second ); ss << percentEncode( arguments_[i].first ) << '=' << percentEncode( arguments_[i].second ); } if ( hasFragment_ ) { DBG( 3, " hasFragment_: ", hasFragment_, ", fragment_: ", fragment_ ); ss << '#' << fragment_; } os << ss.str() << "\n"; DBG( 3, "URL::printURL: done" ); } public: std::string scheme_; std::string user_; std::string passwd_; std::string host_; std::string path_; std::string fragment_; std::vector> arguments_; unsigned short port_; bool hasScheme_; bool hasUser_; bool hasPasswd_; bool hasHost_; bool hasPort_; bool hasQuery_; bool hasFragment_; bool pathIsStar_; }; std::ostream& operator<<( std::ostream& os, URL const & url ) { url.printURL( os ); return os; } enum MimeType { CatchAll = 0, TextHTML, TextXML, TextPlain, TextPlainProm_0_0_4, ApplicationJSON, ImageXIcon, MimeType_spare = 255 }; std::unordered_map> mimeTypeMap = { { CatchAll, "*/*" }, { TextHTML, "text/html" }, { TextPlain, "text/plain" }, { TextPlainProm_0_0_4, "text/plain; version=0.0.4" }, { ImageXIcon, "image/x-icon" }, { ApplicationJSON, "application/json" } }; class HTTPHeader { public: HTTPHeader() = default; HTTPHeader( std::string n, std::string v ) : name_( n ), value_( v ) { type_ = HeaderType::ServerSet; } HTTPHeader( char const * n, char const * v ) : name_( n ), value_( v ) { type_ = HeaderType::ServerSet; } HTTPHeader( HTTPHeader const & ) = default; HTTPHeader( HTTPHeader&& ) = default; HTTPHeader& operator=( HTTPHeader const& ) = default; ~HTTPHeader() = default; public: static HTTPHeader parse( std::string& header ) { HTTPHeader hh; DBG( 3, "Raw Header : '", header, "'" ); std::string::size_type colonPos = header.find( ':' ); if ( std::string::npos == colonPos ) throw std::runtime_error( "Not a valid header, no : found" ); std::string headerName = header.substr( 0, colonPos ); std::string headerValue = header.substr( colonPos+1 ); // FIXME: possible whitespace before, between and after // Spaces in header names are illegal but be lenient und just remove them headerName.erase( std::remove ( headerName.begin(), headerName.begin() + colonPos, ' ' ), headerName.end() ); hh.name_ = headerName; hh.value_ = headerValue; hh.type_ = HTTPHeaderProperties::headerType( hh.name_ ); DBG( 3, "Headername : '", headerName, "'" ); DBG( 3, "Headervalue: '", headerValue, "'" ); DBG( 3, "HeaderType : '", HTTPHeaderProperties::headerTypeAsString(hh.type_), "'" ); if ( hh.type_ == HeaderType::Invalid ) throw std::runtime_error( "Parsing with Invalid HeaderType" ); std::string::size_type quotes = std::count( headerValue.begin(), headerValue.end(), '"' ); bool properlyQuoted = (quotes % 2 == 0); if ( !properlyQuoted ) { DBG( 3, "Parse: header not properly quoted: uneven number of quotes (", quotes, ") found" ); throw std::runtime_error( "parse header: header improperly quoted" ); } return hh; } std::string headerName() const { return name_; } // Not sure what I needed it for but leaving it for now // std::string headerValue() const { // std::cout << "Calling headerValue for HeaderName: " << name_ << "\n"; // std::cout.flush(); // std::string value; // switch ( type_ ) { // case ServerSet: // return value_; // case String: // if ( valueList_.size() > 0 ) // return valueList_[0]; // throw std::runtime_error( "headerValue(): Empty valuelist" ); // break; // case Integer: // if ( integers_.size() > 0 ) // return std::to_string( integers_[0] ); // throw std::runtime_error( "headerValue(): Empty valuelist" ); // break; // case Float: // if ( floats_.size() > 0 ) // return std::to_string( floats_[0] ); // throw std::runtime_error( "headerValue(): Empty valuelist" ); // break; // case Date: // return date_.toString(); // break; // case Range: // return ""; // break; // default: // return std::string("Not implemented yet for '") + name_ + "', type is '" + std::to_string((int)type_) + "'"; // } // } std::vector const headerValueAsList() const { return splitHeaderValue(); } void debugPrint() const { DBG( 3, "Headername: '", name_, "', Headervalue: '", value_, "'" ); } size_t headerValueAsNumber() const { size_t number = std::stoll( value_ ); return number; } double headerValueAsDouble() const { double number = std::stod( value_ ); return number; } std::string const & headerValueAsString() const { return value_; } enum MimeType headerValueAsMimeType() const { auto list = headerValueAsList(); for ( auto& item : list ) { DBG( 3, "item: '", item, "'" ); for( auto& mt : mimeTypeMap ) { DBG( 3, "comparing item: '", item, "' to '", mt.second, "'" ); if ( mt.second.compare( item ) == 0 ) { DBG( 3, "MimeType ", mt.second, " found." ); return mt.first; } } } // If we did not recognize the mimetype we will return TextHTML so the client can see the HTML page return TextHTML; } private: std::vector splitHeaderValue() const { std::vector elementList; std::stringstream ss( value_ ); std::string s; char listSeparatorChar = HTTPHeaderProperties::listSeparatorChar( name_ ); while ( ss.good() ) { std::getline( ss, s, listSeparatorChar ); // Remove leading whitespace s.erase( s.begin(), std::find_if( s.begin(), s.end(), std::bind1st( std::not_equal_to(), ' ' ) ) ); // Remove trailing whitespace s.erase( std::find_if( s.rbegin(), s.rend(), std::bind1st( std::not_equal_to(), ' ') ).base(), s.end() ); elementList.push_back( s ); } return elementList; } private: std::string name_; std::string value_; enum HeaderType type_{HeaderType::Invalid}; std::vector valueList_; std::vector floats_; std::vector integers_; std::vector> ranges_; std::vector> parameters_; datetime date_; }; class HTTPMessage { protected: HTTPMessage() = default; HTTPMessage( HTTPMessage const & ) = default; HTTPMessage & operator = ( HTTPMessage const & ) = default; ~HTTPMessage() = default; public: // Data manipulators/extractors std::string const & body() const { return body_; } void addBody( std::string const& body ) { body_ = body; } void addHeader( std::string const & name, std::string const & value ) { if ( headers_.insert( std::make_pair( name, HTTPHeader( name, value ) ) ).second == false ) { throw std::runtime_error( "Header already exists in the headerlist" ); } } void addHeader( const HTTPHeader & hh ) { if ( headers_.insert( std::make_pair( hh.headerName(), hh ) ).second == false ) { throw std::runtime_error( "Header already exists in the headerlist" ); } } bool hasHeader( std::string const & header ) const { auto pos = headers_.find( header ); if ( pos == headers_.end() ) return false; return true; } HTTPHeader const & getHeader( std::string const & header ) const { auto pos = headers_.find( header ); if ( pos == headers_.end() ) { std::stringstream ss; ss << "HTTPMessage::getHeader: Header '" << header << "' not found."; throw std::runtime_error( ss.str() ); } return (*pos).second; } std::string const & protocolAsString() const { // will throw if key not found, it is a bug anyway return protocol_map_.at(protocol_); } enum HTTPProtocol protocol() const { return protocol_; } void setProtocol( enum HTTPProtocol protocol ) { protocol_ = protocol; } void setProtocol( std::string const & protocolString ) { auto it = protocol_map_.begin(); while( it != protocol_map_.end() ) { if ( (*it).second == protocolString ) { protocol_ = (*it).first; break; } ++it; } if ( it == protocol_map_.end() ) { DBG( 3, "Protocol string '", protocolString, "' not found in map, protocol unsupported!" ); throw std::runtime_error( "Protocol not found in the map" ); } } std::string const host() const { std::string host; if ( hasHeader( "Host" ) ) { HTTPHeader host = getHeader( "Host" ); } else { DBG(3, "HTTPMessage::host: header Host not found." ); host = ""; } return host; } protected: std::string readData( socketstream& in, size_t length ) { std::string data( length, '\0' ); in.read( &data[0], length ); return data; } std::string readChunkedData( socketstream& in ) { std::string chunkHeader; std::string data; std::getline( in, chunkHeader, '\n' ); // Final header starts with 0, rest of the line is not important while ( '0' != chunkHeader[0] ) { // chunkheader: hexadecimal numbers followed by an optional semi-colon with a comment and a \r // stoll should filter all that crap out for us and return just the hexadecimal digits DBG( 3, "chunkHeader (ater check for 0): '", chunkHeader, "'" ); size_t length = std::stoll( chunkHeader, nullptr, 16 ); DBG( 3, "length: '", length, "'" ); // Initialize chunk to all zeros std::string chunk( length, '\0' ); in.read( &chunk[0], length ); DBG( 3, "chunk: '", chunk, "'" ); data += chunk; // Reads trailing \r\n from the chunk std::getline( in, chunkHeader, '\n' ); // Reads the empty line following the chunk std::getline( in, chunkHeader, '\n' ); DBG( 3, "chunkHeader (should be empty line): '", chunkHeader, "'" ); // Read a new line to check for 0\r header std::getline( in, chunkHeader, '\n' ); DBG( 3, "chunkHeader (should be next chunk header): '", chunkHeader, "'" ); } return data; } protected: enum HTTPProtocol protocol_; std::unordered_map headers_; std::string body_; std::unordered_map> protocol_map_ = { { HTTPProtocol::HTTP_0_9, "HTTP/0.9" }, { HTTPProtocol::HTTP_1_0, "HTTP/1.0" }, { HTTPProtocol::HTTP_1_1, "HTTP/1.1" }, { HTTPProtocol::HTTP_2_0, "HTTP/2.0" } }; }; class HTTPRequest : public HTTPMessage { public: HTTPRequest() : method_( HTTPRequestMethod::GET ) {} HTTPRequest( HTTPRequest const & ) = default; HTTPRequest & operator = ( HTTPRequest const & ) = default; ~HTTPRequest() = default; template friend basic_socketstream& operator>>(basic_socketstream&, HTTPRequest& ); public: enum HTTPRequestMethod method() const { return method_; } URL const & url() const { return url_; } void debugPrint() { DBG( 3, "HTTPRequest::debugPrint:" ); DBG( 3, "Method : \"", method_, "\"" ); DBG( 3, "URL : \"", url_, "\"" ); DBG( 3, "Protocol: \"", protocol_, "\"" ); for ( auto& header: headers_ ) DBG( 3, "Header : \"", header.first, "\" ==> \"", header.second.headerValueAsString(), "\"" ); DBG( 3, "Body : \"", body_, "\"" ); } private: enum HTTPRequestMethod method_; URL url_; }; class HTTPResponse : public HTTPMessage { public: HTTPResponse() : responseCode_( HTTPResponseCode::RC_200_OK ) {} HTTPResponse( HTTPResponse const & ) = default; HTTPResponse & operator = ( HTTPResponse const & ) = default; virtual ~HTTPResponse() = default; template friend basic_socketstream& operator<<(basic_socketstream&, HTTPResponse& ); public: enum HTTPResponseCode responseCode() const { return responseCode_; } std::string responseCodeAsString() const { return response_map_.at( responseCode_ ); } void setResponseCode( enum HTTPResponseCode rc ) { DBG( 3, "Setting response code to: '", std::dec, (int)rc, "'" ); responseCode_ = rc; } void debugPrint() { DBG( 3, "HTTPReponse::debugPrint:" ); DBG( 3, "Response Code: \"", (int)responseCode_, "\"" ); for ( auto& header: headers_ ) DBG( 3, "Header: \"", header.first, "\" ==> \"", header.second.headerValueAsString(), "\"" ); DBG( 3, "Body: \"", body_, "\"" ); } void createResponse( enum MimeType mimeType, std::string body, enum HTTPResponseCode rc ) { // mimetype validity checking? addHeader( HTTPHeader( "Content-Type", mimeTypeMap[mimeType] ) ); addHeader( HTTPHeader( "Content-Length", std::to_string( body.size() ) ) ); addBody( body ); setResponseCode( rc ); } private: enum HTTPResponseCode responseCode_; std::unordered_map> response_map_ = { { RC_100_Continue, "Continue" }, { RC_101_SwitchingProtocols, "Switching Protocols" }, { RC_102_Processing, "Processing" }, { RC_200_OK, "OK" }, { RC_201_Created, "Created" }, { RC_202_Accepted, "Accepted" }, { RC_203_NonAuthorativeInformation, "Non-authorative Information" }, { RC_204_NoContent, "No Content" }, { RC_205_ResetContent, "Reset Content" }, { RC_206_PartialContent, "Partial Content" }, { RC_207_MultiStatus, "Multi-Status" }, { RC_208_AlreadyReported, "Already Reported" }, { RC_226_IMUsed, "IM Used" }, { RC_300_MultipleChoices, "Multiple Choices" }, { RC_301_MovedPermanently, "Moved Permanently" }, { RC_302_Found, "Found" }, { RC_303_SeeOther, "See Other" }, { RC_304_NotModified, "Not Modified" }, { RC_305_UseProxy, "Use Proxy" }, { RC_307_TemporaryRedirect, "Temporary Redirect" }, { RC_308_PermanentRedirect, "Permanent Redirect" }, { RC_400_BadRequest, "Bad Request" }, { RC_401_Unauthorized, "Unauthorized" }, { RC_402_PaymentRequired, "Payment Required" }, { RC_403_Forbidden, "Forbidden" }, { RC_404_NotFound, "Not Found" }, { RC_405_MethodNotAllowed, "Method Not Allowed" }, { RC_406_NotAcceptable, "Not Acceptable" }, { RC_407_ProxyAuthenticationRequired, "Proxy Authentication Required" }, { RC_408_RequestTimeout, "Request Timeout" }, { RC_409_Conflict, "Conflict" }, { RC_410_Gone, "Gone" }, { RC_411_LengthRequired, "Length Required" }, { RC_412_PreconditionFailed, "Precondition Failed" }, { RC_413_PayloadTooLarge, "Payload Too Large" }, { RC_414_RequestURITooLong, "Request-URI Too Long" }, { RC_415_UnsupportedMediaType, "Unsupported Media Type" }, { RC_416_RequestRangeNotSatisfiable, "Request Range Not Satisfiable" }, { RC_417_ExpectationFailed, "Expectation Failed" }, { RC_418_ImATeapot, "I'm a teapot" }, { RC_421_MisdirectedRequest, "Misdirected Request" }, { RC_422_UnprocessableEntity, "Unprocessable Entity" }, { RC_423_Locked, "Locked" }, { RC_424_FailedDependency, "Failed Dependency" }, { RC_426_UpgradeRequired, "Upgrade Required" }, { RC_428_PreconditionRequired, "Precondition Required" }, { RC_429_TooManyRequests, "Too Many Requests" }, { RC_431_RequestHeaderFieldsTooLarge, "Request Header Fields Too Large" }, { RC_444_ConnectionClosedWithoutResponse, "Connection Closed Without Response" }, { RC_451_UnavailableForLegalReasons, "Unavailable For Legal Reasons" }, { RC_499_ClientClosedRequest, "Client Closed Request" }, { RC_500_InternalServerError, "Internal Server Error" }, { RC_501_NotImplemented, "Not Implemented" }, { RC_502_BadGateway, "Bad Gateway" }, { RC_503_ServiceUnavailable, "Service Unavailable" }, { RC_504_GatewayTimeout, "Gateway Timeout" }, { RC_505_HTTPVersionNotSupported, "HTTP Version Not Supported" }, { RC_506_VariantAlsoNegotiates, "Variant Also Negotiates" }, { RC_507_InsufficientStorage, "Insufficient Storage" }, { RC_508_LoopDetected, "Loop Detected" }, { RC_510_NotExtended, "Not Extended" }, { RC_511_NetworkAuthenticationRequired, "Network Authentication Required" }, { RC_599_NetworkConnectTimeoutError, "Network Connect Timeout Error" } }; }; // Compress linear white space and remove carriage return, not new line, this one is gone already std::string& compressLWSAndRemoveCR( std::string& line ) { std::string::size_type pos = 0, end = line.size(), start = 0; for ( pos = 0; pos < end; ++pos ) { start = pos; if ( ::isspace( line[pos] ) ) { while ( (pos+1) < line.size() && ::isspace( line[++pos] ) ) { } if ( (pos - start) > 1 ) { line.erase( start+1, pos-start-1 ); end -= pos-start-1; pos = start+1; } } } // Remove trailing '\r' if ( line[line.size()-1] == '\r' ) line.pop_back(); return line; } template basic_socketstream& operator>>( basic_socketstream& rs, HTTPRequest& m ) { DBG( 3, "Reading from the socket" ); std::string method, url, protocol; rs >> method >> url >> protocol; if ( rs.fail() ) { DBG( 5, "Could not read from socket, might have been closed due to e.g. timeout" ); throw std::runtime_error( "Could not read from socket, might have been closed due to e.g. timeout" ); } m.method_ = HTTPMethodProperties::getMethodAsEnum( method ); m.url_ = URL::parse( url ); m.setProtocol( protocol ); //m.debugPrint(); // ignore the '\n' after the protocol rs.ignore( std::numeric_limits::max(), '\n' ); std::string line; std::string concatLine; while ( true ) { std::getline( rs, line ); DBG( 3, "Line with whitespace: '", line, "'" ); concatLine += compressLWSAndRemoveCR( line ); DBG( 3, "Line without whitespace: '", line, "'" ); DBG( 3, "ConcatLine: '", concatLine, "'" ); // empty line is separator between headers and body if ( concatLine.empty() ) { break; } // Header spans multiple lines if a line starts with SP or HTAB, fetch another line and append to concatLine if ( rs.peek() == ' ' || rs.peek() == '\t' ) continue; HTTPHeader hh; hh = HTTPHeader::parse( concatLine ); hh.debugPrint(); m.addHeader( hh ); // Parsing of header done, clear concatLine to start fresh concatLine.clear(); } DBG( 3, "Done parsing headers" ); enum HTTPRequestHasBody hasBody = HTTPMethodProperties::requestHasBody( m.method_ ); DBG( 3, "Request has Body (0 No, 1 Optional, 2 Yes): ", (int)hasBody ); if ( hasBody != HTTPRequestHasBody::No ) { // this mess of code checks if the body is chunked or regular and tests the pre conditions // that belong with them either content-length header or transfer-encoding header, both // means bad request, in case neither is there we need to check if body is optional bool validCL = false; size_t contentLength = 0; bool chunkedTE = false; std::string body( "" ); // cl = Content Length if ( m.hasHeader( "Content-Length" ) ) { HTTPHeader const h = m.getHeader( "Content-Length" ); contentLength = h.headerValueAsNumber(); validCL = true; DBG( 3, "Content-Length: clValue: ", contentLength, ", validCL: ", validCL ); } else { validCL = false; DBG( 3, "Content-Length: header not found." ); } // te = Transfer Encoding if ( m.hasHeader( "Transfer-Encoding" ) ) { HTTPHeader const h = m.getHeader( "Transfer-Encoding" ); std::string teString = h.headerValueAsString(); // Validate header if ( teString.find( "chunked" ) != std::string::npos ) { chunkedTE = true; } else { chunkedTE = false; } DBG( 3, "Transfer-Encoding: teString: ", teString, ", chunkedTE: ", chunkedTE ); } else { DBG( 3, "Transfer-Encoding: header not found " ); chunkedTE = false; } size_t trailerLength = 0; if ( m.hasHeader( "Trailer" ) ) { HTTPHeader const trailer = m.getHeader( "Trailer" ); trailerLength = trailer.headerValueAsList().size(); } else { DBG( 3, "Trailer: header not found " ); } if ( ( chunkedTE && !validCL ) || ( !chunkedTE && validCL ) ) { DBG( 3, "Good request" ); // Good request, get body // but first check if the client sent the Expect header, if so we // need to respond with 100 Continue so it starts transmitting the body std::string expect( "" ); if ( m.hasHeader( "Expect" ) ) { HTTPHeader const h = m.getHeader( "Expect" ); expect = h.headerValueAsString(); } else { expect = ""; } if ( expect == "100-continue" ) { // We have to send a HTTP/1.1 100 Continue response followed by an empty line HTTPResponse resp; resp.setProtocol( HTTPProtocol::HTTP_1_1 ); resp.setResponseCode( HTTPResponseCode::RC_100_Continue ); rs << resp; } else if ( expect != "" ) throw std::runtime_error( "Not a valid Expect header" ); // now load the body if ( chunkedTE ) { m.body_ = m.readChunkedData( rs ); // There is now either a \r\n pair in the stream, or footers/trailers, lets see: std::string remainder; size_t numHeadersAdded = 0; std::getline( rs, remainder, '\n' ); DBG( 3, "Parsing remainder '", remainder, "'" ); while ( remainder[0] != '\r' ) { HTTPHeader hh = HTTPHeader::parse( remainder ); m.addHeader( hh ); ++numHeadersAdded; } // If trailer contains 3 headers then 3 headers should be added if ( numHeadersAdded != trailerLength ) throw std::runtime_error( "Trailing headers does not match Trailer header content" ); } else { body = m.readData( rs, contentLength ); } } else if ( hasBody == HTTPRequestHasBody::Optional && ! validCL && !chunkedTE ){ // Good request, no body, done return rs; } else { // Bad request, respond, throw exception HTTPResponse resp; resp.setProtocol( HTTPProtocol::HTTP_1_1 ); resp.setResponseCode( HTTPResponseCode::RC_400_BadRequest ); rs << resp; throw std::runtime_error( "Bad Request received" ); } } return rs; } template basic_socketstream& operator<<( basic_socketstream& ws, HTTPResponse& m ) { DBG( 3, "Writing the HTTPResponse to the socket" ); m.debugPrint(); DBG( 3, m.protocolAsString(), " ", (int)m.responseCode(), " ", m.responseCodeAsString() ); ws << m.protocolAsString() << " " << (int)m.responseCode() << " " << m.responseCodeAsString() << HTTP_EOL; DBG( 3, "Headers:" ); // write headers for( auto& header : m.headers_ ) { DBG( 3, header.first, ": ", header.second.headerValueAsString() ); if ( header.first == "Content-Type" ) ws << header.first << ": " << header.second.headerValueAsString() << "; charset=UTF-8" << HTTP_EOL; else ws << header.first << ": " << header.second.headerValueAsString() << HTTP_EOL; } ws << HTTP_EOL; DBG( 3, "Body:", m.body() ); ws << m.body(); ws.flush(); return ws; } typedef void (*http_callback)( HTTPServer *, HTTPRequest const &, HTTPResponse & ); class HTTPConnection : public Work { public: HTTPConnection() = delete; #if defined (USE_SSL) HTTPConnection( HTTPServer* hs, int socketFD, struct sockaddr_in clientAddr, std::vector const & cl, SSL* ssl = nullptr ) : hs_( hs ), socketStream_( socketFD, ssl ), clientAddress_( clientAddr ), callbackList_( cl ) {} #else HTTPConnection( HTTPServer* hs, int socketFD, struct sockaddr_in clientAddr, std::vector const & cl ) : hs_( hs ), socketStream_( socketFD ), clientAddress_( clientAddr ), callbackList_( cl ) {} #endif HTTPConnection( HTTPConnection const & ) = delete; void operator=( HTTPConnection const & ) = delete; ~HTTPConnection() = default; public: void close() { socketStream_.close(); } virtual void execute() override { bool keepListening = false; int numRequests = 0; do { HTTPRequest request; HTTPResponse response; try { socketStream_ >> request; } catch( std::exception& e ) { DBG( 3, "Reading request from socket: Exception caught: ", e.what(), "\n" ); break; } ++numRequests; // Debug: // request.debugPrint(); response.setProtocol( request.protocol() ); // Check for protocol conformity if ( request.protocol() == HTTPProtocol::HTTP_1_1 ) { if ( ! request.hasHeader( "Host" ) ) { DBG( 3, "Mandatory Host header not found." ); std::string body( "400 Bad Request. HTTP 1.1: Mandatory Host header is missing." ); response.createResponse( TextPlain, body, RC_400_BadRequest ); return; } } // Do processing of the request here if (*callbackList_[request.method()]) (*callbackList_[request.method()])( hs_, request, response ); else { std::string body( "501 Not Implemented." ); body += " Method \"" + HTTPMethodProperties::getMethodAsString(request.method()) + "\" is not implemented (yet)."; response.createResponse( TextPlain, body, RC_501_NotImplemented ); } // Post-processing, adding some server specific response headers int const requestLimit = 100; int const connectionTimeout = 10; response.addHeader( HTTPHeader( "Server", std::string( "PCMWebServer " ) + PCMWebServerVersion ) ); response.addHeader( HTTPHeader( "Date", datetime().toString() ) ); if ( numRequests < requestLimit ) { std::string connection; if ( request.hasHeader( "Connection" ) ) { HTTPHeader const h = request.getHeader( "Connection" ); connection = h.headerValueAsString(); } else { DBG( 3, "Connection: header not found" ); connection = ""; } // FIXME: case insensitive compare if ( connection == "keep-alive" ) { DBG( 3, "HTTPConnection::execute: keep-alive header found" ); response.addHeader( HTTPHeader( "Connection", "keep-alive" ) ); std::string tmp = "timeout=" + std::to_string(connectionTimeout) + ", max=" + std::to_string( requestLimit ); HTTPHeader header2( "Keep-Alive", tmp ); response.addHeader( header2 ); keepListening = true; } } else { DBG( 3, "Keep-Alive connection request limit (", requestLimit, ") reached" ); // Now respond with the answer response.addHeader( HTTPHeader( "Connection", "close" ) ); keepListening = false; } // Remove body if method is HEAD, it is using the same callback as GET but does not need the body if ( request.method() == HEAD ) { DBG( 1, "Method HEAD, removing body" ); response.addBody( "" ); } response.debugPrint(); socketStream_ << response; socketStream_.flush(); } while ( !keepListening ); close(); } private: HTTPServer* hs_; socketstream socketStream_; struct sockaddr_in clientAddress_; std::vector const & callbackList_; std::vector responseHeader_; std::string responseBody_; std::string protocol_; }; class PeriodicCounterFetcher : public Work { public: PeriodicCounterFetcher( HTTPServer* hs ) : hs_(hs), run_(false), exit_(false) {} virtual ~PeriodicCounterFetcher() override {} void start( void ) { DBG( 3, "PeriodicCounterFetcher::start() called" ); run_ = true; } void pause( void ) { DBG( 3, "PeriodicCounterFetcher::pause() called" ); run_ = false; } void stop( void ) { DBG( 3, "PeriodicCounterFetcher::stop() called" ); exit_ = true; } virtual void execute() override; private: HTTPServer* hs_; std::atomic run_; std::atomic exit_; }; class HTTPServer : public Server { public: HTTPServer() : Server( "", 80 ) { DBG( 3, "HTTPServer::HTTPServer()" ); callbackList_.resize( 256 ); createPeriodicCounterFetcher(); pcf_->start(); SignalHandler::getInstance()->setHTTPServer( this ); } HTTPServer( std::string const & ip, uint16_t port ) : Server( ip, port ) { DBG( 3, "HTTPServer::HTTPServer( ip=", ip, ", port=", port, " )" ); callbackList_.resize( 256 ); createPeriodicCounterFetcher(); pcf_->start(); SignalHandler::getInstance()->setHTTPServer( this ); } HTTPServer( HTTPServer const & ) = delete; HTTPServer & operator = ( HTTPServer const & ) = delete; virtual ~HTTPServer() { pcf_->stop(); std::this_thread::sleep_for( std::chrono::seconds(1) ); delete pcf_; } public: virtual void run() override; virtual void stop() { pcf_->stop(); } // Register Callbacks void registerCallback( HTTPRequestMethod rm, http_callback hc ) { callbackList_[rm] = hc; } void unregisterCallback( HTTPRequestMethod rm ) { callbackList_[rm] = nullptr; } void addAggregator( std::shared_ptr agp ) { DBG( 3, "HTTPServer::addAggregator( agp=", std::hex, agp.get(), " ) called" ); agVectorMutex_.lock(); agVector_.insert( agVector_.begin(), agp ); if ( agVector_.size() > 30 ) { DBG( 3, "HTTPServer::addAggregator(): Removing last Aggegator" ); agVector_.pop_back(); } agVectorMutex_.unlock(); } std::pair,std::shared_ptr> getAggregators( size_t index, size_t index2 ) { if ( index == index2 ) throw std::runtime_error("BUG: getAggregator: both indices are equal. Fix the code!" ); // simply wait until we have enough samples to return while( agVector_.size() < ( std::max( index, index2 ) + 1 ) ) std::this_thread::sleep_for(std::chrono::seconds(1)); agVectorMutex_.lock(); auto ret = std::make_pair( agVector_[ index ], agVector_[ index2 ] ); agVectorMutex_.unlock(); return ret; } private: void createPeriodicCounterFetcher() { pcf_ = new PeriodicCounterFetcher( this ); wq_.addWork( pcf_ ); pcf_->start(); } protected: std::vector callbackList_; std::vector> agVector_; std::mutex agVectorMutex_; PeriodicCounterFetcher* pcf_; }; // Here to break dependency on HTTPServer void SignalHandler::handleSignal( int signum ) { // Clean up, close socket and such std::cerr << "handleSignal: signal " << signum << " caught.\n"; std::cerr << "handleSignal: closing socket " << networkSocket_ << "\n"; ::close( networkSocket_ ); std::cerr << "Stopping HTTPServer\n"; httpServer_->stop(); std::cerr << "Cleaning up PMU:\n"; PCM::getInstance()->cleanup(); std::cerr << "handleSignal: exiting with exit code 1...\n"; exit(1); } void PeriodicCounterFetcher::execute() { using namespace std::chrono; system_clock::time_point now = system_clock::now(); now = now + std::chrono::seconds(1); std::this_thread::sleep_until( now ); while( 1 ) { if ( exit_ ) break; if ( run_ ) { auto before = steady_clock::now(); // create an aggregator std::shared_ptr sagp = std::make_shared(); assert(sagp.get()); DBG( 2, "PCF::execute(): AGP=", sagp.get(), " )" ); // dispatch it sagp->dispatch( PCM::getInstance()->getSystemTopology() ); // add it to the vector hs_->addAggregator( sagp ); auto after = steady_clock::now(); auto elapsed = duration_cast(after - before); DBG( 2, "Aggregation Duration: ", elapsed.count(), "ms." ); } now = now + std::chrono::seconds(1); std::this_thread::sleep_until( now ); } } void HTTPServer::run() { struct sockaddr_in clientAddress; clientAddress.sin_family = AF_INET; int clientSocketFD = 0; while (1) { // Listen on socket for incoming requests socklen_t sa_len = sizeof( struct sockaddr_in ); int retval = ::accept( serverSocket_, (struct sockaddr*)&clientAddress, &sa_len ); if ( -1 == retval ) { std::cerr << ::strerror( errno ) << "\n"; continue; } clientSocketFD = retval; // Client connected, let's determine the client ip as string. char ipbuf[INET_ADDRSTRLEN]; std::fill(ipbuf, ipbuf + INET_ADDRSTRLEN, 0); char const * resbuf = ::inet_ntop( AF_INET, &(clientAddress.sin_addr), ipbuf, INET_ADDRSTRLEN ); if ( nullptr == resbuf ) { std::cerr << ::strerror( errno ) << "\n"; ::close( clientSocketFD ); continue; } int port = ntohs( clientAddress.sin_port ); DBG( 3, "Client IP is: ", ipbuf, ", and the port it uses is : ", port ); HTTPConnection* connection = nullptr; try { connection = new HTTPConnection( this, clientSocketFD, clientAddress, callbackList_ ); } catch ( std::exception& e ) { DBG( 3, "Exception caught while creating a HTTPConnection: " ); if (connection) delete connection; ::close( clientSocketFD ); continue; } wq_.addWork( connection ); } } #if defined (USE_SSL) class HTTPSServer : public HTTPServer { public: HTTPSServer() : HTTPServer( "", 443 ) {} HTTPSServer( std::string const & ip, uint16_t port ) : HTTPServer( ip, port ), sslCTX_( nullptr ) {} HTTPSServer( HTTPSServer const & ) = delete; virtual ~HTTPSServer() = default; public: virtual void run() final; public: void setPrivateKeyFile ( std::string const & privateKeyFile ) { privateKeyFile_ = privateKeyFile; } void setCertificateFile( std::string const & certificateFile ) { certificateFile_ = certificateFile; } void initialiseSSL() { if ( nullptr != sslCTX_ ) throw std::runtime_error( "HTTPSServer SSL already initialised" ); if ( privateKeyFile_.empty() ) throw std::runtime_error( "No private key file given" ); if ( certificateFile_.empty() ) throw std::runtime_error( "No certificate file given" ); SSL_library_init(); SSL_load_error_strings(); // SSL too old on development machine, not available yet FIXME //OPENSSL_config(nullptr); sslCTX_ = SSL_CTX_new( SSLv23_method() ); if ( nullptr == sslCTX_ ) throw std::runtime_error( "Cannot create an SSL context" ); if ( SSL_CTX_use_certificate_file( sslCTX_, certificateFile_.c_str(), SSL_FILETYPE_PEM ) <= 0 ) throw std::runtime_error( "Cannot use certificate file" ); if ( SSL_CTX_use_PrivateKey_file( sslCTX_, privateKeyFile_.c_str(), SSL_FILETYPE_PEM ) <= 0 ) throw std::runtime_error( "Cannot use private key file" ); } private: SSL_CTX* sslCTX_ = nullptr; std::string certificateFile_; std::string privateKeyFile_; }; void HTTPSServer::run() { struct sockaddr_in clientAddress; clientAddress.sin_family = AF_INET; int clientSocketFD = 0; // Check SSL CTX for validity if ( nullptr == sslCTX_ ) throw std::runtime_error( "No SSL_CTX created" ); while (1) { // Listen on socket for incoming requests, same as for regular connection socklen_t sa_len = sizeof( struct sockaddr_in ); int retval = ::accept( serverSocket_, (struct sockaddr*)&clientAddress, &sa_len ); if ( -1 == retval ) { std::cerr << strerror( errno ) << "\n"; continue; } clientSocketFD = retval; // Create and setup SSL on the socket SSL* ssl = SSL_new( sslCTX_ ); SSL_set_fd( ssl, clientSocketFD ); // Check if the SSL handshake worked if ( SSL_accept( ssl ) <= 0 ) throw std::runtime_error( "SSL handshake failure" ); // Client connected, let's determine the client ip as string. char ipbuf[INET_ADDRSTRLEN]; memset( ipbuf, 0, 16 ); char const * resbuf = ::inet_ntop( AF_INET, &(clientAddress.sin_addr), ipbuf, INET_ADDRSTRLEN ); if ( nullptr == resbuf ) { std::cerr << strerror( errno ) << "\n"; ::close( clientSocketFD ); continue; } int port = ntohs( clientAddress.sin_port ); DBG( 3, "Client IP is: ", ipbuf, ", and the port it uses is : ", port ); DBG( 3, "SSL info: version: ", SSL_get_version( ssl ), ", stuff" ); HTTPConnection* connection = new HTTPConnection( this, clientSocketFD, clientAddress, callbackList_, ssl ); wq_.addWork( connection ); } } #endif // USE_SSL // Hack needed to convert from unsigned char to signed char, favicon.h is changed to have _uc for each char inline constexpr signed char operator "" _uc( unsigned long long arg ) noexcept { return static_cast(arg); } #include "favicon.ico.h" std::pair,std::shared_ptr> getNullAndCurrentAggregator() { std::shared_ptr current = std::make_shared(); std::shared_ptr null = std::make_shared(); assert(current.get()); current->dispatch( PCM::getInstance()->getSystemTopology() ); return std::make_pair( null, current ); } enum OutputFormat { Prometheus_0_0_4 = 1, JSON, HTML, XML, PlainText, OutputFormat_Spare = 255 }; std::unordered_map> mimeTypeToOutputFormat = { { TextHTML, HTML }, { TextXML, XML }, { ApplicationJSON, JSON }, { TextPlainProm_0_0_4, Prometheus_0_0_4 }, { CatchAll, HTML } }; std::unordered_map> supportedOutputMimeTypes = { { TextPlainProm_0_0_4, "text/plain;version=0.0.4" }, { ApplicationJSON, "application/json" } }; enum MimeType matchSupportedWithAcceptedMimeTypes( HTTPHeader const& h ) { auto list = h.headerValueAsList(); // TODO: We should actually build up a list of accepted mimetypes and their preference, sort // the list and then compare against it. We now use the inherent order as preference which // is not entirely accurate but is good enough. for ( auto& item : list ) { DBG( 2, "Item: \"", item, "\"" ); // Search for preference and remove it auto copy = item; size_t pos; // Using erase with npos as second parameter to be explicit about the intent: delete until end if ( std::string::npos != ( pos = item.find( "q=", 0 ) ) ){ // found it, remove q=... copy.erase( pos, std::string::npos ); DBG( 2, "q= found and erased: \"", copy, "\"" ); if ( std::string::npos != ( pos = item.rfind( ";", pos ) ) ) { // remove trailing ; copy.erase( pos, std::string::npos ); DBG( 2, "trailing ';' found and erased: \"", copy, "\"" ); } } // remove all whitespace from the item copy.erase( std::remove_if( copy.begin(), copy.end(), isspace ), copy.end() ); // compare mimetype with supported ones for ( auto& mimetype : supportedOutputMimeTypes ) { auto str = mimetype.second; str.erase( std::remove_if( str.begin(), str.end(), isspace ), str.end() ); DBG( 2, "Comparing mimetype '", copy, "' with known Mimetype '", str, "'" ); if ( str == copy ) { DBG( 2, "Found a match!" ); return mimetype.first; } } } return CatchAll; } /* Normally the Accept Header decides what format is returned but certain endpoints can override this, * therefore we have a separate enum for output format */ void my_get_callback( HTTPServer* hs, HTTPRequest const & req, HTTPResponse & resp ) { enum MimeType mt; enum OutputFormat format; HTTPHeader accept; if ( req.hasHeader( "Accept" ) ) { accept = req.getHeader( "Accept" ); mt = matchSupportedWithAcceptedMimeTypes( accept ); } else { // If there is no accept header then the assumption is that the client can handle anything mt = CatchAll; } format = mimeTypeToOutputFormat[ mt ]; URL url; url = req.url(); DBG( 3, "PATH=\"", url.path_, "\", size=", url.path_.size() ); if ( url.path_ == "/favicon.ico" ) { DBG( 3, "my_get_callback: client requesting '/favicon.ico'" ); std::string favicon( favicon_ico, favicon_ico + favicon_ico_len ); resp.createResponse( ImageXIcon, favicon, RC_200_OK ); return; } std::pair,std::shared_ptr> aggregatorPair; if ( (1 == url.path_.size()) && (url.path_ == "/") ) { DBG( 3, "my_get_callback: client requesting '/'" ); // If it is not Prometheus and not JSON just return this html code // It might violate the protocol but it makes coding this easier if ( ApplicationJSON != mt && TextPlainProm_0_0_4 != mt ) { // If you make changes to the HTML, please validate it // Probably best to put this in static files and serve this std::string body = "\ \n\ \n\ \n\ PCM Sensor Server\n\ \n\ \n\

PCM Sensor Server

\n\

PCM Sensor Server provides performance counter data through an HTTP interface. By default this text is served when requesting the endpoint \"/\".

\n\

The endpoints for retrieving counter data, /, /persecond and /persecond/X, support returning data in JSON or prometheus format. For JSON have your client send the HTTP header \"Accept: application/json\" and for prometheus \"Accept: text/plain; version=0.0.4\" along with the request, PCM Sensor Server will then return the counter data in the requested format.

\n\

Endpoints you can call are:

\n\
    \n\
  • / : This will fetch the counter values since start of the daemon, minus overflow so should be considered absolute numbers and should be used for further processing by yourself.
  • \n\
  • /persecond : This will fetch data from the internal sample thread which samples every second and returns the difference between the last 2 samples.
  • \n\
  • /persecond/X : This will fetch data from the internal sample thread which samples every second and returns the difference between the last 2 samples which are X seconds apart. X can be at most 30 seconds without changing the source code.
  • \n\
  • /metrics : The Prometheus server does not send an Accept header to decide what format to return so it got its own endpoint that will always return data in the Prometheus format. pcm-sensor-server is sending the header \"Content-Type: text/plain; version=0.0.4\" as required. This /metrics endpoints mimics the same behavior as / and data is thus absolute, not relative.
  • \n\
  • /dashboard/influxdb : This will return JSON for a Grafana dashboard with InfluxDB backend that holds all counters. Please see the documentation for more information.
  • \n\
  • /dashboard/prometheus : This will return JSON for a Grafana dashboard with Prometheus backend that holds all counters. Please see the documentation for more information.
  • \n\
  • /dashboard/prometheus/default : Same as /dashboard/prometheus but tuned for existing installations with default Prometheus scrape period of 15 seconds and the rate of 1 minute in Grafana. Please see the documentation for more information.
  • \n\
  • /dashboard : same as /dashboard/influxdb
  • \n\
  • /favicon.ico : This will return a small favicon.ico as requested by many browsers.
  • \n\
\n\ \n\ \n"; resp.createResponse( TextHTML, body, RC_200_OK ); return; } //std::shared_ptr current; //std::shared_ptr null; //current = std::make_shared(); //null = std::make_shared(); //current->dispatch( PCM::getInstance()->getSystemTopology() ); //aggregatorPair = std::make_pair( null, current ); aggregatorPair = getNullAndCurrentAggregator(); } else if ( url.path_ == "/dashboard" || url.path_ == "/dashboard/influxdb") { DBG( 3, "client requesting /dashboard path: '", url.path_, "'" ); resp.createResponse( ApplicationJSON, getPCMDashboardJSON(InfluxDB), RC_200_OK ); return; } else if (url.path_ == "/dashboard/prometheus") { DBG(3, "client requesting /dashboard path: '", url.path_, "'"); resp.createResponse(ApplicationJSON, getPCMDashboardJSON(Prometheus), RC_200_OK); return; } else if (url.path_ == "/dashboard/prometheus/default") { DBG(3, "client requesting /dashboard path: '", url.path_, "'"); resp.createResponse(ApplicationJSON, getPCMDashboardJSON(Prometheus_Default), RC_200_OK); return; } else if ( 0 == url.path_.rfind( "/persecond", 0 ) ) { DBG( 3, "client requesting /persecond path: '", url.path_, "'" ); if ( 10 == url.path_.size() || ( 11 == url.path_.size() && url.path_.at(10) == '/' ) ) { DBG( 3, "size == 10 or 11" ); // path looks like /persecond or /persecond/ aggregatorPair = hs->getAggregators( 1, 0 ); } else { DBG( 3, "size > 11: size = ", url.path_.size() ); // We're looking for value X after /persecond/X and possibly a trailing / anything else not url.path_.erase( 0, 10 ); // remove /persecond DBG( 3, "after removal: path = \"", url.path_, "\", size = ", url.path_.size() ); if ( url.path_.at(0) == '/' ) { url.path_.erase( 0, 1 ); if ( url.path_.at( url.path_.size() - 1 ) == '/' ) { url.path_.pop_back(); } if ( std::all_of( url.path_.begin(), url.path_.end(), ::isdigit ) ) { size_t seconds; try { seconds = std::stoll( url.path_ ); } catch ( std::exception& e ) { DBG( 3, "Error during conversion of /persecond/ seconds: ", e.what() ); seconds = 0; } if ( 1 <= seconds && 30 >= seconds ) { aggregatorPair = hs->getAggregators( seconds, 0 ); } else { DBG( 3, "seconds == 0 or seconds >= 30, not allowed" ); std::string body( "400 Bad Request. seconds == 0 or seconds >= 30, not allowed" ); resp.createResponse( TextPlain, body, RC_400_BadRequest ); return; } } else { DBG( 3, "/persecond/ Not followed by all numbers" ); std::string body( "400 Bad Request Request starts with /persecond/ but is not followed by numbers only." ); resp.createResponse( TextPlain, body, RC_400_BadRequest ); return; } } else { DBG( 3, "/persecond something requested: something=\"", url.path_, "\"" ); std::string body( "404 Bad Request. Request starts with /persecond but contains bad characters." ); resp.createResponse( TextPlain, body, RC_404_NotFound ); return; } } } else if ( 8 == url.path_.size() && 0 == url.path_.find( "/metrics", 0 ) ) { DBG( 3, "Special snowflake prometheus wants a /metrics URL, it can't be bothered to use its own mimetype in the Accept header" ); format = Prometheus_0_0_4; aggregatorPair = getNullAndCurrentAggregator(); } else { DBG( 3, "Unknown path requested: \"", url.path_, "\"" ); std::string body( "404 Unknown path." ); resp.createResponse( TextPlain, body, RC_404_NotFound ); return; } switch ( format ) { case JSON: { JSONPrinter jp( aggregatorPair ); jp.dispatch( PCM::getInstance()->getSystemTopology() ); resp.createResponse( ApplicationJSON, jp.str(), RC_200_OK ); break; } case Prometheus_0_0_4: { PrometheusPrinter pp( aggregatorPair ); pp.dispatch( PCM::getInstance()->getSystemTopology() ); resp.createResponse( TextPlainProm_0_0_4, pp.str(), RC_200_OK ); break; } default: std::string body( "406 Not Acceptable. Server can only serve \"" ); body += req.url().path_ + "\" as application/json, \"text/plain; version=0.0.4\" (prometheus format)."; resp.createResponse( TextPlain, body, RC_406_NotAcceptable ); } } int startHTTPServer( unsigned short port ) { HTTPServer server( "", port ); // HEAD is GET without body, we will remove the body in execute() server.registerCallback( HTTPRequestMethod::GET, my_get_callback ); server.registerCallback( HTTPRequestMethod::HEAD, my_get_callback ); server.run(); return 0; } #if defined (USE_SSL) int startHTTPSServer( unsigned short port, std::string const & cFile, std::string const & pkFile) { HTTPSServer server( "", port ); server.setPrivateKeyFile ( pkFile ); server.setCertificateFile( cFile ); server.initialiseSSL(); // HEAD is GET without body, we will remove the body in execute() server.registerCallback( HTTPRequestMethod::GET, my_get_callback ); server.registerCallback( HTTPRequestMethod::HEAD, my_get_callback ); server.run(); return 0; } #endif void printHelpText( std::string const & programName ) { std::cout << "Usage: " << programName << " [OPTION]\n\n"; std::cout << "Valid Options:\n"; std::cout << " -d : Run in the background\n"; #if defined (USE_SSL) std::cout << " -s : Use https protocol (default port " << DEFAULT_HTTPS_PORT << ")\n"; #endif std::cout << " -p portnumber : Run on port (default port is " << DEFAULT_HTTP_PORT << ")\n"; std::cout << " -r|--reset : Reset programming of the performance counters.\n"; std::cout << " -D|--debug level : level = 0: no debug info, > 0 increase verbosity.\n"; std::cout << " -R|--real-time : If possible the daemon will run with real time\n"; std::cout << " priority, could be useful under heavy load to \n"; std::cout << " stabilize the async counter fetching.\n"; #if defined (USE_SSL) std::cout << " -C|--certificateFile : \n"; std::cout << " -P|--privateKeyFile : \n"; #endif std::cout << " -h|--help : This information\n"; std::cout << " -silent : Silence information output and print only measurements\n"; std::cout << " --version : Print application version\n"; print_help_force_rtm_abort_mode(25, ":"); } #if not defined( UNIT_TEST ) /* Main */ PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); // Argument handling bool daemonMode = false; #if defined (USE_SSL) bool useSSL = false; #endif bool forcedProgramming = false; bool useRealtimePriority = false; bool forceRTMAbortMode = false; unsigned short port = 0; unsigned short debug_level = 0; std::string certificateFile; std::string privateKeyFile; null_stream nullStream; check_and_set_silent(argc, argv, nullStream); if ( argc > 1 ) { std::string arg_value; for ( int i=1; i < argc; ++i ) { if ( check_argument_equals( argv[i], {"-d"} ) ) daemonMode = true; else if ( check_argument_equals( argv[i], {"-p"} ) ) { if ( (++i) < argc ) { std::stringstream ss( argv[i] ); try { ss >> port; } catch( std::exception& e ) { std::cerr << "main: port number is not an unsigned short!\n"; ::exit( 2 ); } } else { throw std::runtime_error( "main: Error no port argument given" ); } } #if defined (USE_SSL) else if ( check_argument_equals( argv[i], {"-s"} ) ) { useSSL = true; } #endif else if ( check_argument_equals( argv[i], {"-r", "--reset"} ) ) { forcedProgramming = true; } else if ( check_argument_equals( argv[i], {"-D", "--debug"} ) ) { if ( (++i) < argc ) { std::stringstream ss( argv[i] ); try { ss >> debug_level; } catch( std::exception& e ) { std::cerr << "main: debug level is not an unsigned short!\n"; ::exit( 2 ); } } else { throw std::runtime_error( "main: Error no debug level argument given" ); } } else if ( check_argument_equals( argv[i], {"-R", "--real-time"} ) ) { useRealtimePriority = true; } else if ( check_argument_equals( argv[i], {"--help", "-h", "/h"} ) ) { printHelpText( argv[0] ); exit(0); } else if (check_argument_equals( argv[i], { "-force-rtm-abort-mode" })) { forceRTMAbortMode = true; } else if ( check_argument_equals( argv[i], {"-silent", "/silent"} ) ) { // handled in check_and_set_silent continue; } #if defined (USE_SSL) else if ( check_argument_equals( argv[i], {"-C", "--certificateFile"} ) ) { if ( (++i) < argc ) { std::ifstream fp( argv[i] ); if ( ! fp.is_open() ) { std::cerr << "Cannot open certificate file \"" << argv[i] << "\".\n"; printHelpText( argv[0] ); exit( 3 ); } certificateFile = argv[i]; } else { std::cerr << "Missing certificate file argument.\n"; printHelpText( argv[0] ); exit( 3 ); } } else if ( check_argument_equals( argv[i], {"-P", "--privateKeyFile"} ) ) { if ( (++i) < argc ) { std::ifstream fp( argv[i] ); if ( ! fp.is_open() ) { std::cerr << "Cannot open private key file \"" << argv[i] << "\".\n"; printHelpText( argv[0] ); exit( 4 ); } privateKeyFile = argv[i]; } else { std::cerr << "Missing private key file argument.\n"; printHelpText( argv[0] ); exit( 4 ); } } #endif else throw std::runtime_error( "Unknown argument" ); } } debug::dyn_debug_level( debug_level ); #if defined (USE_SSL) if ( useSSL ) { if ( certificateFile.empty() || privateKeyFile.empty() ) { std::cerr << "Error: wanting to use SSL but missing certificate and or private key file(s).\n"; printHelpText( argv[0] ); exit( 5 ); } } #endif if ( useRealtimePriority ) { int priority = sched_get_priority_min( SCHED_RR ); if ( priority == -1 ) { std::cerr << "Could not get SCHED_RR min priority: " << strerror( errno ) << "\n"; exit( 6 ); } else { struct sched_param sp = { .sched_priority = priority }; if ( sched_setscheduler(0, SCHED_RR, &sp ) == -1 ) { int errnosave = errno; std::cerr << "Could not set scheduler to realtime! Errno: " << errnosave << "\n"; std::cerr << "Error message: \"" << strerror( errnosave ) << "\"\n"; exit( 6 ); } else { std::cerr << "Scheduler changed to SCHED_RR and priority to " << priority << "\n"; } } } pid_t pid; if ( daemonMode ) pid = fork(); else pid = 0; if ( pid == 0 ) { /* child */ // Default programming is to use normal core counters and memory bandwidth counters // and if pmem is available to also show this instead of partial writes // A HTTP interface to change the programming is planned PCM::ErrorCode status; PCM * pcmInstance = PCM::getInstance(); assert(pcmInstance); if (forceRTMAbortMode) { pcmInstance->enableForceRTMAbortMode(); } do { status = pcmInstance->program(); switch ( status ) { case PCM::PMUBusy: { if ( forcedProgramming == false ) { std::cout << "Warning: PMU appears to be busy, do you want to reset it? (y/n)\n"; char answer; std::cin >> answer; if ( answer == 'y' || answer == 'Y' ) pcmInstance->resetPMU(); else exit(0); } else { pcmInstance->resetPMU(); } break; } case PCM::Success: break; case PCM::MSRAccessDenied: case PCM::UnknownError: default: exit(1); } } while( status != PCM::Success ); if ( pcmInstance->PMMTrafficMetricsAvailable() ) { DBG( 1, "Programmed PMEM R/W BW instead of Partial Writes" ); } else { DBG( 1, "Programmed Partial Writes instead of PMEM R/W BW" ); } //TODO: check return value when its implemented pcmInstance->programCXLCM(); #if defined (USE_SSL) if ( useSSL ) { if ( port == 0 ) port = DEFAULT_HTTPS_PORT; std::cerr << "Starting SSL enabled server on https://localhost:" << port << "/\n"; startHTTPSServer( port, certificateFile, privateKeyFile ); } else #endif { if ( port == 0 ) port = DEFAULT_HTTP_PORT; std::cerr << "Starting plain HTTP server on http://localhost:" << port << "/\n"; startHTTPServer( port ); } } else if ( pid > 0 ) { /* Parent, just leave */ DBG( 2, "Child pid: ", pid ); return 0; } else { /* Error */ DBG( 2, "Error forking. " ); return 200; } return 0; } #endif // UNIT_TEST pcm-202307/src/pcm-sensor-server.service.in000066400000000000000000000003651445420033100205400ustar00rootroot00000000000000[Unit] Description=Intel Performance Counter Monitor (PCM) Sensor Service Wants=network-online.target After=network-online.target [Service] Type=simple ExecStart=@@CMAKE_INSTALL_SBINDIR@@/pcm-sensor-server [Install] WantedBy=multi-user.target pcm-202307/src/pcm-sensor.cpp000066400000000000000000000745441445420033100157630ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // // monitor CPU conters for ksysguard // // contact: Thomas Willhalm, Patrick Ungerer, Roman Dementiev // // This program is not a tutorial on how to write nice interpreters // but a proof of concept on using ksysguard with performance counters // /*! \file pcm-sensor.cpp \brief Example of using CPU counters: implements a graphical plugin for KDE ksysguard */ #include #include #include #include "cpuasynchcounter.h" #include "utils.h" using namespace std; using namespace pcm; PCM_MAIN_NOTHROW; int mainThrows(int /* argc */, char * /*argv*/ []) { set_signal_handlers(); AsynchronCounterState counters; cout << "CPU counter sensor " << PCM_VERSION << "\n"; cout << "ksysguardd 1.2.0\n"; cout << "ksysguardd> "; while (1) { string s; cin >> s; const auto xpi = counters.getXpi(); // list counters if (s == "monitors") { for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { cout << "Socket" << a << "/CPU" << i << "/Frequency\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/IPC\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/L2CacheHitRatio\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/L3CacheHitRatio\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/L2CacheMisses\tinteger\n"; cout << "Socket" << a << "/CPU" << i << "/L3CacheMisses\tinteger\n"; cout << "Socket" << a << "/CPU" << i << "/L3Occupancy\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/LocalMemoryBandwidth\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/RemoteMemoryBandwidth\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/CoreC0StateResidency\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/CoreC3StateResidency\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/CoreC6StateResidency\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/CoreC7StateResidency\tfloat\n"; cout << "Socket" << a << "/CPU" << i << "/ThermalHeadroom\tinteger\n"; } } for (uint32 a = 0; a < counters.getNumSockets(); ++a) { cout << "Socket" << a << "/BytesReadFromMC\tfloat\n"; cout << "Socket" << a << "/BytesWrittenToMC\tfloat\n"; cout << "Socket" << a << "/BytesReadFromPMM\tfloat\n"; cout << "Socket" << a << "/BytesWrittenToPMM\tfloat\n"; cout << "Socket" << a << "/Frequency\tfloat\n"; cout << "Socket" << a << "/IPC\tfloat\n"; cout << "Socket" << a << "/L2CacheHitRatio\tfloat\n"; cout << "Socket" << a << "/L3CacheHitRatio\tfloat\n"; cout << "Socket" << a << "/L2CacheMisses\tinteger\n"; cout << "Socket" << a << "/L3CacheMisses\tinteger\n"; cout << "Socket" << a << "/L3Occupancy\tfloat\n"; cout << "Socket" << a << "/LocalMemoryBandwidth\tfloat\n"; cout << "Socket" << a << "/RemoteMemoryBandwidth\tfloat\n"; cout << "Socket" << a << "/CoreC0StateResidency\tfloat\n"; cout << "Socket" << a << "/CoreC3StateResidency\tfloat\n"; cout << "Socket" << a << "/CoreC6StateResidency\tfloat\n"; cout << "Socket" << a << "/CoreC7StateResidency\tfloat\n"; cout << "Socket" << a << "/PackageC2StateResidency\tfloat\n"; cout << "Socket" << a << "/PackageC3StateResidency\tfloat\n"; cout << "Socket" << a << "/PackageC6StateResidency\tfloat\n"; cout << "Socket" << a << "/PackageC7StateResidency\tfloat\n"; cout << "Socket" << a << "/ThermalHeadroom\tinteger\n"; cout << "Socket" << a << "/CPUEnergy\tfloat\n"; cout << "Socket" << a << "/DRAMEnergy\tfloat\n"; } for (uint32 a = 0; a < counters.getNumSockets(); ++a) { for (uint32 l = 0; l < counters.getQPILinksPerSocket(); ++l) cout << "Socket" << a << "/BytesIncomingTo" << xpi << l << "\tfloat\n"; } cout << xpi << "_Traffic\tfloat\n"; cout << "Frequency\tfloat\n"; cout << "IPC\tfloat\n"; //double check output cout << "L2CacheHitRatio\tfloat\n"; cout << "L3CacheHitRatio\tfloat\n"; cout << "L2CacheMisses\tinteger\n"; cout << "L3CacheMisses\tinteger\n"; cout << "CoreC0StateResidency\tfloat\n"; cout << "CoreC3StateResidency\tfloat\n"; cout << "CoreC6StateResidency\tfloat\n"; cout << "CoreC7StateResidency\tfloat\n"; cout << "PackageC2StateResidency\tfloat\n"; cout << "PackageC3StateResidency\tfloat\n"; cout << "PackageC6StateResidency\tfloat\n"; cout << "PackageC7StateResidency\tfloat\n"; cout << "CPUEnergy\tfloat\n"; cout << "DRAMEnergy\tfloat\n"; } // provide metadata for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { { stringstream c; c << "Socket" << a << "/CPU" << i << "/Frequency?"; if (s == c.str()) { cout << "FREQ. CPU" << i << "\t\t\tMHz\n"; } } { stringstream c; c << "Socket" << a << "/CPU" << i << "/ThermalHeadroom?"; if (s == c.str()) { cout << "Temperature reading in 1 degree Celsius relative to the TjMax temperature (thermal headroom) for CPU" << i << "\t\t\t°C\n"; } } { stringstream c; c << "Socket" << a << "/CPU" << i << "/CoreC0StateResidency?"; if (s == c.str()) { cout << "core C0-state residency for CPU" << i << "\t\t\t%\n"; } } { stringstream c; c << "Socket" << a << "/CPU" << i << "/CoreC3StateResidency?"; if (s == c.str()) { cout << "core C3-state residency for CPU" << i << "\t\t\t%\n"; } } { stringstream c; c << "Socket" << a << "/CPU" << i << "/CoreC6StateResidency?"; if (s == c.str()) { cout << "core C6-state residency for CPU" << i << "\t\t\t%\n"; } } { stringstream c; c << "Socket" << a << "/CPU" << i << "/CoreC7StateResidency?"; if (s == c.str()) { cout << "core C7-state residency for CPU" << i << "\t\t\t%\n"; } } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/IPC?"; if (s == c.str()) { cout << "IPC CPU" << i << "\t0\t\t\n"; //cout << "CPU" << i << "\tInstructions per Cycle\t0\t1\t \n"; } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/L2CacheHitRatio?"; if (s == c.str()) { cout << "L2 Cache Hit Ratio CPU" << i << "\t0\t\t\n"; // cout << "CPU" << i << "\tL2 Cache Hit Ratio\t0\t1\t \n"; } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/L3CacheHitRatio?"; if (s == c.str()) { cout << "L3 Cache Hit Ratio CPU" << i << "\t0\t\t \n"; } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/L2CacheMisses?"; if (s == c.str()) { cout << "L2 Cache Misses CPU" << i << "\t0\t\t \n"; //cout << "CPU" << i << "\tL2 Cache Misses\t0\t1\t \n"; } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/L3CacheMisses?"; if (s == c.str()) { cout << "L3 Cache Misses CPU" << i << "\t0\t\t \n"; //cout << "CPU" << i << "\tL3 Cache Misses\t0\t1\t \n"; } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/L3Occupancy?"; if (s == c.str()) { cout << "L3 Cache Occupancy CPU " << i << "\t0\t\t \n"; //cout << "CPU" << i << "\tL3 Cache Occupancy\t0\t1\t \n"; } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/LocalMemoryBandwidth?"; if (s == c.str()) { cout << "Local Memory Bandwidth CPU " << i << "\t0\t\t \n"; //cout << "CPU" << i << "\tLocal Memory Bandwidth\t0\t1\t \n"; } } } for (uint32 i = 0; i < counters.getNumCores(); ++i) { for (uint32 a = 0; a < counters.getNumSockets(); ++a) if (a == counters.getSocketId(i)) { stringstream c; c << "Socket" << a << "/CPU" << i << "/RemoteMemoryBandwidth?"; if (s == c.str()) { cout << "Remote Memory Bandwidth CPU " << i << "\t0\t\t \n"; //cout << "CPU" << i << "\tRemote Memory Bandwidth\t0\t1\t \n"; } } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/BytesReadFromMC?"; if (s == c.str()) { cout << "read from MC Socket" << i << "\t0\t\tGB\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/BytesReadFromPMM?"; if (s == c.str()) { cout << "read from PMM memory on Socket" << i << "\t0\t\tGB\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/DRAMEnergy?"; if (s == c.str()) { cout << "Energy consumed by DRAM on socket " << i << "\t0\t\tJoule\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/CPUEnergy?"; if (s == c.str()) { cout << "Energy consumed by CPU package " << i << "\t0\t\tJoule\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/ThermalHeadroom?"; if (s == c.str()) { cout << "Temperature reading in 1 degree Celsius relative to the TjMax temperature (thermal headroom) for CPU package " << i << "\t0\t\t°C\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/CoreC0StateResidency?"; if (s == c.str()) { cout << "core C0-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/CoreC3StateResidency?"; if (s == c.str()) { cout << "core C3-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/CoreC6StateResidency?"; if (s == c.str()) { cout << "core C6-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/CoreC7StateResidency?"; if (s == c.str()) { cout << "core C7-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/PackageC2StateResidency?"; if (s == c.str()) { cout << "package C2-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/PackageC3StateResidency?"; if (s == c.str()) { cout << "package C3-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/PackageC6StateResidency?"; if (s == c.str()) { cout << "package C6-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/PackageC7StateResidency?"; if (s == c.str()) { cout << "package C7-state residency for CPU package " << i << "\t0\t\t%\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/BytesWrittenToPMM?"; if (s == c.str()) { cout << "written to PMM memory on Socket" << i << "\t0\t\tGB\n"; //cout << "CPU" << i << "\tBytes written to memory channel\t0\t1\t GB\n"; } } for (uint32 l = 0; l < counters.getQPILinksPerSocket(); ++l) { for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/BytesIncomingTo" << xpi << l << "?"; if (s == c.str()) { //cout << "Socket" << i << "\tBytes incoming to QPI link\t" << l<< "\t\t GB\n"; cout << "incoming to Socket" << i << " " << xpi << " Link" << l << "\t0\t\tGB\n"; } } } { stringstream c; c << xpi << "_Traffic?"; if (s == c.str()) { cout << "Traffic on all " << xpi << " links\t0\t\tGB\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/Frequency?"; if (s == c.str()) { cout << "Socket" << i << " Frequency\t0\t\tMHz\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/IPC?"; if (s == c.str()) { cout << "Socket" << i << " IPC\t0\t\t\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/L2CacheHitRatio?"; if (s == c.str()) { cout << "Socket" << i << " L2 Cache Hit Ratio\t0\t\t\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/L3CacheHitRatio?"; if (s == c.str()) { cout << "Socket" << i << " L3 Cache Hit Ratio\t0\t\t\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/L2CacheMisses?"; if (s == c.str()) { cout << "Socket" << i << " L2 Cache Misses\t0\t\t\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/L3CacheMisses?"; if (s == c.str()) { cout << "Socket" << i << " L3 Cache Misses\t0\t\t\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/L3Occupancy"; if (s == c.str()) { cout << "Socket" << i << " L3 Cache Occupancy\t0\t\t\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/LocalMemoryBandwidth"; if (s == c.str()) { cout << "Socket" << i << " Local Memory Bandwidth\t0\t\t\n"; } } for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/RemoteMemoryBandwidth"; if (s == c.str()) { cout << "Socket" << i << " Remote Memory Bandwidth\t0\t\t\n"; } } { stringstream c; c << "Frequency?"; if (s == c.str()) { cout << "Frequency system wide\t0\t\tMhz\n"; } } { stringstream c; c << "IPC?"; if (s == c.str()) { cout << "IPC system wide\t0\t\t\n"; } } { stringstream c; c << "L2CacheHitRatio?"; if (s == c.str()) { cout << "System wide L2 Cache Hit Ratio\t0\t\t\n"; } } { stringstream c; c << "L3CacheHitRatio?"; if (s == c.str()) { cout << "System wide L3 Cache Hit Ratio\t0\t\t\n"; } } { stringstream c; c << "L2CacheMisses?"; if (s == c.str()) { cout << "System wide L2 Cache Misses\t0\t\t\n"; } } { stringstream c; c << "L3CacheMisses?"; if (s == c.str()) { cout << "System wide L3 Cache Misses\t0\t\t\n"; } } { stringstream c; c << "L3CacheMisses?"; if (s == c.str()) { cout << "System wide L3 Cache Misses\t0\t\t\n"; } } { stringstream c; c << "DRAMEnergy?"; if (s == c.str()) { cout << "System wide energy consumed by DRAM \t0\t\tJoule\n"; } } { stringstream c; c << "CPUEnergy?"; if (s == c.str()) { cout << "System wide energy consumed by CPU packages \t0\t\tJoule\n"; } } { stringstream c; c << "CoreC0StateResidency?"; if (s == c.str()) { cout << "System wide core C0-state residency \t0\t\t%\n"; } } { stringstream c; c << "CoreC3StateResidency?"; if (s == c.str()) { cout << "System wide core C3-state residency \t0\t\t%\n"; } } { stringstream c; c << "CoreC6StateResidency?"; if (s == c.str()) { cout << "System wide core C6-state residency \t0\t\t%\n"; } } { stringstream c; c << "CoreC7StateResidency?"; if (s == c.str()) { cout << "System wide core C7-state residency \t0\t\t%\n"; } } { stringstream c; c << "PackageC2StateResidency?"; if (s == c.str()) { cout << "System wide package C2-state residency \t0\t\t%\n"; } } { stringstream c; c << "PackageC3StateResidency?"; if (s == c.str()) { cout << "System wide package C3-state residency \t0\t\t%\n"; } } { stringstream c; c << "PackageC6StateResidency?"; if (s == c.str()) { cout << "System wide package C6-state residency \t0\t\t%\n"; } } { stringstream c; c << "PackageC7StateResidency?"; if (s == c.str()) { cout << "System wide package C7-state residency \t0\t\t%\n"; } } // sensors #define OUTPUT_CORE_METRIC(name, function) \ for (uint32 i = 0; i(i) / 1000000)) OUTPUT_CORE_METRIC("/IPC", (counters.get(i))) OUTPUT_CORE_METRIC("/L2CacheHitRatio", (counters.get(i))) OUTPUT_CORE_METRIC("/L3CacheHitRatio", (counters.get(i))) OUTPUT_CORE_METRIC("/L2CacheMisses", (counters.get(i))) OUTPUT_CORE_METRIC("/L3CacheMisses", (counters.get(i))) OUTPUT_CORE_METRIC("/L3Occupancy", (counters.get(i))) OUTPUT_CORE_METRIC("/LocalMemoryBandwidth", (counters.get(i))) OUTPUT_CORE_METRIC("/RemoteMemoryBandwidth", (counters.get(i))) OUTPUT_CORE_METRIC("/CoreC0StateResidency", (counters.get(0, i) * 100.)) OUTPUT_CORE_METRIC("/CoreC3StateResidency", (counters.get(3, i) * 100.)) OUTPUT_CORE_METRIC("/CoreC6StateResidency", (counters.get(6, i) * 100.)) OUTPUT_CORE_METRIC("/CoreC7StateResidency", (counters.get(7, i) * 100.)) OUTPUT_CORE_METRIC("/ThermalHeadroom", (counters.get(i))) #define OUTPUT_SOCKET_METRIC(name, function) \ for (uint32 i = 0; i(i))) OUTPUT_SOCKET_METRIC("/CPUEnergy", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/CoreC0StateResidency", (counters.getSocket(0, i) * 100.)) OUTPUT_SOCKET_METRIC("/CoreC3StateResidency", (counters.getSocket(3, i) * 100.)) OUTPUT_SOCKET_METRIC("/CoreC6StateResidency", (counters.getSocket(6, i) * 100.)) OUTPUT_SOCKET_METRIC("/CoreC7StateResidency", (counters.getSocket(7, i) * 100.)) OUTPUT_SOCKET_METRIC("/PackageC2StateResidency", (counters.getSocket(2, i) * 100.)) OUTPUT_SOCKET_METRIC("/PackageC3StateResidency", (counters.getSocket(3, i) * 100.)) OUTPUT_SOCKET_METRIC("/PackageC6StateResidency", (counters.getSocket(6, i) * 100.)) OUTPUT_SOCKET_METRIC("/PackageC7StateResidency", (counters.getSocket(7, i) * 100.)) OUTPUT_SOCKET_METRIC("/ThermalHeadroom", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/BytesReadFromMC", (double(counters.getSocket(i)) / 1024 / 1024 / 1024)) OUTPUT_SOCKET_METRIC("/BytesWrittenToMC", (double(counters.getSocket(i)) / 1024 / 1024 / 1024)) OUTPUT_SOCKET_METRIC("/BytesReadFromPMM", (double(counters.getSocket(i)) / 1024 / 1024 / 1024)) OUTPUT_SOCKET_METRIC("/BytesWrittenToPMM", (double(counters.getSocket(i)) / 1024 / 1024 / 1024)) OUTPUT_SOCKET_METRIC("/Frequency", (counters.getSocket(i) / 1000000)) OUTPUT_SOCKET_METRIC("/IPC", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/L2CacheHitRatio", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/L3CacheHitRatio", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/L2CacheMisses", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/L3CacheMisses", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/L3Occupancy", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/LocalMemoryBandwidth", (counters.getSocket(i))) OUTPUT_SOCKET_METRIC("/RemoteMemoryBandwidth", (counters.getSocket(i))) for (uint32 l = 0; l < counters.getQPILinksPerSocket(); ++l) { for (uint32 i = 0; i < counters.getNumSockets(); ++i) { stringstream c; c << "Socket" << i << "/BytesIncomingTo" << xpi << l; if (s == c.str()) { cout << double(counters.getSocket(i, l)) / 1024 / 1024 / 1024 << "\n"; } } } #define OUTPUT_SYSTEM_METRIC(name, function) \ { \ stringstream c; \ c << name; \ if (s == c.str()) { \ cout << function << "\n"; \ } \ } OUTPUT_SYSTEM_METRIC("DRAMEnergy", (counters.getSystem())) OUTPUT_SYSTEM_METRIC("CPUEnergy", (counters.getSystem())) OUTPUT_SYSTEM_METRIC("CoreC0StateResidency", (counters.getSystem(0) * 100.)) OUTPUT_SYSTEM_METRIC("CoreC3StateResidency", (counters.getSystem(3) * 100.)) OUTPUT_SYSTEM_METRIC("CoreC6StateResidency", (counters.getSystem(6) * 100.)) OUTPUT_SYSTEM_METRIC("CoreC7StateResidency", (counters.getSystem(7) * 100.)) OUTPUT_SYSTEM_METRIC("PackageC2StateResidency", (counters.getSystem(2) * 100.)) OUTPUT_SYSTEM_METRIC("PackageC3StateResidency", (counters.getSystem(3) * 100.)) OUTPUT_SYSTEM_METRIC("PackageC6StateResidency", (counters.getSystem(6) * 100.)) OUTPUT_SYSTEM_METRIC("PackageC7StateResidency", (counters.getSystem(7) * 100.)) OUTPUT_SYSTEM_METRIC("Frequency", (double(counters.getSystem()) / 1000000)) OUTPUT_SYSTEM_METRIC("IPC", (double(counters.getSystem()))) OUTPUT_SYSTEM_METRIC("L2CacheHitRatio", (double(counters.getSystem()))) OUTPUT_SYSTEM_METRIC("L3CacheHitRatio", (double(counters.getSystem()))) OUTPUT_SYSTEM_METRIC("L2CacheMisses", (double(counters.getSystem()))) OUTPUT_SYSTEM_METRIC("L3CacheMisses", (double(counters.getSystem()))) OUTPUT_SYSTEM_METRIC(std::string(xpi + std::string("_Traffic")), (double(counters.getSystem()) / 1024 / 1024 / 1024)) // exit if (s == "quit" || s == "exit" || s == "") { break; } cout << "ksysguardd> "; } return 0; } pcm-202307/src/pcm-tsx.cpp000066400000000000000000000723061445420033100152620ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev /*! \file pcm-tsx.cpp \brief Example of using CPU counters: implements a performance counter monitoring utility for Intel Transactional Synchronization Extensions */ #include #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #include #include // for gettimeofday() #endif #include #include #include #include #include #include #include #include "cpucounters.h" #include "utils.h" #ifdef _MSC_VER #include "freegetopt/getopt.h" #endif #include #define PCM_DELAY_DEFAULT 1.0 // in seconds #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs using namespace std; using namespace pcm; struct TSXEvent { const char * name; unsigned char event; unsigned char umask; const char * description; }; vector eventDefinition = { { "RTM_RETIRED.START", 0xC9, 0x01, "Number of times an RTM execution started." }, { "RTM_RETIRED.COMMIT", 0xC9, 0x02, "Number of times an RTM execution successfully committed" }, { "RTM_RETIRED.ABORTED", 0xC9, 0x04, "Number of times an RTM execution aborted due to any reasons (multiple categories may count as one)" }, { "RTM_RETIRED.ABORTED_MEM", 0xC9, 0x08, "Number of times an RTM execution aborted due to various memory events" }, { "RTM_RETIRED.ABORTED_TIMER", 0xC9, 0x10, "Number of times an RTM execution aborted due to uncommon conditions" }, { "RTM_RETIRED.ABORTED_UNFRIENDLY", 0xC9, 0x20, "Number of times an RTM execution aborted due to Intel TSX-unfriendly instructions" }, { "RTM_RETIRED.ABORTED_MEMTYPE", 0xC9, 0x40, "Number of times an RTM execution aborted due to incompatible memory type" }, { "RTM_RETIRED.ABORTED_EVENTS", 0xC9, 0x80, "Number of times an RTM execution aborted due to none of the previous 4 categories (e.g. interrupt)" }, { "HLE_RETIRED.START", 0xC8, 0x01, "Number of times an HLE execution started." }, { "HLE_RETIRED.COMMIT", 0xC8, 0x02, "Number of times an HLE execution successfully committed" }, { "HLE_RETIRED.ABORTED", 0xC8, 0x04, "Number of times an HLE execution aborted due to any reasons (multiple categories may count as one)" }, { "HLE_RETIRED.ABORTED_MEM", 0xC8, 0x08, "Number of times an HLE execution aborted due to various memory events" }, { "HLE_RETIRED.ABORTED_TIMER", 0xC8, 0x10, "Number of times an HLE execution aborted due to uncommon conditions" }, { "HLE_RETIRED.ABORTED_UNFRIENDLY", 0xC8, 0x20, "Number of times an HLE execution aborted due to Intel TSX-unfriendly instructions" }, { "HLE_RETIRED.ABORTED_MEMTYPE", 0xC8, 0x40, "Number of times an HLE execution aborted due to incompatible memory type" }, { "HLE_RETIRED.ABORTED_EVENTS", 0xC8, 0x80, "Number of times an HLE execution aborted due to none of the previous 4 categories (e.g. interrupt)" }, { "TX_MEM.ABORT_CONFLICT", 0x54, 0x01, "Number of times a transactional abort was signaled due to a data conflict on a transactionally accessed address" }, { "TX_MEM.ABORT_CAPACITY_WRITE", 0x54, 0x02, "Number of times a transactional abort was signaled due to limited resources for transactional stores" }, { "TX_MEM.ABORT_HLE_STORE_TO_ELIDED_LOCK", 0x54, 0x04, "Number of times a HLE transactional region aborted due to a non XRELEASE prefixed instruction writing to an elided lock in the elision buffer" }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_NOT_EMPTY", 0x54, 0x08, "Number of times an HLE transactional execution aborted due to NoAllocatedElisionBuffer being nonzero." }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_MISMATCH", 0x54, 0x10, "Number of times an HLE transactional execution aborted due to XRELEASE lock not satisfying the address and value requirements in the elision buffer." }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_UNSUPPORTED_ALIGNMENT", 0x54, 0x20, "Number of times an HLE transactional execution aborted due to an unsupported read alignment from the elision buffer." }, { "TX_MEM.HLE_ELISION_BUFFER_FULL", 0x54, 0x40, "Number of times HLE lock could not be elided due to ElisionBufferAvailable being zero." }, { "TX_EXEC.MISC1", 0x5D, 0x01, "Counts the number of times a class of instructions that may cause a transactional abort was executed. Since this is the count of execution, it may not always cause a transactional abort." }, { "TX_EXEC.MISC2", 0x5D, 0x02, "Counts the number of times a class of instructions that may cause a transactional abort was executed inside a transactional region" }, { "TX_EXEC.MISC3", 0x5D, 0x04, "Counts the number of times an instruction execution caused the nest count supported to be exceeded" }, { "TX_EXEC.MISC4", 0x5D, 0x08, "Counts the number of times a XBEGIN instruction was executed inside an HLE transactional region" }, { "TX_EXEC.MISC5", 0x5D, 0x10, "Counts the number of times an HLE XACQUIRE instruction was executed inside an RTM transactional region" } }; const vector sklEventDefinition = { { "RTM_RETIRED.START", 0xC9, 0x01, "Number of times an RTM execution started." }, { "RTM_RETIRED.COMMIT", 0xC9, 0x02, "Number of times an RTM execution successfully committed" }, { "RTM_RETIRED.ABORTED", 0xC9, 0x04, "Number of times an RTM execution aborted due to any reasons (multiple categories may count as one)" }, { "RTM_RETIRED.ABORTED_MEM", 0xC9, 0x08, "Number of times an RTM execution aborted due to various memory events" }, { "RTM_RETIRED.ABORTED_TIMER", 0xC9, 0x10, "Number of times an RTM execution aborted due to uncommon conditions" }, { "RTM_RETIRED.ABORTED_UNFRIENDLY", 0xC9, 0x20, "Number of times an RTM execution aborted due to Intel TSX-unfriendly instructions" }, { "RTM_RETIRED.ABORTED_MEMTYPE", 0xC9, 0x40, "Number of times an RTM execution aborted due to incompatible memory type" }, { "RTM_RETIRED.ABORTED_EVENTS", 0xC9, 0x80, "Number of times an RTM execution aborted due to none of the previous 4 categories (e.g. interrupt)" }, { "HLE_RETIRED.START", 0xC8, 0x01, "Number of times an HLE execution started." }, { "HLE_RETIRED.COMMIT", 0xC8, 0x02, "Number of times an HLE execution successfully committed" }, { "HLE_RETIRED.ABORTED", 0xC8, 0x04, "Number of times an HLE execution aborted due to any reasons (multiple categories may count as one)" }, { "HLE_RETIRED.ABORTED_MEM", 0xC8, 0x08, "Number of times an HLE execution aborted due to various memory events" }, { "HLE_RETIRED.ABORTED_TIMER", 0xC8, 0x10, "Number of times an HLE execution aborted due to uncommon conditions" }, { "HLE_RETIRED.ABORTED_UNFRIENDLY", 0xC8, 0x20, "Number of times an HLE execution aborted due to Intel TSX-unfriendly instructions" }, { "HLE_RETIRED.ABORTED_MEMTYPE", 0xC8, 0x40, "Number of times an HLE execution aborted due to incompatible memory type" }, { "HLE_RETIRED.ABORTED_EVENTS", 0xC8, 0x80, "Number of times an HLE execution aborted due to none of the previous 4 categories (e.g. interrupt)" }, { "TX_MEM.ABORT_CONFLICT", 0x54, 0x01, "Number of times a transactional abort was signaled due to a data conflict on a transactionally accessed address" }, { "TX_MEM.ABORT_CAPACITY", 0x54, 0x02, "Number of times a transactional abort was signaled due to a data capacity limitation for transactional reads or writes" }, { "TX_MEM.ABORT_HLE_STORE_TO_ELIDED_LOCK", 0x54, 0x04, "Number of times a HLE transactional region aborted due to a non XRELEASE prefixed instruction writing to an elided lock in the elision buffer" }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_NOT_EMPTY", 0x54, 0x08, "Number of times an HLE transactional execution aborted due to NoAllocatedElisionBuffer being nonzero." }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_MISMATCH", 0x54, 0x10, "Number of times an HLE transactional execution aborted due to XRELEASE lock not satisfying the address and value requirements in the elision buffer." }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_UNSUPPORTED_ALIGNMENT", 0x54, 0x20, "Number of times an HLE transactional execution aborted due to an unsupported read alignment from the elision buffer." }, { "TX_MEM.HLE_ELISION_BUFFER_FULL", 0x54, 0x40, "Number of times HLE lock could not be elided due to ElisionBufferAvailable being zero." }, { "TX_EXEC.MISC1", 0x5D, 0x01, "Counts the number of times a class of instructions that may cause a transactional abort was executed. Since this is the count of execution, it may not always cause a transactional abort." }, { "TX_EXEC.MISC2", 0x5D, 0x02, "Counts the number of times a class of instructions (e.g., vzeroupper) that may cause a transactional abort was executed inside a transactional region" }, { "TX_EXEC.MISC3", 0x5D, 0x04, "Counts the number of times an instruction execution caused the nest count supported to be exceeded" }, { "TX_EXEC.MISC4", 0x5D, 0x08, "Counts the number of times a XBEGIN instruction was executed inside an HLE transactional region" }, { "TX_EXEC.MISC5", 0x5D, 0x10, "Counts the number of times an HLE XACQUIRE instruction was executed inside an RTM transactional region" } }; const vector iclEventDefinition = { { "RTM_RETIRED.START", 0xC9, 0x01, "Number of times an RTM execution started." }, { "RTM_RETIRED.COMMIT", 0xC9, 0x02, "Number of times an RTM execution successfully committed" }, { "RTM_RETIRED.ABORTED", 0xC9, 0x04, "Number of times an RTM execution aborted due to any reasons (multiple categories may count as one)" }, { "RTM_RETIRED.ABORTED_MEM", 0xC9, 0x08, "Number of times an RTM execution aborted due to various memory events" }, { "RTM_RETIRED.ABORTED_TIMER", 0xC9, 0x10, "Number of times an RTM execution aborted due to uncommon conditions" }, { "RTM_RETIRED.ABORTED_UNFRIENDLY", 0xC9, 0x20, "Number of times an RTM execution aborted due to Intel TSX-unfriendly instructions" }, { "RTM_RETIRED.ABORTED_MEMTYPE", 0xC9, 0x40, "Number of times an RTM execution aborted due to incompatible memory type" }, { "RTM_RETIRED.ABORTED_EVENTS", 0xC9, 0x80, "Number of times an RTM execution aborted due to none of the previous 4 categories (e.g. interrupt)" }, { "HLE_RETIRED.START", 0xC8, 0x01, "Number of times an HLE execution started." }, { "HLE_RETIRED.COMMIT", 0xC8, 0x02, "Number of times an HLE execution successfully committed" }, { "HLE_RETIRED.ABORTED", 0xC8, 0x04, "Number of times an HLE execution aborted due to any reasons (multiple categories may count as one)" }, { "HLE_RETIRED.ABORTED_MEM", 0xC8, 0x08, "Number of times an HLE execution aborted due to various memory events" }, { "HLE_RETIRED.ABORTED_TIMER", 0xC8, 0x10, "Number of times an HLE execution aborted due to uncommon conditions" }, { "HLE_RETIRED.ABORTED_UNFRIENDLY", 0xC8, 0x20, "Number of times an HLE execution aborted due to Intel TSX-unfriendly instructions" }, { "HLE_RETIRED.ABORTED_MEMTYPE", 0xC8, 0x40, "Number of times an HLE execution aborted due to incompatible memory type" }, { "HLE_RETIRED.ABORTED_EVENTS", 0xC8, 0x80, "Number of times an HLE execution aborted due to none of the previous 4 categories (e.g. interrupt)" }, { "TX_MEM.ABORT_CONFLICT", 0x54, 0x01, "Number of times a transactional abort was signaled due to a data conflict on a transactionally accessed address" }, { "TX_MEM.ABORT_CAPACITY_WRITE", 0x54, 0x02, "Speculatively counts the number of TSX aborts due to a data capacity limitation for transactional writes" }, { "TX_MEM.ABORT_CAPACITY_READ", 0x54, 0x80, "Speculatively counts the number of TSX aborts due to a data capacity limitation for transactional reads" }, { "TX_MEM.ABORT_HLE_STORE_TO_ELIDED_LOCK", 0x54, 0x04, "Number of times a HLE transactional region aborted due to a non XRELEASE prefixed instruction writing to an elided lock in the elision buffer" }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_NOT_EMPTY", 0x54, 0x08, "Number of times an HLE transactional execution aborted due to NoAllocatedElisionBuffer being nonzero." }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_MISMATCH", 0x54, 0x10, "Number of times an HLE transactional execution aborted due to XRELEASE lock not satisfying the address and value requirements in the elision buffer." }, { "TX_MEM.ABORT_HLE_ELISION_BUFFER_UNSUPPORTED_ALIGNMENT", 0x54, 0x20, "Number of times an HLE transactional execution aborted due to an unsupported read alignment from the elision buffer." }, { "TX_MEM.HLE_ELISION_BUFFER_FULL", 0x54, 0x40, "Number of times HLE lock could not be elided due to ElisionBufferAvailable being zero." }, { "TX_EXEC.MISC2", 0x5D, 0x02, "Counts the number of times a class of instructions (e.g., vzeroupper) that may cause a transactional abort was executed inside a transactional region" }, { "TX_EXEC.MISC3", 0x5D, 0x04, "Counts the number of times an instruction execution caused the nest count supported to be exceeded" } }; const vector sprEventDefinition = { { "RTM_RETIRED.START", 0xC9, 0x01, "Number of times an RTM execution started." }, { "RTM_RETIRED.COMMIT", 0xC9, 0x02, "Number of times an RTM execution successfully committed" }, { "RTM_RETIRED.ABORTED", 0xC9, 0x04, "Number of times an RTM execution aborted." }, { "RTM_RETIRED.ABORTED_MEM", 0xC9, 0x08, "Number of times an RTM execution aborted due to various memory events (e.g. read/write capacity and conflicts)" }, { "RTM_RETIRED.ABORTED_UNFRIENDLY", 0xC9, 0x20, "Number of times an RTM execution aborted due to HLE-unfriendly instructions" }, { "RTM_RETIRED.ABORTED_MEMTYPE", 0xC9, 0x40, "Number of times an RTM execution aborted due to incompatible memory type" }, { "RTM_RETIRED.ABORTED_EVENTS", 0xC9, 0x80, "Number of times an RTM execution aborted due to none of the previous 4 categories (e.g. interrupt)" }, { "TX_MEM.ABORT_CONFLICT", 0x54, 0x01, "Number of times a transactional abort was signaled due to a data conflict on a transactionally accessed address" }, { "TX_MEM.ABORT_CAPACITY_WRITE", 0x54, 0x02, "Speculatively counts the number of TSX aborts due to a data capacity limitation for transactional writes." }, { "TX_MEM.ABORT_CAPACITY_READ", 0x54, 0x80, "Speculatively counts the number of TSX aborts due to a data capacity limitation for transactional reads" } }; void print_usage(const string & progname) { cout << "\n Usage: \n " << progname << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -F | -force => force running this program despite lack of HW RTM support (optional)\n"; cout << " -pid PID | /pid PID => collect core metrics only for specified process ID\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; cout << " [-e event1] [-e event2] [-e event3]=> optional list of custom TSX events to monitor (up to 4)." << " The list of supported events:\n"; for (auto & e: eventDefinition) { cout << e.name << "\t" << e.description << "\n"; } cout << "\n"; cout << " Examples:\n"; cout << " " << progname << " 1 => print counters every second without core and socket output\n"; cout << " " << progname << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << progname << " /csv 5 2>/dev/null => one sample every 5 seconds, and discard all diagnostic output\n"; cout << "\n"; } #define TX_CYCLES_POS (1) #define TX_CYCLES_COMMITED_POS (2) #define N_HLE_POS (3) #define N_RTM_POS (0) bool supportNHLECountBasicStat = true; template void print_basic_stats(const StateType & BeforeState, const StateType & AfterState, bool csv) { uint64 cycles = getCycles(BeforeState, AfterState); uint64 instr = getInstructionsRetired(BeforeState, AfterState); const uint64 TXcycles = getNumberOfCustomEvents(TX_CYCLES_POS, BeforeState, AfterState); const uint64 TXcycles_commited = getNumberOfCustomEvents(TX_CYCLES_COMMITED_POS, BeforeState, AfterState); const uint64 Abr_cycles = (TXcycles > TXcycles_commited) ? (TXcycles - TXcycles_commited) : 0ULL; uint64 nRTM = getNumberOfCustomEvents(N_RTM_POS, BeforeState, AfterState); uint64 nHLE = getNumberOfCustomEvents(N_HLE_POS, BeforeState, AfterState); if (csv) { cout << double(instr) / double(cycles) << ","; cout << instr << ","; cout << cycles << ","; cout << TXcycles << "," << std::setw(5) << 100. * double(TXcycles) / double(cycles) << "%,"; cout << Abr_cycles << "," << std::setw(5) << 100. * double(Abr_cycles) / double(cycles) << "%,"; cout << nRTM << ","; if (supportNHLECountBasicStat) { cout << nHLE << ","; } } else { cout << double(instr) / double(cycles) << " "; cout << unit_format(instr) << " "; cout << unit_format(cycles) << " "; cout << unit_format(TXcycles) << " (" << std::setw(5) << 100. * double(TXcycles) / double(cycles) << "%) "; cout << unit_format(Abr_cycles) << " (" << std::setw(5) << 100. * double(Abr_cycles) / double(cycles) << "%) "; cout << unit_format(nRTM) << " "; if (supportNHLECountBasicStat) { cout << unit_format(nHLE) << " "; } } if (nRTM + nHLE) { uint64 cyclesPerTransaction = TXcycles / (nRTM + nHLE); if (csv) cout << cyclesPerTransaction << "\n"; else cout << unit_format(cyclesPerTransaction) << "\n"; } else cout << " N/A\n"; } std::vector events; template void print_custom_stats(const StateType & BeforeState, const StateType & AfterState, bool csv) { for (size_t i = 0; i < events.size(); ++i) if (!csv) cout << unit_format(getNumberOfCustomEvents((pcm::int32)i, BeforeState, AfterState)) << " "; else cout << getNumberOfCustomEvents((pcm::int32)i, BeforeState, AfterState) << ","; cout << "\n"; } int findEvent(const char * name) { for (size_t i = 0; i < eventDefinition.size(); ++i) { if (strcmp(name, eventDefinition[i].name) == 0) return (int)i; } return -1; } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream2; #ifdef PCM_FORCE_SILENT null_stream nullStream1; std::cout.rdbuf(&nullStream1); std::cerr.rdbuf(&nullStream2); #else check_and_set_silent(argc, argv, nullStream2); #endif set_signal_handlers(); cerr << "\n"; cerr << " Intel(r) Performance Counter Monitor: Intel(r) Transactional Synchronization Extensions Monitoring Utility \n"; cerr << "\n"; double delay = -1.0; int pid{ -1 }; char * sysCmd = NULL; char ** sysArgv = NULL; int cur_event; bool csv = false; bool force = false; MainLoop mainLoop; string program = string(argv[0]); parsePID(argc, argv, pid); PCM * m = PCM::getInstance(); const size_t numCtrSupported = m->getMaxCustomCoreEvents(); switch (m->getCPUModel()) { case PCM::SKL: case PCM::SKX: case PCM::KBL: eventDefinition = sklEventDefinition; break; case PCM::ICL: case PCM::ICX: case PCM::RKL: eventDefinition = iclEventDefinition; break; case PCM::SPR: eventDefinition = sprEventDefinition; break; } if (argc > 1) do { argv++; argc--; string arg_value; if (*argv == nullptr) { continue; } else if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_usage(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (isPIDOption(argv)) { argv++; argc--; continue; } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv = true; if (!arg_value.empty()) { m->setOutput(arg_value); } continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (check_argument_equals(*argv, {"-e"})) { argv++; argc--; if (events.size() >= numCtrSupported) { cerr << "At most " << numCtrSupported << " events are allowed\n"; exit(EXIT_FAILURE); } cur_event = findEvent(*argv); if (cur_event < 0) { cerr << "Event " << *argv << " is not supported. See the list of supported events\n"; print_usage(program); exit(EXIT_FAILURE); } events.push_back(cur_event); continue; } else if (CheckAndForceRTMAbortMode(*argv, m)) // for pcm-tsx this option is enabled for testing only, not exposed in the help { continue; } else if (check_argument_equals(*argv, {"-F", "-f", "-force"})) { force = true; } else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_usage); continue; } } while (argc > 1); // end of command line partsing loop EventSelectRegister def_event_select_reg; def_event_select_reg.value = 0; def_event_select_reg.fields.usr = 1; def_event_select_reg.fields.os = 1; def_event_select_reg.fields.enable = 1; PCM::ExtendedCustomCoreEventDescription conf; conf.fixedCfg = NULL; // default EventSelectRegister regs[PERF_MAX_CUSTOM_COUNTERS]; conf.gpCounterCfg = regs; for (int i = 0; i < PERF_MAX_CUSTOM_COUNTERS; ++i) regs[i] = def_event_select_reg; if (events.empty()) { conf.nGPCounters = 4; if (m->getMaxCustomCoreEvents() == 3) { conf.nGPCounters = 3; supportNHLECountBasicStat = false; } regs[N_RTM_POS].fields.event_select = 0xc9; regs[N_RTM_POS].fields.umask = 0x01; regs[N_HLE_POS].fields.event_select = 0xc8; regs[N_HLE_POS].fields.umask = 0x01; regs[TX_CYCLES_COMMITED_POS].fields.event_select = 0x3c; regs[TX_CYCLES_COMMITED_POS].fields.in_tx = 1; regs[TX_CYCLES_COMMITED_POS].fields.in_txcp = 1; regs[TX_CYCLES_POS].fields.event_select = 0x3c; regs[TX_CYCLES_POS].fields.in_tx = 1; } else { conf.nGPCounters = (uint32) events.size(); for (unsigned int i = 0; i < events.size(); ++i) { const auto event = eventDefinition[events[i]].event; if (event == 0x54 && i >= 4) { cerr << "Error: a TX_MEM.* event found in position " << i << " which is not supported. Reorder the events in the command line such that TX_MEM events are at positions 0..3.\n"; return -1; } regs[i].fields.event_select = event; regs[i].fields.umask = eventDefinition[events[i]].umask; } } bool rtm_support = m->supportsRTM(); if (!rtm_support) { if (!force) { cerr << "No RTM support detected, use -F if you still want to run this program.\n"; exit(EXIT_FAILURE); } cerr << "No RTM support detected, but -F found as argument, running anyway.\n"; } print_pid_collection_message(pid); PCM::ErrorCode status = m->program(PCM::EXT_CUSTOM_CORE_EVENTS, &conf, false, pid); m->checkError(status); print_cpu_details(); uint64 BeforeTime = 0, AfterTime = 0; SystemCounterState SysBeforeState, SysAfterState; const uint32 ncores = m->getNumCores(); std::vector BeforeState, AfterState; std::vector DummySocketStates; if ((sysCmd != NULL) && (delay <= 0.0)) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } if (csv) { if (delay <= 0.0) delay = PCM_DELAY_DEFAULT; } else { // for non-CSV mode delay < 1.0 does not make a lot of practical sense: // hard to read from the screen, or // in case delay is not provided in command line => set default if (((delay < 1.0) && (delay > 0.0)) || (delay <= 0.0)) delay = PCM_DELAY_DEFAULT; } cerr << "Update every " << delay << " seconds\n"; std::cout.precision(2); std::cout << std::fixed; BeforeTime = m->getTickCount(); m->getAllCounterStates(SysBeforeState, DummySocketStates, BeforeState); if (sysCmd != NULL) { MySystem(sysCmd, sysArgv); } mainLoop([&]() { if (!csv) cout << std::flush; calibratedSleep(delay, sysCmd, mainLoop, m); AfterTime = m->getTickCount(); m->getAllCounterStates(SysAfterState, DummySocketStates, AfterState); cout << "Time elapsed: " << dec << fixed << AfterTime - BeforeTime << " ms\n"; //cout << "Called sleep function for " <isBlocked()) { // in case PCM was blocked after spawning child application: break monitoring loop here return false; } return true; }); exit(EXIT_SUCCESS); } pcm-202307/src/pcm.cpp000066400000000000000000001627271445420033100144550ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev, // Thomas Willhalm, // Patrick Ungerer /*! \file pcm.cpp \brief Example of using CPU counters: implements a simple performance counter monitoring utility */ #include #ifdef _MSC_VER #include #include "windows/windriver.h" #else #include #include // for atexit() #include // for gettimeofday() #endif #include #include #include #include #include #include #include #include #include #include "cpucounters.h" #include "utils.h" #define SIZE (10000000) #define PCM_DELAY_DEFAULT 1.0 // in seconds #define PCM_DELAY_MIN 0.015 // 15 milliseconds is practical on most modern CPUs #define MAX_CORES 4096 using namespace std; using namespace pcm; template double float_format(IntType n) { return double(n) / 1e6; } std::string temp_format(int32 t) { char buffer[1024]; if (t == PCM_INVALID_THERMAL_HEADROOM) return "N/A"; snprintf(buffer, 1024, "%2d", t); return buffer; } std::string l3cache_occ_format(uint64 o) { char buffer[1024]; if (o == PCM_INVALID_QOS_MONITORING_DATA) return "N/A"; snprintf(buffer, 1024, "%6u", (uint32) o); return buffer; } template double getAverageUncoreFrequencyGhz(const UncoreStateType& before, const UncoreStateType& after) // in GHz { return getAverageUncoreFrequency(before, after) / 1e9; } void print_help(const string & prog_name) { cout << "\n Usage: \n " << prog_name << " --help | [delay] [options] [-- external_program [external_program_options]]\n"; cout << " => time interval to sample performance counters.\n"; cout << " If not specified, or 0, with external program given\n"; cout << " will read counters only after external program finishes\n"; cout << " Supported are: \n"; cout << " -h | --help | /h => print this help and exit\n"; cout << " -silent => silence information output and print only measurements\n"; cout << " --version => print application version\n"; cout << " -pid PID | /pid PID => collect core metrics only for specified process ID\n"; #ifdef _MSC_VER cout << " --uninstallDriver | --installDriver=> (un)install driver\n"; #endif cout << " -r | --reset | /reset => reset PMU configuration (at your own risk)\n"; cout << " -nc | --nocores | /nc => hide core related output\n"; cout << " -yc | --yescores | /yc => enable specific cores to output\n"; cout << " -ns | --nosockets | /ns => hide socket related output\n"; cout << " -nsys | --nosystem | /nsys => hide system related output\n"; cout << " -csv[=file.csv] | /csv[=file.csv] => output compact CSV format to screen or\n" << " to a file, in case filename is provided\n" << " the format used is documented here: https://www.intel.com/content/www/us/en/developer/articles/technical/intel-pcm-column-names-decoder-ring.html\n"; cout << " -i[=number] | /i[=number] => allow to determine number of iterations\n"; print_enforce_flush_option_help(); print_help_force_rtm_abort_mode(37); cout << " Examples:\n"; cout << " " << prog_name << " 1 -nc -ns => print counters every second without core and socket output\n"; cout << " " << prog_name << " 1 -i=10 => print counters every second 10 times and exit\n"; cout << " " << prog_name << " 0.5 -csv=test.log => twice a second save counter values to test.log in CSV format\n"; cout << " " << prog_name << " /csv 5 2>/dev/null => one sample every 5 seconds, and discard all diagnostic output\n"; cout << "\n"; } template void print_basic_metrics(const PCM * m, const State & state1, const State & state2) { cout << " " << getExecUsage(state1, state2) << " " << getIPC(state1, state2) << " " << getRelativeFrequency(state1, state2); if (m->isActiveRelativeFrequencyAvailable()) cout << " " << getActiveRelativeFrequency(state1, state2); if (m->isL3CacheMissesAvailable()) cout << " " << unit_format(getL3CacheMisses(state1, state2)); if (m->isL2CacheMissesAvailable()) cout << " " << unit_format(getL2CacheMisses(state1, state2)); if (m->isL3CacheHitRatioAvailable()) cout << " " << getL3CacheHitRatio(state1, state2); if (m->isL2CacheHitRatioAvailable()) cout << " " << getL2CacheHitRatio(state1, state2); cout.precision(4); if (m->isL3CacheMissesAvailable()) cout << " " << double(getL3CacheMisses(state1, state2)) / getInstructionsRetired(state1, state2); if (m->isL2CacheMissesAvailable()) cout << " " << double(getL2CacheMisses(state1, state2)) / getInstructionsRetired(state1, state2); cout.precision(2); } template void print_other_metrics(const PCM * m, const State & state1, const State & state2) { if (m->L3CacheOccupancyMetricAvailable()) cout << " " << setw(6) << l3cache_occ_format(getL3CacheOccupancy(state2)); if (m->CoreLocalMemoryBWMetricAvailable()) cout << " " << setw(6) << getLocalMemoryBW(state1, state2); if (m->CoreRemoteMemoryBWMetricAvailable()) cout << " " << setw(6) << getRemoteMemoryBW(state1, state2); cout << " " << temp_format(state2.getThermalHeadroom()) << "\n"; } void print_output(PCM * m, const std::vector & cstates1, const std::vector & cstates2, const std::vector & sktstate1, const std::vector & sktstate2, const std::bitset & ycores, const SystemCounterState& sstate1, const SystemCounterState& sstate2, const int cpu_model, const bool show_core_output, const bool show_partial_core_output, const bool show_socket_output, const bool show_system_output ) { cout << "\n"; cout << " EXEC : instructions per nominal CPU cycle\n"; cout << " IPC : instructions per CPU cycle\n"; cout << " FREQ : relation to nominal CPU frequency='unhalted clock ticks'/'invariant timer ticks' (includes Intel Turbo Boost)\n"; if (m->isActiveRelativeFrequencyAvailable()) cout << " AFREQ : relation to nominal CPU frequency while in active state (not in power-saving C state)='unhalted clock ticks'/'invariant timer ticks while in C0-state' (includes Intel Turbo Boost)\n"; if (m->isL3CacheMissesAvailable()) cout << " L3MISS: L3 (read) cache misses \n"; if (m->isL2CacheHitsAvailable()) { if (m->isAtom() || cpu_model == PCM::KNL) cout << " L2MISS: L2 (read) cache misses \n"; else cout << " L2MISS: L2 (read) cache misses (including other core's L2 cache *hits*) \n"; } if (m->isL3CacheHitRatioAvailable()) cout << " L3HIT : L3 (read) cache hit ratio (0.00-1.00)\n"; if (m->isL2CacheHitRatioAvailable()) cout << " L2HIT : L2 cache hit ratio (0.00-1.00)\n"; if (m->isL3CacheMissesAvailable()) cout << " L3MPI : number of L3 (read) cache misses per instruction\n"; if (m->isL2CacheMissesAvailable()) cout << " L2MPI : number of L2 (read) cache misses per instruction\n"; if (m->memoryTrafficMetricsAvailable()) cout << " READ : bytes read from main memory controller (in GBytes)\n"; if (m->memoryTrafficMetricsAvailable()) cout << " WRITE : bytes written to main memory controller (in GBytes)\n"; if (m->localMemoryRequestRatioMetricAvailable()) cout << " LOCAL : ratio of local memory requests to memory controller in %\n"; if (m->LLCReadMissLatencyMetricsAvailable()) cout << "LLCRDMISSLAT: average latency of last level cache miss for reads and prefetches (in ns)\n"; if (m->PMMTrafficMetricsAvailable()) cout << " PMM RD : bytes read from PMM memory (in GBytes)\n"; if (m->PMMTrafficMetricsAvailable()) cout << " PMM WR : bytes written to PMM memory (in GBytes)\n"; if (m->HBMmemoryTrafficMetricsAvailable()) cout << " HBM READ : bytes read from HBM controller (in GBytes)\n"; if (m->HBMmemoryTrafficMetricsAvailable()) cout << " HBM WRITE : bytes written to HBM controller (in GBytes)\n"; if (m->memoryIOTrafficMetricAvailable()) { cout << " IO : bytes read/written due to IO requests to memory controller (in GBytes); this may be an over estimate due to same-cache-line partial requests\n"; cout << " IA : bytes read/written due to IA requests to memory controller (in GBytes); this may be an over estimate due to same-cache-line partial requests\n"; cout << " GT : bytes read/written due to GT requests to memory controller (in GBytes); this may be an over estimate due to same-cache-line partial requests\n"; } if (m->L3CacheOccupancyMetricAvailable()) cout << " L3OCC : L3 occupancy (in KBytes)\n"; if (m->CoreLocalMemoryBWMetricAvailable()) cout << " LMB : L3 cache external bandwidth satisfied by local memory (in MBytes)\n"; if (m->CoreRemoteMemoryBWMetricAvailable()) cout << " RMB : L3 cache external bandwidth satisfied by remote memory (in MBytes)\n"; cout << " TEMP : Temperature reading in 1 degree Celsius relative to the TjMax temperature (thermal headroom): 0 corresponds to the max temperature\n"; cout << " energy: Energy in Joules\n"; cout << "\n"; cout << "\n"; const char * longDiv = "---------------------------------------------------------------------------------------------------------------\n"; cout.precision(2); cout << std::fixed; if (cpu_model == PCM::KNL) cout << " Proc Tile Core Thread |"; else cout << " Core (SKT) |"; cout << " EXEC | IPC | FREQ |"; if (m->isActiveRelativeFrequencyAvailable()) cout << " AFREQ |"; if (m->isL3CacheMissesAvailable()) cout << " L3MISS |"; if (m->isL2CacheMissesAvailable()) cout << " L2MISS |"; if (m->isL3CacheHitRatioAvailable()) cout << " L3HIT |"; if (m->isL2CacheHitRatioAvailable()) cout << " L2HIT |"; if (m->isL3CacheMissesAvailable()) cout << " L3MPI |"; if (m->isL2CacheMissesAvailable()) cout << " L2MPI | "; if (m->L3CacheOccupancyMetricAvailable()) cout << " L3OCC |"; if (m->CoreLocalMemoryBWMetricAvailable()) cout << " LMB |"; if (m->CoreRemoteMemoryBWMetricAvailable()) cout << " RMB |"; cout << " TEMP\n\n"; if (show_core_output) { for (uint32 i = 0; i < m->getNumCores(); ++i) { if (m->isCoreOnline(i) == false || (show_partial_core_output && ycores.test(i) == false)) continue; if (cpu_model == PCM::KNL) cout << setfill(' ') << internal << setw(5) << i << setw(5) << m->getTileId(i) << setw(5) << m->getCoreId(i) << setw(7) << m->getThreadId(i); else cout << " " << setw(3) << i << " " << setw(2) << m->getSocketId(i); print_basic_metrics(m, cstates1[i], cstates2[i]); print_other_metrics(m, cstates1[i], cstates2[i]); } } if (show_socket_output) { if (!(m->getNumSockets() == 1 && (m->isAtom() || cpu_model == PCM::KNL))) { cout << longDiv; for (uint32 i = 0; i < m->getNumSockets(); ++i) { cout << " SKT " << setw(2) << i; print_basic_metrics(m, sktstate1[i], sktstate2[i]); print_other_metrics(m, sktstate1[i], sktstate2[i]); } } } cout << longDiv; if (show_system_output) { if (cpu_model == PCM::KNL) cout << setw(22) << left << " TOTAL" << internal << setw(7-5); else cout << " TOTAL *"; print_basic_metrics(m, sstate1, sstate2); if (m->L3CacheOccupancyMetricAvailable()) cout << " N/A "; if (m->CoreLocalMemoryBWMetricAvailable()) cout << " N/A "; if (m->CoreRemoteMemoryBWMetricAvailable()) cout << " N/A "; cout << " N/A\n"; cout << "\n Instructions retired: " << unit_format(getInstructionsRetired(sstate1, sstate2)) << " ; Active cycles: " << unit_format(getCycles(sstate1, sstate2)) << " ; Time (TSC): " << unit_format(getInvariantTSC(cstates1[0], cstates2[0])) << "ticks ; C0 (active,non-halted) core residency: " << (getCoreCStateResidency(0, sstate1, sstate2)*100.) << " %\n"; cout << "\n"; for (int s = 1; s <= PCM::MAX_C_STATE; ++s) { if (m->isCoreCStateResidencySupported(s)) { std::cout << " C" << s << " core residency: " << (getCoreCStateResidency(s, sstate1, sstate2)*100.) << " %;"; } } cout << "\n"; std::vector CoreCStateStackedBar, PackageCStateStackedBar; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) { std::ostringstream sstr(std::ostringstream::out); sstr << std::hex << s; const char fill = sstr.str().c_str()[0]; if (m->isCoreCStateResidencySupported(s)) { CoreCStateStackedBar.push_back(StackedBarItem(getCoreCStateResidency(s, sstate1, sstate2), "", fill)); } if (m->isPackageCStateResidencySupported(s)) { std::cout << " C" << s << " package residency: " << (getPackageCStateResidency(s, sstate1, sstate2)*100.) << " %;"; PackageCStateStackedBar.push_back(StackedBarItem(getPackageCStateResidency(s, sstate1, sstate2), "", fill)); } } cout << "\n"; drawStackedBar(" Core C-state distribution", CoreCStateStackedBar, 80); drawStackedBar(" Package C-state distribution", PackageCStateStackedBar, 80); if (m->getNumCores() == m->getNumOnlineCores()) { cout << "\n PHYSICAL CORE IPC : " << getCoreIPC(sstate1, sstate2) << " => corresponds to " << 100. * (getCoreIPC(sstate1, sstate2) / double(m->getMaxIPC())) << " % utilization for cores in active state"; cout << "\n Instructions per nominal CPU cycle: " << getTotalExecUsage(sstate1, sstate2) << " => corresponds to " << 100. * (getTotalExecUsage(sstate1, sstate2) / double(m->getMaxIPC())) << " % core utilization over time interval\n"; } if (m->isHWTMAL1Supported()) { cout << " Pipeline stalls: Frontend bound: " << int(100. * getFrontendBound(sstate1, sstate2)) << " %, bad Speculation: " << int(100. * getBadSpeculation(sstate1, sstate2)) << " %, Backend bound: " << int(100. * getBackendBound(sstate1, sstate2)) << " %, Retiring: " << int(100. * getRetiring(sstate1, sstate2)) << " %\n"; std::vector TMAStackedBar; TMAStackedBar.push_back(StackedBarItem(getFrontendBound(sstate1, sstate2), "", 'F')); TMAStackedBar.push_back(StackedBarItem(getBadSpeculation(sstate1, sstate2), "", 'S')); TMAStackedBar.push_back(StackedBarItem(getBackendBound(sstate1, sstate2), "", 'B')); TMAStackedBar.push_back(StackedBarItem(getRetiring(sstate1, sstate2), "", 'R')); drawStackedBar(" Pipeline stall distribution ", TMAStackedBar, 80); cout << "\n"; } cout << " SMI count: " << getSMICount(sstate1, sstate2) << "\n"; } if (show_socket_output) { if (m->getNumSockets() > 1 && m->incomingQPITrafficMetricsAvailable()) // QPI info only for multi socket systems { cout << "\nIntel(r) " << m->xPI() << " data traffic estimation in bytes (data traffic coming to CPU/socket through " << m->xPI() << " links):\n\n"; const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); cout << " "; for (uint32 i = 0; i < qpiLinks; ++i) cout << " " << m->xPI() << i << " "; if (m->qpiUtilizationMetricsAvailable()) { cout << "| "; for (uint32 i = 0; i < qpiLinks; ++i) cout << " " << m->xPI() << i << " "; } cout << "\n" << longDiv; for (uint32 i = 0; i < m->getNumSockets(); ++i) { cout << " SKT " << setw(2) << i << " "; for (uint32 l = 0; l < qpiLinks; ++l) cout << unit_format(getIncomingQPILinkBytes(i, l, sstate1, sstate2)) << " "; if (m->qpiUtilizationMetricsAvailable()) { cout << "| "; for (uint32 l = 0; l < qpiLinks; ++l) cout << setw(3) << std::dec << int(100. * getIncomingQPILinkUtilization(i, l, sstate1, sstate2)) << "% "; } cout << "\n"; } } } if (show_system_output) { cout << longDiv; if (m->getNumSockets() > 1 && m->incomingQPITrafficMetricsAvailable()) // QPI info only for multi socket systems cout << "Total " << m->xPI() << " incoming data traffic: " << unit_format(getAllIncomingQPILinkBytes(sstate1, sstate2)) << " " << m->xPI() << " data traffic/Memory controller traffic: " << getQPItoMCTrafficRatio(sstate1, sstate2) << "\n"; } if (show_socket_output) { if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems { cout << "\nIntel(r) " << m->xPI() << " traffic estimation in bytes (data and non-data traffic outgoing from CPU/socket through " << m->xPI() << " links):\n\n"; const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); cout << " "; for (uint32 i = 0; i < qpiLinks; ++i) cout << " " << m->xPI() << i << " "; cout << "| "; for (uint32 i = 0; i < qpiLinks; ++i) cout << " " << m->xPI() << i << " "; cout << "\n" << longDiv; for (uint32 i = 0; i < m->getNumSockets(); ++i) { cout << " SKT " << setw(2) << i << " "; for (uint32 l = 0; l < qpiLinks; ++l) cout << unit_format(getOutgoingQPILinkBytes(i, l, sstate1, sstate2)) << " "; cout << "| "; for (uint32 l = 0; l < qpiLinks; ++l) cout << setw(3) << std::dec << int(100. * getOutgoingQPILinkUtilization(i, l, sstate1, sstate2)) << "% "; cout << "\n"; } cout << longDiv; cout << "Total " << m->xPI() << " outgoing data and non-data traffic: " << unit_format(getAllOutgoingQPILinkBytes(sstate1, sstate2)) << "\n"; } } if (show_socket_output) { cout << "MEM (GB)->|"; if (m->memoryTrafficMetricsAvailable()) cout << " READ | WRITE |"; if (m->localMemoryRequestRatioMetricAvailable()) cout << " LOCAL |"; if (m->PMMTrafficMetricsAvailable()) cout << " PMM RD | PMM WR |"; if (m->HBMmemoryTrafficMetricsAvailable()) cout << " HBM READ | HBM WRITE |"; if (m->memoryIOTrafficMetricAvailable()) cout << " IO |"; if (m->memoryIOTrafficMetricAvailable()) cout << " IA |"; if (m->memoryIOTrafficMetricAvailable()) cout << " GT |"; if (m->packageEnergyMetricsAvailable()) cout << " CPU energy |"; if (m->dramEnergyMetricsAvailable()) cout << " DIMM energy |"; if (m->LLCReadMissLatencyMetricsAvailable()) cout << " LLCRDMISSLAT (ns)|"; if (m->uncoreFrequencyMetricAvailable()) cout << " UncFREQ (Ghz)|"; cout << "\n"; cout << longDiv; for (uint32 i = 0; i < m->getNumSockets(); ++i) { cout << " SKT " << setw(2) << i; if (m->memoryTrafficMetricsAvailable()) cout << " " << setw(5) << getBytesReadFromMC(sktstate1[i], sktstate2[i]) / double(1e9) << " " << setw(5) << getBytesWrittenToMC(sktstate1[i], sktstate2[i]) / double(1e9); if (m->localMemoryRequestRatioMetricAvailable()) cout << " " << setw(3) << int(100.* getLocalMemoryRequestRatio(sktstate1[i], sktstate2[i])) << " %"; if (m->PMMTrafficMetricsAvailable()) cout << " " << setw(5) << getBytesReadFromPMM(sktstate1[i], sktstate2[i]) / double(1e9) << " " << setw(5) << getBytesWrittenToPMM(sktstate1[i], sktstate2[i]) / double(1e9); if (m->HBMmemoryTrafficMetricsAvailable()) cout << " " << setw(11) << getBytesReadFromEDC(sktstate1[i], sktstate2[i]) / double(1e9) << " " << setw(11) << getBytesWrittenToEDC(sktstate1[i], sktstate2[i]) / double(1e9); if (m->memoryIOTrafficMetricAvailable()) { cout << " " << setw(5) << getIORequestBytesFromMC(sktstate1[i], sktstate2[i]) / double(1e9); cout << " " << setw(5) << getIARequestBytesFromMC(sktstate1[i], sktstate2[i]) / double(1e9); cout << " " << setw(5) << getGTRequestBytesFromMC(sktstate1[i], sktstate2[i]) / double(1e9); } if(m->packageEnergyMetricsAvailable()) { cout << " "; cout << setw(6) << getConsumedJoules(sktstate1[i], sktstate2[i]); } if(m->dramEnergyMetricsAvailable()) { cout << " "; cout << setw(6) << getDRAMConsumedJoules(sktstate1[i], sktstate2[i]); } if (m->LLCReadMissLatencyMetricsAvailable()) { cout << " "; cout << setw(6) << getLLCReadMissLatency(sktstate1[i], sktstate2[i]); } if (m->uncoreFrequencyMetricAvailable()) { cout << " "; cout << setw(4) << getAverageUncoreFrequencyGhz(sktstate1[i], sktstate2[i]); } cout << "\n"; } cout << longDiv; if (m->getNumSockets() > 1) { cout << " *"; if (m->memoryTrafficMetricsAvailable()) cout << " " << setw(5) << getBytesReadFromMC(sstate1, sstate2) / double(1e9) << " " << setw(5) << getBytesWrittenToMC(sstate1, sstate2) / double(1e9); if (m->localMemoryRequestRatioMetricAvailable()) cout << " " << setw(3) << int(100.* getLocalMemoryRequestRatio(sstate1, sstate2)) << " %"; if (m->PMMTrafficMetricsAvailable()) cout << " " << setw(5) << getBytesReadFromPMM(sstate1, sstate2) / double(1e9) << " " << setw(5) << getBytesWrittenToPMM(sstate1, sstate2) / double(1e9); if (m->memoryIOTrafficMetricAvailable()) cout << " " << setw(5) << getIORequestBytesFromMC(sstate1, sstate2) / double(1e9); if (m->packageEnergyMetricsAvailable()) { cout << " "; cout << setw(6) << getConsumedJoules(sstate1, sstate2); } if (m->dramEnergyMetricsAvailable()) { cout << " "; cout << setw(6) << getDRAMConsumedJoules(sstate1, sstate2); } if (m->LLCReadMissLatencyMetricsAvailable()) { cout << " "; cout << setw(6) << getLLCReadMissLatency(sstate1, sstate2); } if (m->uncoreFrequencyMetricAvailable()) { cout << " "; cout << setw(4) << getAverageUncoreFrequencyGhz(sstate1, sstate2); } cout << "\n"; } } } void print_basic_metrics_csv_header(const PCM * m) { cout << "EXEC,IPC,FREQ,"; if (m->isActiveRelativeFrequencyAvailable()) cout << "AFREQ,"; if (m->isL3CacheMissesAvailable()) cout << "L3MISS,"; if (m->isL2CacheMissesAvailable()) cout << "L2MISS,"; if (m->isL3CacheHitRatioAvailable()) cout << "L3HIT,"; if (m->isL2CacheHitRatioAvailable()) cout << "L2HIT,"; if (m->isL3CacheMissesAvailable()) cout << "L3MPI,"; if (m->isL2CacheMissesAvailable()) cout << "L2MPI,"; if (m->isHWTMAL1Supported()) cout << "Frontend_bound(%),Bad_Speculation(%),Backend_Bound(%),Retiring(%),"; } void print_csv_header_helper(const string & header, int count=1){ for(int i = 0; i < count; i++){ cout << header << ","; } } void print_basic_metrics_csv_semicolons(const PCM * m, const string & header) { print_csv_header_helper(header, 3); // EXEC;IPC;FREQ; if (m->isActiveRelativeFrequencyAvailable()) print_csv_header_helper(header); // AFREQ; if (m->isL3CacheMissesAvailable()) print_csv_header_helper(header); // L3MISS; if (m->isL2CacheMissesAvailable()) print_csv_header_helper(header); // L2MISS; if (m->isL3CacheHitRatioAvailable()) print_csv_header_helper(header); // L3HIT if (m->isL2CacheHitRatioAvailable()) print_csv_header_helper(header); // L2HIT; if (m->isL3CacheMissesAvailable()) print_csv_header_helper(header); // L3MPI; if (m->isL2CacheMissesAvailable()) print_csv_header_helper(header); // L2MPI; if (m->isHWTMAL1Supported()) print_csv_header_helper(header, 4); // Frontend_bound(%),Bad_Speculation(%),Backend_Bound(%),Retiring(%) } void print_csv_header(PCM * m, const std::bitset & ycores, const int /*cpu_model*/, const bool show_core_output, const bool show_partial_core_output, const bool show_socket_output, const bool show_system_output ) { // print first header line string header; header = "System"; print_csv_header_helper(header,2); if (show_system_output) { print_basic_metrics_csv_semicolons(m,header); if (m->memoryTrafficMetricsAvailable()) print_csv_header_helper(header,2); if (m->localMemoryRequestRatioMetricAvailable()) print_csv_header_helper(header); if (m->PMMTrafficMetricsAvailable()) print_csv_header_helper(header,2); if (m->HBMmemoryTrafficMetricsAvailable()) print_csv_header_helper(header,2); print_csv_header_helper(header,7); if (m->getNumSockets() > 1) { // QPI info only for multi socket systems if (m->incomingQPITrafficMetricsAvailable()) print_csv_header_helper(header,2); if (m->outgoingQPITrafficMetricsAvailable()) print_csv_header_helper(header); } for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) print_csv_header_helper("System Core C-States"); for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isPackageCStateResidencySupported(s)) print_csv_header_helper("System Pack C-States"); if (m->packageEnergyMetricsAvailable()) print_csv_header_helper(header); if (m->dramEnergyMetricsAvailable()) print_csv_header_helper(header); if (m->LLCReadMissLatencyMetricsAvailable()) print_csv_header_helper(header); if (m->uncoreFrequencyMetricAvailable()) print_csv_header_helper(header); } if (show_socket_output) { for (uint32 i = 0; i < m->getNumSockets(); ++i) { header = "Socket " + std::to_string(i); print_basic_metrics_csv_semicolons(m,header); if (m->L3CacheOccupancyMetricAvailable()) print_csv_header_helper(header); if (m->CoreLocalMemoryBWMetricAvailable()) print_csv_header_helper(header); if (m->CoreRemoteMemoryBWMetricAvailable()) print_csv_header_helper(header); if (m->memoryTrafficMetricsAvailable()) print_csv_header_helper(header,2); if (m->localMemoryRequestRatioMetricAvailable()) print_csv_header_helper(header); if (m->PMMTrafficMetricsAvailable()) print_csv_header_helper(header,2); if (m->HBMmemoryTrafficMetricsAvailable()) print_csv_header_helper(header,2); if (m->memoryIOTrafficMetricAvailable()) print_csv_header_helper(header,3); print_csv_header_helper(header, 8); //TEMP,INST,ACYC,TIME(ticks),PhysIPC,PhysIPC%,INSTnom,INSTnom%, } if (m->getNumSockets() > 1 && (m->incomingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems { const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); for (uint32 s = 0; s < m->getNumSockets(); ++s) { header = "SKT" + std::to_string(s) + "dataIn"; print_csv_header_helper(header,qpiLinks); if (m->qpiUtilizationMetricsAvailable()) { header = "SKT" + std::to_string(s) + "dataIn (percent)"; print_csv_header_helper(header,qpiLinks); } } } if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems { const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); for (uint32 s = 0; s < m->getNumSockets(); ++s) { header = "SKT" + std::to_string(s) + "trafficOut"; print_csv_header_helper(header,qpiLinks); header = "SKT" + std::to_string(s) + "trafficOut (percent)"; print_csv_header_helper(header,qpiLinks); } } for (uint32 i = 0; i < m->getNumSockets(); ++i) { header = "SKT" + std::to_string(i) + " Core C-State"; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) print_csv_header_helper(header); header = "SKT" + std::to_string(i) + " Package C-State"; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isPackageCStateResidencySupported(s)) print_csv_header_helper(header); } if (m->packageEnergyMetricsAvailable()) { header = "Proc Energy (Joules)"; print_csv_header_helper(header,m->getNumSockets()); } if (m->dramEnergyMetricsAvailable()) { header = "DRAM Energy (Joules)"; print_csv_header_helper(header,m->getNumSockets()); } if (m->LLCReadMissLatencyMetricsAvailable()) { header = "LLCRDMISSLAT (ns)"; print_csv_header_helper(header,m->getNumSockets()); } if (m->uncoreFrequencyMetricAvailable()) { header = "UncFREQ (Ghz)"; print_csv_header_helper(header, m->getNumSockets()); } } if (show_core_output) { for (uint32 i = 0; i < m->getNumCores(); ++i) { if (show_partial_core_output && ycores.test(i) == false) continue; std::stringstream hstream; hstream << "Core" << i << " (Socket" << setw(2) << m->getSocketId(i) << ")"; header = hstream.str(); print_basic_metrics_csv_semicolons(m,header); if (m->L3CacheOccupancyMetricAvailable()) print_csv_header_helper(header); if (m->CoreLocalMemoryBWMetricAvailable()) print_csv_header_helper(header); if (m->CoreRemoteMemoryBWMetricAvailable()) print_csv_header_helper(header); for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) print_csv_header_helper(header); print_csv_header_helper(header);// TEMP print_csv_header_helper(header,7); //ACYC,TIME(ticks),PhysIPC,PhysIPC%,INSTnom,INSTnom%, } } // print second header line cout << "\n"; printDateForCSV(Header2); if (show_system_output) { print_basic_metrics_csv_header(m); if (m->memoryTrafficMetricsAvailable()) cout << "READ,WRITE,"; if (m->localMemoryRequestRatioMetricAvailable()) cout << "LOCAL,"; if (m->PMMTrafficMetricsAvailable()) cout << "PMM_RD,PMM_WR,"; if (m->HBMmemoryTrafficMetricsAvailable()) cout << "HBM_READ,HBM_WRITE,"; cout << "INST,ACYC,TIME(ticks),PhysIPC,PhysIPC%,INSTnom,INSTnom%,"; if (m->getNumSockets() > 1) { // QPI info only for multi socket systems if (m->incomingQPITrafficMetricsAvailable()) cout << "Total" << m->xPI() << "in," << m->xPI() << "toMC,"; if (m->outgoingQPITrafficMetricsAvailable()) cout << "Total" << m->xPI() << "out,"; } for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) cout << "C" << s << "res%,"; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isPackageCStateResidencySupported(s)) cout << "C" << s << "res%,"; if (m->packageEnergyMetricsAvailable()) cout << "Proc Energy (Joules),"; if (m->dramEnergyMetricsAvailable()) cout << "DRAM Energy (Joules),"; if (m->LLCReadMissLatencyMetricsAvailable()) cout << "LLCRDMISSLAT (ns),"; if (m->uncoreFrequencyMetricAvailable()) cout << "UncFREQ (Ghz),"; } if (show_socket_output) { for (uint32 i = 0; i < m->getNumSockets(); ++i) { print_basic_metrics_csv_header(m); if (m->L3CacheOccupancyMetricAvailable()) cout << "L3OCC,"; if (m->CoreLocalMemoryBWMetricAvailable()) cout << "LMB,"; if (m->CoreRemoteMemoryBWMetricAvailable()) cout << "RMB,"; if (m->memoryTrafficMetricsAvailable()) cout << "READ,WRITE,"; if (m->localMemoryRequestRatioMetricAvailable()) cout << "LOCAL,"; if (m->PMMTrafficMetricsAvailable()) cout << "PMM_RD,PMM_WR,"; if (m->HBMmemoryTrafficMetricsAvailable()) cout << "HBM_READ,HBM_WRITE,"; if (m->memoryIOTrafficMetricAvailable()) cout << "IO,IA,GT,"; cout << "TEMP,INST,ACYC,TIME(ticks),PhysIPC,PhysIPC%,INSTnom,INSTnom%,"; } if (m->getNumSockets() > 1 && (m->incomingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems { const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 i = 0; i < qpiLinks; ++i) cout << m->xPI() << i << ","; if (m->qpiUtilizationMetricsAvailable()) for (uint32 i = 0; i < qpiLinks; ++i) cout << m->xPI() << i << ","; } } if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems { const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); for (uint32 s = 0; s < m->getNumSockets(); ++s) { for (uint32 i = 0; i < qpiLinks; ++i) cout << m->xPI() << i << ","; for (uint32 i = 0; i < qpiLinks; ++i) cout << m->xPI() << i << ","; } } for (uint32 i = 0; i < m->getNumSockets(); ++i) { for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) cout << "C" << s << "res%,"; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isPackageCStateResidencySupported(s)) cout << "C" << s << "res%,"; } if (m->packageEnergyMetricsAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << "SKT" << i << ","; } if (m->dramEnergyMetricsAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << "SKT" << i << ","; } if (m->LLCReadMissLatencyMetricsAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << "SKT" << i << ","; } if (m->uncoreFrequencyMetricAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << "SKT" << i << ","; } } if (show_core_output) { for (uint32 i = 0; i < m->getNumCores(); ++i) { if (show_partial_core_output && ycores.test(i) == false) continue; print_basic_metrics_csv_header(m); if (m->L3CacheOccupancyMetricAvailable()) cout << "L3OCC,"; if (m->CoreLocalMemoryBWMetricAvailable()) cout << "LMB,"; if (m->CoreRemoteMemoryBWMetricAvailable()) cout << "RMB,"; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) cout << "C" << s << "res%,"; cout << "TEMP,"; cout << "INST,ACYC,TIME(ticks),PhysIPC,PhysIPC%,INSTnom,INSTnom%,"; } } } template void print_basic_metrics_csv(const PCM * m, const State & state1, const State & state2, const bool print_last_semicolon = true) { cout << getExecUsage(state1, state2) << ',' << getIPC(state1, state2) << ',' << getRelativeFrequency(state1, state2); if (m->isActiveRelativeFrequencyAvailable()) cout << ',' << getActiveRelativeFrequency(state1, state2); if (m->isL3CacheMissesAvailable()) cout << ',' << float_format(getL3CacheMisses(state1, state2)); if (m->isL2CacheMissesAvailable()) cout << ',' << float_format(getL2CacheMisses(state1, state2)); if (m->isL3CacheHitRatioAvailable()) cout << ',' << getL3CacheHitRatio(state1, state2); if (m->isL2CacheHitRatioAvailable()) cout << ',' << getL2CacheHitRatio(state1, state2); cout.precision(4); if (m->isL3CacheMissesAvailable()) cout << ',' << double(getL3CacheMisses(state1, state2)) / getInstructionsRetired(state1, state2); if (m->isL2CacheMissesAvailable()) cout << ',' << double(getL2CacheMisses(state1, state2)) / getInstructionsRetired(state1, state2); cout.precision(2); if (m->isHWTMAL1Supported()) { cout << ',' << int(100. * getFrontendBound(state1, state2)); cout << ',' << int(100. * getBadSpeculation(state1, state2)); cout << ',' << int(100. * getBackendBound(state1, state2)); cout << ',' << int(100. * getRetiring(state1, state2)); } if (print_last_semicolon) cout << ","; } template void print_other_metrics_csv(const PCM * m, const State & state1, const State & state2) { if (m->L3CacheOccupancyMetricAvailable()) cout << ',' << l3cache_occ_format(getL3CacheOccupancy(state2)); if (m->CoreLocalMemoryBWMetricAvailable()) cout << ',' << getLocalMemoryBW(state1, state2); if (m->CoreRemoteMemoryBWMetricAvailable()) cout << ',' << getRemoteMemoryBW(state1, state2); } void print_csv(PCM * m, const std::vector & cstates1, const std::vector & cstates2, const std::vector & sktstate1, const std::vector & sktstate2, const std::bitset & ycores, const SystemCounterState& sstate1, const SystemCounterState& sstate2, const int /*cpu_model*/, const bool show_core_output, const bool show_partial_core_output, const bool show_socket_output, const bool show_system_output ) { cout << "\n"; printDateForCSV(CsvOutputType::Data); if (show_system_output) { print_basic_metrics_csv(m, sstate1, sstate2); if (m->memoryTrafficMetricsAvailable()) cout << getBytesReadFromMC(sstate1, sstate2) / double(1e9) << ',' << getBytesWrittenToMC(sstate1, sstate2) / double(1e9) << ','; if (m->localMemoryRequestRatioMetricAvailable()) cout << int(100. * getLocalMemoryRequestRatio(sstate1, sstate2)) << ','; if (m->PMMTrafficMetricsAvailable()) cout << getBytesReadFromPMM(sstate1, sstate2) / double(1e9) << ',' << getBytesWrittenToPMM(sstate1, sstate2) / double(1e9) << ','; if (m->HBMmemoryTrafficMetricsAvailable()) cout << getBytesReadFromEDC(sstate1, sstate2) / double(1e9) << ',' << getBytesWrittenToEDC(sstate1, sstate2) / double(1e9) << ','; cout << float_format(getInstructionsRetired(sstate1, sstate2)) << "," << float_format(getCycles(sstate1, sstate2)) << "," << float_format(getInvariantTSC(cstates1[0], cstates2[0])) << "," << getCoreIPC(sstate1, sstate2) << "," << 100. * (getCoreIPC(sstate1, sstate2) / double(m->getMaxIPC())) << "," << getTotalExecUsage(sstate1, sstate2) << "," << 100. * (getTotalExecUsage(sstate1, sstate2) / double(m->getMaxIPC())) << ","; if (m->getNumSockets() > 1) { // QPI info only for multi socket systems if (m->incomingQPITrafficMetricsAvailable()) cout << float_format(getAllIncomingQPILinkBytes(sstate1, sstate2)) << "," << getQPItoMCTrafficRatio(sstate1, sstate2) << ","; if (m->outgoingQPITrafficMetricsAvailable()) cout << float_format(getAllOutgoingQPILinkBytes(sstate1, sstate2)) << ","; } for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) cout << getCoreCStateResidency(s, sstate1, sstate2) * 100 << ","; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isPackageCStateResidencySupported(s)) cout << getPackageCStateResidency(s, sstate1, sstate2) * 100 << ","; if (m->packageEnergyMetricsAvailable()) cout << getConsumedJoules(sstate1, sstate2) << ","; if (m->dramEnergyMetricsAvailable()) cout << getDRAMConsumedJoules(sstate1, sstate2) << ","; if (m->LLCReadMissLatencyMetricsAvailable()) cout << getLLCReadMissLatency(sstate1, sstate2) << ","; if (m->uncoreFrequencyMetricAvailable()) cout << getAverageUncoreFrequencyGhz(sstate1, sstate2) << ","; } if (show_socket_output) { for (uint32 i = 0; i < m->getNumSockets(); ++i) { print_basic_metrics_csv(m, sktstate1[i], sktstate2[i], false); print_other_metrics_csv(m, sktstate1[i], sktstate2[i]); if (m->memoryTrafficMetricsAvailable()) cout << ',' << getBytesReadFromMC(sktstate1[i], sktstate2[i]) / double(1e9) << ',' << getBytesWrittenToMC(sktstate1[i], sktstate2[i]) / double(1e9); if (m->localMemoryRequestRatioMetricAvailable()) cout << ',' << int(100. * getLocalMemoryRequestRatio(sktstate1[i], sktstate2[i])); if (m->PMMTrafficMetricsAvailable()) cout << ',' << getBytesReadFromPMM(sktstate1[i], sktstate2[i]) / double(1e9) << ',' << getBytesWrittenToPMM(sktstate1[i], sktstate2[i]) / double(1e9); if (m->HBMmemoryTrafficMetricsAvailable()) cout << ',' << getBytesReadFromEDC(sktstate1[i], sktstate2[i]) / double(1e9) << ',' << getBytesWrittenToEDC(sktstate1[i], sktstate2[i]) / double(1e9); if (m->memoryIOTrafficMetricAvailable()) { cout << ',' << getIORequestBytesFromMC(sktstate1[i], sktstate2[i]) / double(1e9) << ',' << getIARequestBytesFromMC(sktstate1[i], sktstate2[i]) / double(1e9) << ',' << getGTRequestBytesFromMC(sktstate1[i], sktstate2[i]) / double(1e9); } cout << ',' << temp_format(sktstate2[i].getThermalHeadroom()) << ','; cout << float_format(getInstructionsRetired(sktstate1[i], sktstate2[i])) << "," << float_format(getCycles(sktstate1[i], sktstate2[i])) << "," << float_format(getInvariantTSC(cstates1[0], cstates2[0])) << "," << getCoreIPC(sktstate1[i], sktstate2[i]) << "," << 100. * (getCoreIPC(sktstate1[i], sktstate2[i]) / double(m->getMaxIPC())) << "," << getTotalExecUsage(sktstate1[i], sktstate2[i]) << "," << 100. * (getTotalExecUsage(sktstate1[i], sktstate2[i]) / double(m->getMaxIPC())) << ","; } if (m->getNumSockets() > 1 && (m->incomingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems { const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); for (uint32 i = 0; i < m->getNumSockets(); ++i) { for (uint32 l = 0; l < qpiLinks; ++l) cout << float_format(getIncomingQPILinkBytes(i, l, sstate1, sstate2)) << ","; if (m->qpiUtilizationMetricsAvailable()) { for (uint32 l = 0; l < qpiLinks; ++l) cout << setw(3) << std::dec << int(100. * getIncomingQPILinkUtilization(i, l, sstate1, sstate2)) << "%,"; } } } if (m->getNumSockets() > 1 && (m->outgoingQPITrafficMetricsAvailable())) // QPI info only for multi socket systems { const uint32 qpiLinks = (uint32)m->getQPILinksPerSocket(); for (uint32 i = 0; i < m->getNumSockets(); ++i) { for (uint32 l = 0; l < qpiLinks; ++l) cout << float_format(getOutgoingQPILinkBytes(i, l, sstate1, sstate2)) << ","; for (uint32 l = 0; l < qpiLinks; ++l) cout << setw(3) << std::dec << int(100. * getOutgoingQPILinkUtilization(i, l, sstate1, sstate2)) << "%,"; } } for (uint32 i = 0; i < m->getNumSockets(); ++i) { for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) cout << getCoreCStateResidency(s, sktstate1[i], sktstate2[i]) * 100 << ","; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isPackageCStateResidencySupported(s)) cout << getPackageCStateResidency(s, sktstate1[i], sktstate2[i]) * 100 << ","; } if (m->packageEnergyMetricsAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << getConsumedJoules(sktstate1[i], sktstate2[i]) << ","; } if (m->dramEnergyMetricsAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << getDRAMConsumedJoules(sktstate1[i], sktstate2[i]) << " ,"; } if (m->LLCReadMissLatencyMetricsAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << getLLCReadMissLatency(sktstate1[i], sktstate2[i]) << " ,"; } if (m->uncoreFrequencyMetricAvailable()) { for (uint32 i = 0; i < m->getNumSockets(); ++i) cout << getAverageUncoreFrequencyGhz(sktstate1[i], sktstate2[i]) << ","; } } if (show_core_output) { for (uint32 i = 0; i < m->getNumCores(); ++i) { if (show_partial_core_output && ycores.test(i) == false) continue; print_basic_metrics_csv(m, cstates1[i], cstates2[i], false); print_other_metrics_csv(m, cstates1[i], cstates2[i]); cout << ','; for (int s = 0; s <= PCM::MAX_C_STATE; ++s) if (m->isCoreCStateResidencySupported(s)) cout << getCoreCStateResidency(s, cstates1[i], cstates2[i]) * 100 << ","; cout << temp_format(cstates2[i].getThermalHeadroom()) << ','; cout << float_format(getInstructionsRetired(cstates1[i], cstates2[i])) << "," << float_format(getCycles(cstates1[i], cstates2[i])) << "," << float_format(getInvariantTSC(cstates1[0], cstates2[0])) << "," << getCoreIPC(cstates1[i], cstates2[i]) << "," << 100. * (getCoreIPC(cstates1[i], cstates2[i]) / double(m->getMaxIPC())) << "," << getTotalExecUsage(cstates1[i], cstates2[i]) << "," << 100. * (getTotalExecUsage(cstates1[i], cstates2[i]) / double(m->getMaxIPC())) << ","; } } } PCM_MAIN_NOTHROW; int mainThrows(int argc, char * argv[]) { if(print_version(argc, argv)) exit(EXIT_SUCCESS); null_stream nullStream2; #ifdef PCM_FORCE_SILENT null_stream nullStream1; std::cout.rdbuf(&nullStream1); std::cerr.rdbuf(&nullStream2); #else check_and_set_silent(argc, argv, nullStream2); #endif set_signal_handlers(); cerr << "\n"; cerr << " Intel(r) Performance Counter Monitor " << PCM_VERSION << "\n"; cerr << "\n"; cerr << "\n"; // if delay is not specified: use either default (1 second), // or only read counters before or after PCM started: keep PCM blocked double delay = -1.0; int pid{-1}; char *sysCmd = NULL; char **sysArgv = NULL; bool show_core_output = true; bool show_partial_core_output = false; bool show_socket_output = true; bool show_system_output = true; bool csv_output = false; bool reset_pmu = false; bool disable_JKT_workaround = false; // as per http://software.intel.com/en-us/articles/performance-impact-when-sampling-certain-llc-events-on-snb-ep-with-vtune bool enforceFlush = false; parsePID(argc, argv, pid); MainLoop mainLoop; std::bitset ycores; string program = string(argv[0]); PCM * m = PCM::getInstance(); if (argc > 1) do { argv++; argc--; std::string arg_value; if (*argv == nullptr) { continue; } else if (check_argument_equals(*argv, {"--help", "-h", "/h"})) { print_help(program); exit(EXIT_FAILURE); } else if (check_argument_equals(*argv, {"-silent", "/silent"})) { // handled in check_and_set_silent continue; } else if (check_argument_equals(*argv, {"--yescores", "-yc", "/yc"})) { argv++; argc--; show_partial_core_output = true; if(*argv == NULL) { cerr << "Error: --yescores requires additional argument.\n"; exit(EXIT_FAILURE); } std::stringstream ss(*argv); while(ss.good()) { string s; int core_id; std::getline(ss, s, ','); if(s.empty()) continue; core_id = atoi(s.c_str()); if(core_id > MAX_CORES) { cerr << "Core ID:" << core_id << " exceed maximum range " << MAX_CORES << ", program abort\n"; exit(EXIT_FAILURE); } ycores.set(core_id, true); } if(m->getNumCores() > MAX_CORES) { cerr << "Error: --yescores option is enabled, but #define MAX_CORES " << MAX_CORES << " is less than m->getNumCores() = " << m->getNumCores() << "\n"; cerr << "There is a potential to crash the system. Please increase MAX_CORES to at least " << m->getNumCores() << " and re-enable this option.\n"; exit(EXIT_FAILURE); } continue; } else if (check_argument_equals(*argv, {"--nocores", "-nc", "/nc"})) { show_core_output = false; continue; } else if (check_argument_equals(*argv, {"--nosockets", "-ns", "/ns"})) { show_socket_output = false; continue; } else if (check_argument_equals(*argv, {"--nosystem", "-nsys", "/nsys"})) { show_system_output = false; continue; } else if (check_argument_equals(*argv, {"-csv", "/csv"})) { csv_output = true; } else if (extract_argument_value(*argv, {"-csv", "/csv"}, arg_value)) { csv_output = true; if (!arg_value.empty()) { m->setOutput(arg_value); } continue; } else if (isPIDOption(argv)) { argv++; argc--; continue; } else if (mainLoop.parseArg(*argv)) { continue; } else if (check_argument_equals(*argv, {"-reset", "/reset", "-r"})) { reset_pmu = true; continue; } else if (CheckAndForceRTMAbortMode(*argv, m)) { continue; } else if (check_argument_equals(*argv, {"--noJKTWA"})) { disable_JKT_workaround = true; continue; } PCM_ENFORCE_FLUSH_OPTION #ifdef _MSC_VER else if (check_argument_equals(*argv, {"--uninstallDriver"})) { Driver tmpDrvObject; tmpDrvObject.uninstall(); cerr << "msr.sys driver has been uninstalled. You might need to reboot the system to make this effective.\n"; exit(EXIT_SUCCESS); } else if (check_argument_equals(*argv, {"--installDriver"})) { Driver tmpDrvObject = Driver(Driver::msrLocalPath()); if (!tmpDrvObject.start()) { tcerr << "Can not access CPU counters\n"; tcerr << "You must have a signed driver at " << tmpDrvObject.driverPath() << " and have administrator rights to run this program\n"; exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } #endif else if (check_argument_equals(*argv, {"--"})) { argv++; sysCmd = *argv; sysArgv = argv; break; } else { delay = parse_delay(*argv, program, (print_usage_func)print_help); continue; } } while (argc > 1); // end of command line partsing loop if (disable_JKT_workaround) m->disableJKTWorkaround(); if (reset_pmu) { cerr << "\n Resetting PMU configuration\n"; m->resetPMU(); } // program() creates common semaphore for the singleton, so ideally to be called before any other references to PCM const PCM::ErrorCode status = m->program(PCM::DEFAULT_EVENTS, nullptr, false, pid); switch (status) { case PCM::Success: break; case PCM::MSRAccessDenied: cerr << "Access to Intel(r) Performance Counter Monitor has denied (no MSR or PCI CFG space access).\n"; exit(EXIT_FAILURE); case PCM::PMUBusy: cerr << "Access to Intel(r) Performance Counter Monitor has denied (Performance Monitoring Unit is occupied by other application). Try to stop the application that uses PMU.\n"; cerr << "Alternatively you can try running PCM with option -r to reset PMU.\n"; exit(EXIT_FAILURE); default: cerr << "Access to Intel(r) Performance Counter Monitor has denied (Unknown error).\n"; exit(EXIT_FAILURE); } print_cpu_details(); std::vector cstates1, cstates2; std::vector sktstate1, sktstate2; SystemCounterState sstate1, sstate2; const auto cpu_model = m->getCPUModel(); print_pid_collection_message(pid); if ((sysCmd != NULL) && (delay <= 0.0)) { // in case external command is provided in command line, and // delay either not provided (-1) or is zero m->setBlocked(true); } else { m->setBlocked(false); } // in case delay is not provided in command line => set default if (delay <= 0.0) delay = PCM_DELAY_DEFAULT; // cerr << "DEBUG: Delay: " << delay << " seconds. Blocked: " << m->isBlocked() << "\n"; if (csv_output) { print_csv_header(m, ycores, cpu_model, show_core_output, show_partial_core_output, show_socket_output, show_system_output); } m->getAllCounterStates(sstate1, sktstate1, cstates1); if (sysCmd != NULL) { MySystem(sysCmd, sysArgv); } mainLoop([&]() { if (enforceFlush || !csv_output) cout << std::flush; calibratedSleep(delay, sysCmd, mainLoop, m); m->getAllCounterStates(sstate2, sktstate2, cstates2); if (csv_output) print_csv(m, cstates1, cstates2, sktstate1, sktstate2, ycores, sstate1, sstate2, cpu_model, show_core_output, show_partial_core_output, show_socket_output, show_system_output); else print_output(m, cstates1, cstates2, sktstate1, sktstate2, ycores, sstate1, sstate2, cpu_model, show_core_output, show_partial_core_output, show_socket_output, show_system_output); std::swap(sstate1, sstate2); std::swap(sktstate1, sktstate2); std::swap(cstates1, cstates2); if (m->isBlocked()) { // in case PCM was blocked after spawning child application: break monitoring loop here return false; } return true; }); exit(EXIT_SUCCESS); } pcm-202307/src/readmem.cpp000066400000000000000000000027601445420033100152760ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // #include "cpucounters.h" #include #include #include #include #include using std::cout; inline double my_timestamp() { struct timeval tp; gettimeofday(&tp, NULL); return double(tp.tv_sec) + tp.tv_usec / 1000000.; } struct T { int key[1] = { 0 }; int data[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; T() { } T(int a) { key[0] = a; } bool operator == (const T & k) const { return k.key[0] == key[0]; } }; template void Memory_intensive_task(DS & ds) { // cppcheck-suppress ignoredReturnValue std::find(ds.begin(), ds.end(), ds.size()); } int main(int argc, char * argv[]) { std::vector vector; int nelements = 13000000; int i = 0; int delay = atoi(argv[1]); cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB\n"; for ( ; i < nelements; ++i) { vector.push_back(i); } double before_ts, after_ts; while (1) { before_ts = my_timestamp(); cout << "Reading memory for " << delay << " seconds\n" << flush; do { Memory_intensive_task(vector); after_ts = my_timestamp(); } while ((after_ts - before_ts) < delay); cout << "Sleeping for " << delay << " seconds\n" << flush; sleep(delay); } return 0; } pcm-202307/src/realtime.cpp000066400000000000000000000160001445420033100154560ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // #include "cpucounters.h" #include "cpuasynchcounter.h" #include #include #include #include #include /*! \file realtime.cpp \brief Two use-cases: realtime data structure performance analysis and memory-bandwidth aware scheduling */ using std::cout; inline double my_timestamp() { struct timeval tp; gettimeofday(&tp, NULL); return double(tp.tv_sec) + tp.tv_usec / 1000000.; } long long int fib(long long int num) { long long int result = 1, a = 1, b = 1; for (long long int i = 3; i <= num; ++i) { result = a + b; a = b; b = result; } return result; } SystemCounterState before_sstate, after_sstate; double before_time, after_time; AsynchronCounterState counters; long long int all_fib = 0; void CPU_intensive_task() { cout << "CPU task\n"; all_fib += fib(80000000ULL + (rand() % 2)); } template void Memory_intensive_task(DS & ds) { cout << "Mem task\n"; // cppcheck-suppress ignoredReturnValue std::find(ds.begin(), ds.end(), ds.size()); } double currentMemoryBandwidth() { return (counters.getSystem() + counters.getSystem()) / (1024 * 1024); } template void measure(DS & ds, size_t repeat, size_t nelements) { SystemCounterState before_sstate, after_sstate; double before_ts = 0.0, after_ts; // warm up // cppcheck-suppress ignoredReturnValue std::find(ds.begin(), ds.end(), nelements); double before1_ts; #if 0 for (int kkk = 1000; kkk > 0; --kkk) { ::sleep(1); before1_ts = my_timestamp(); // start measuring before_sstate = getSystemCounterState(); before_ts = my_timestamp(); cout << "Response time of getSystemCounterState(): " << 1000. * (before_ts - before1_ts) << " ms\n"; } #endif // cppcheck-suppress ignoredReturnValue for (int j = 0; j < repeat; ++j) std::find(ds.begin(), ds.end(), nelements); // stop measuring after_sstate = getSystemCounterState(); after_ts = my_timestamp(); cout << "\nSearch runtime: " << ((after_ts - before_ts) * 1000. / repeat) << " ms \n"; cout << "Search runtime per element: " << ((after_ts - before_ts) * 1000000000. / repeat) / nelements << " ns \n"; cout << "Number of L2 cache misses per 1000 elements: " << (1000. * getL2CacheMisses(before_sstate, after_sstate) / repeat) / nelements << " \nL2 Cache hit ratio : " << getL2CacheHitRatio(before_sstate, after_sstate) * 100. << " %\n"; cout << "Number of L3 cache misses per 1000 elements: " << (1000. * getL3CacheMisses(before_sstate, after_sstate) / repeat) / nelements << " \nL3 Cache hit ratio : " << getL3CacheHitRatio(before_sstate, after_sstate) * 100. << " %\n"; cout << "Bytes written to memory controller per element: " << (double(getBytesWrittenToMC(before_sstate, after_sstate)) / repeat) / nelements << "\n"; cout << "Bytes read from memory controller per element : " << (double(getBytesReadFromMC(before_sstate, after_sstate)) / repeat) / nelements << "\n"; cout << "Used memory bandwidth: " << ((getBytesReadFromMC(before_sstate, after_sstate) + getBytesWrittenToMC(before_sstate, after_sstate)) / (after_ts - before_ts)) / (1024 * 1024) << " MByte/sec\n"; cout << "Instructions retired: " << getInstructionsRetired(before_sstate, after_sstate) / 1000000 << "mln\n"; cout << "CPU cycles: " << getCycles(before_sstate, after_sstate) / 1000000 << "mln\n"; cout << "Instructions per cycle: " << getCoreIPC(before_sstate, after_sstate) << "\n"; cout << flush; } #if 0 typedef int T; #else struct T { int key[1] = { 0 }; int data[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };; T() { } T(int a) { key[0] = a; } bool operator == (const T & k) const { return k.key[0] == key[0]; } }; #endif int main(int argc, char * argv[]) { PCM * m = PCM::getInstance(); if (!m->good()) { cout << "Can not access CPU counters\n"; cout << "Try to execute 'modprobe msr' as root user and then\n"; cout << "you also must have read and write permissions for /dev/cpu/?/msr devices (the 'chown' command can help)."; return -1; } if (m->program() != PCM::Success) { cout << "Program was not successful...\n"; delete m; return -1; } int nelements = atoi(argv[1]); #if 1 /* use-case: compare data structures in real-time */ std::list list; std::vector vector; int i = 0; for ( ; i < nelements; ++i) { list.push_back(i); vector.push_back(i); } unsigned long long int totalops = 200000ULL * 1000ULL * 64ULL / sizeof(T); int repeat = totalops / nelements, j; cout << "\n\nElements to traverse: " << totalops << "\n"; cout << "Items in data structure: " << nelements << "\n"; cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB\n"; cout << "Test repetitions: " << repeat << "\n"; cout << "\n*List data structure*\n"; measure(list, repeat, nelements); cout << "\n\n*Vector/array data structure*\n"; measure(vector, repeat, nelements); #else /* use-case: memory bandwidth-aware scheduling */ std::vector vector; nelements = 13000000; int i = 0; cout << "Elements data size: " << sizeof(T) * nelements / 1024 << " KB\n"; for ( ; i < nelements; ++i) { vector.push_back(i); } double before_ts, after_ts; before_ts = my_timestamp(); { int m_tasks = 1000; int c_tasks = 1000; while (m_tasks + c_tasks != 0) { if (m_tasks > 0) { Memory_intensive_task(vector); --m_tasks; continue; } if (c_tasks > 0) { CPU_intensive_task(); --c_tasks; } } } after_ts = my_timestamp(); cout << "In order scheduling, Running time: " << (after_ts - before_ts) << " seconds\n"; before_ts = my_timestamp(); { int m_tasks = 1000; int c_tasks = 1000; while (m_tasks + c_tasks != 0) { double band = currentMemoryBandwidth(); //cout << "Mem band: " << band << " MB/sec\n"; if (m_tasks > 0 && (band < (25 * 1024 /* MB/sec*/) || c_tasks == 0)) { Memory_intensive_task(vector); --m_tasks; continue; } if (c_tasks > 0) { CPU_intensive_task(); --c_tasks; continue; } } } after_ts = my_timestamp(); cout << "CPU monitoring conscoius scheduling, Running time: " << (after_ts - before_ts) << " seconds\n"; #endif m->cleanup(); return 0; } pcm-202307/src/resctrl.cpp000066400000000000000000000113361445420033100153410ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation // written by Roman Dementiev #ifdef __linux__ #include "resctrl.h" #include "cpucounters.h" #include #include #include #include #include namespace pcm { bool Resctrl::isMounted() { struct stat st; if (stat("/sys/fs/resctrl/mon_groups", &st) < 0) { return false; } return true; } void Resctrl::init() { if (isMounted() == false) { std::cerr << "ERROR: /sys/fs/resctrl is not mounted\n"; std::cerr << "ERROR: RDT metrics (L3OCC,LMB,RMB) will not be available\n"; std::cerr << "Mount it to make it work: mount -t resctrl resctrl /sys/fs/resctrl\n"; return; } const auto numCores = pcm.getNumCores(); for (unsigned int c = 0; c < numCores; ++c) { if (pcm.isCoreOnline(c)) { const auto C = std::to_string(c); const auto dir = std::string(PCMPath) + C; struct stat st; if (stat(dir.c_str(), &st) < 0 && mkdir(dir.c_str(), 0700) < 0) { std::cerr << "INFO: can't create directory " << dir << " error: " << strerror(errno) << "\n"; const auto containerDir = std::string("/pcm") + dir; if (stat(containerDir.c_str(), &st) < 0 && mkdir(containerDir.c_str(), 0700) < 0) { std::cerr << "INFO: can't create directory " << containerDir << " error: " << strerror(errno) << "\n"; std::cerr << "ERROR: RDT metrics (L3OCC,LMB,RMB) will not be available\n"; break; } } const auto cpus_listFilename = dir + "/cpus_list"; writeSysFS(cpus_listFilename.c_str(), C, false); auto generateMetricFiles = [&dir, c] (PCM & pcm, const std::string & metric, FileMapType & fileMap) { auto getMetricFilename = [] (const std::string & dir, const uint64 s, const std::string & metric) { std::ostringstream ostr; ostr << dir << "/mon_data/mon_L3_" << std::setfill('0') << std::setw(2) << s << "/" << metric; return ostr.str(); }; for (uint64 s = 0; s < pcm.getNumSockets(); ++s) { fileMap[c].push_back(getMetricFilename(dir, s, metric)); } }; if (pcm.L3CacheOccupancyMetricAvailable()) { generateMetricFiles(pcm, "llc_occupancy", L3OCC); } if (pcm.CoreLocalMemoryBWMetricAvailable()) { generateMetricFiles(pcm, "mbm_local_bytes", MBL); } if (pcm.CoreRemoteMemoryBWMetricAvailable()) { generateMetricFiles(pcm, "mbm_total_bytes", MBT); } } } } void Resctrl::cleanup() { const auto numCores = pcm.getNumCores(); for (unsigned int c = 0; c < numCores; ++c) { if (pcm.isCoreOnline(c)) { const auto dir = std::string(PCMPath) + std::to_string(c); rmdir(dir.c_str()); const auto containerDir = std::string("/pcm") + dir; rmdir(containerDir.c_str()); } } } size_t Resctrl::getMetric(const Resctrl::FileMapType & fileMap, int core) { auto files = fileMap.find(core); if (files == fileMap.end()) { return 0ULL; } size_t result = 0; for (auto& f : files->second) { const auto data = readSysFS(f.c_str(), false); if (data.empty() == false) { result += atoll(data.c_str()); } else { static std::mutex lock; std::lock_guard _(lock); std::cerr << "Error reading " << f << ". Error: " << strerror(errno) << "\n"; if (errno == 24) { std::cerr << PCM_ULIMIT_RECOMMENDATION; } } } return result; } size_t Resctrl::getL3OCC(int core) { return getMetric(L3OCC, core); } size_t Resctrl::getMBL(int core) { return getMetric(MBL, core); } size_t Resctrl::getMBT(int core) { return getMetric(MBT, core); } }; #endif // __linux__ pcm-202307/src/resctrl.h000066400000000000000000000017121445420033100150030ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation // written by Roman Dementiev #pragma once /*! \file resctrl.h \brief interface to MBM and CMT using Linux resctrl */ #ifdef __linux__ #include #include #include #include #include #include namespace pcm { class PCM; class Resctrl { PCM & pcm; typedef std::unordered_map > FileMapType; FileMapType L3OCC, MBL, MBT; Resctrl() = delete; size_t getMetric(const FileMapType & fileMap, int core); static constexpr auto PCMPath = "/sys/fs/resctrl/mon_groups/pcm"; public: Resctrl(PCM & m) : pcm(m) {} bool isMounted(); void init(); size_t getL3OCC(int core); size_t getMBL(int core); size_t getMBT(int core); void cleanup(); }; }; #endif // __linux__ pcm-202307/src/simdjson/000077500000000000000000000000001445420033100150015ustar00rootroot00000000000000pcm-202307/src/threadpool.cpp000066400000000000000000000005151445420033100160210ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation #include "threadpool.h" namespace pcm { void ThreadPool::execute( ThreadPool* tp ) { while( 1 ) { Work* w = tp->retrieveWork(); if ( w == nullptr ) break; w->execute(); delete w; } } } // namespace pcm pcm-202307/src/threadpool.h000066400000000000000000000056571445420033100155020ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2020-2022, Intel Corporation #pragma once #include "debug.h" #include #include #include #include #include #include namespace pcm { class Work { public: Work() {} virtual ~Work() {} virtual void execute() = 0; }; template class LambdaJob : public Work { public: template LambdaJob( F&& f, Args&& ... args ) //: task_( std::forward(f)(std::forward( args )... ) ) { : task_(std::bind( f, args... ) ) { } virtual void execute() override { task_(); } std::future getFuture() { return task_.get_future(); } private: std::packaged_task task_; }; class WorkQueue; class ThreadPool { private: ThreadPool( const int n ) { for ( int i = 0; i < n; ++i ) addThread(); } ThreadPool( ThreadPool const& ) = delete; ThreadPool & operator = ( ThreadPool const& ) = delete; public: ~ThreadPool() { try { for (size_t i = 0; i < threads_.size(); ++i) addWork(nullptr); for (size_t i = 0; i < threads_.size(); ++i) threads_[i].join(); threads_.clear(); } catch (const std::exception& e) { std::cerr << "PCM Error. Exception in ThreadPool::~ThreadPool: " << e.what() << "\n"; } } public: static ThreadPool& getInstance() { static ThreadPool tp_(64); return tp_; } void addWork( Work* w ) { DBG( 3, "WQ: Adding work" ); std::lock_guard lg( qMutex_ ); workQ_.push( w ); queueCV_.notify_one(); DBG( 3, "WQ: Work available" ); } Work* retrieveWork() { DBG( 3, "WQ: Retrieving work" ); std::unique_lock lock( qMutex_ ); queueCV_.wait( lock, [this]{ return !workQ_.empty(); } ); Work* w = workQ_.front(); workQ_.pop(); lock.unlock(); DBG( 3, "WQ: Work retrieved" ); return w; } private: void addThread() { threads_.push_back( std::thread( std::bind( &this->execute, this ) ) ); } // Executes work items from a std::thread, do not call manually static void execute( ThreadPool* ); private: std::vector threads_; std::queue workQ_; std::mutex qMutex_; std::condition_variable queueCV_; }; class WorkQueue { public: WorkQueue() : tp_( ThreadPool::getInstance() ), workProcessed_(0) {} WorkQueue( WorkQueue const& ) = delete; WorkQueue & operator = ( WorkQueue const& ) = delete; ~WorkQueue() = default; // Just forwarding to the threadpool void addWork( Work* w ) { ++workProcessed_; tp_.addWork( w ); } private: ThreadPool& tp_; size_t workProcessed_; }; } // namespace pcm pcm-202307/src/topology.cpp000066400000000000000000000073201445420033100155350ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2016-2022, Intel Corporation #include "cpucounters.h" #include "topology.h" namespace pcm { UncoreCounterState ServerUncore::uncoreCounterState( void ) const { UncoreCounterState ucs; // Fill the ucs PCM* pcm = PCM::getInstance(); pcm->readAndAggregateUncoreMCCounters( socketID(), ucs ); pcm->readAndAggregateEnergyCounters( socketID(), ucs ); pcm->readAndAggregatePackageCStateResidencies( refCore()->msrHandle(), ucs ); return ucs; } Socket::Socket( PCM* m, int32 apicID, int32 logicalID ) : pcm_(m), refCore_(nullptr), apicID_(apicID), logicalID_(logicalID) { if ( pcm_->isServerCPU() ) uncore_ = new ServerUncore( pcm_, logicalID ); else if ( pcm_->isClientCPU() ) uncore_ = new ClientUncore( pcm_, logicalID ); else throw std::runtime_error( "ERROR: Neither a client nor a server part, please fix the code!" ); } SocketCounterState Socket::socketCounterState( void ) const { SocketCounterState scs; // Fill the scs // by iterating the cores for( auto& core : cores_ ) { scs.BasicCounterState::operator += ( core->coreCounterState() ); } // and the uncore scs.UncoreCounterState::operator += ( uncore_->uncoreCounterState() ); PCM::getInstance()->readPackageThermalHeadroom( socketID(), scs ); return scs; } void Aggregator::dispatch( SystemRoot const& syp ) { // std::cerr << "Aggregator::dispatch( SystemRoot )\n"; dispatchedAt_ = std::chrono::steady_clock::now(); // CoreCounterStates are fetched asynchronously here for ( auto* socket : syp.sockets() ) socket->accept( *this ); // Dispatching offlined cores for ( auto* htp : syp.offlinedThreadsAtStart() ) htp->accept( *this ); auto ccsFuturesIter = ccsFutures_.begin(); auto ccsIter = ccsVector_.begin(); // int i; // i = 0; for ( ; ccsFuturesIter != ccsFutures_.end() && ccsIter != ccsVector_.end(); ++ccsFuturesIter, ++ccsIter ) { // std::cerr << "Works ccsFuture: " << ++i << "\n"; (*ccsIter) = (*ccsFuturesIter).get(); } // Aggregate BasicCounterStates for ( auto* socket : syp.sockets() ) { for ( auto* core : socket->cores() ) for ( auto* thread : core->threads() ) socsVector_[ socket->socketID() ] += ( ccsVector_[ thread->osID() ] ); // UncoreCounterStates have not been filled here so it is ok to add // the entire SocketCounterState here sycs_ += socsVector_[ socket->socketID() ]; } // Fetch and aggregate UncoreCounterStates auto ucsFuturesIter = ucsFutures_.begin(); auto socsIter = socsVector_.begin(); // i = 0; for ( ; ucsFuturesIter != ucsFutures_.end() && socsIter != socsVector_.end(); ++ucsFuturesIter, ++socsIter ) { // std::cerr << "Works ucsFuture: " << ++i << "\n"; // Because we already aggregated the Basic/CoreCounterStates above, sycs_ // only needs the ucs added here. If we would add socs to sycs we would // count all Basic/CoreCounterState counters double UncoreCounterState ucs = (*ucsFuturesIter).get(); sycs_ += ucs; (*socsIter) = std::move( ucs ); } PCM* pcm = PCM::getInstance(); pcm->readQPICounters( sycs_ ); pcm->readAndAggregateCXLCMCounters( sycs_ ); } Aggregator::Aggregator() { PCM* const pcm = PCM::getInstance(); // Resize user provided vectors to the right size ccsVector_.resize( pcm->getNumCores() ); socsVector_.resize( pcm->getNumSockets() ); // Internal use only, need to be the same size as the user provided vectors ccsFutures_.resize( pcm->getNumCores() ); ucsFutures_.resize( pcm->getNumSockets() ); } }// namespace pcm pcm-202307/src/topology.h000066400000000000000000000364511445420033100152110ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // 2016-2020, Intel Corporation #pragma once #include #include #include #include #include "types.h" #include "cpucounters.h" #include "threadpool.h" namespace pcm { // all can be done with forwards, anything hat actually uses PCM should be put in the topology.cpp file class PCM; class SystemRoot; class Socket; class Core; class HyperThread; class ServerUncore; class ClientUncore; class Visitor { public: Visitor() { // Set the floatingpoint format to fixed. Setting the number of decimal digits to 3. ss << std::fixed << std::setprecision(3); } Visitor(const Visitor &) = delete; Visitor & operator = (const Visitor &) = delete; public: virtual void dispatch( SystemRoot const & ) = 0; virtual void dispatch( Socket* ) = 0; virtual void dispatch( Core* ) = 0; virtual void dispatch( HyperThread* ) = 0; virtual void dispatch( ServerUncore* ) = 0; virtual void dispatch( ClientUncore* ) = 0; virtual ~Visitor() {}; protected: std::stringstream ss{}; }; class SystemObject { public: virtual void accept( Visitor & v ) = 0; virtual ~SystemObject() {}; }; enum Status { Offline = 0, Online = 1 }; class HyperThread : public SystemObject { public: HyperThread( PCM* m, int32 threadID, int32 osID, enum Status status ) : pcm_(m), threadID_(threadID), osID_(osID), status_(status) {} virtual ~HyperThread() { pcm_ = nullptr; } virtual void accept( Visitor& v ) override { v.dispatch( this ); } CoreCounterState coreCounterState() const { CoreCounterState ccs; // fill ccs ccs.BasicCounterState::readAndAggregate( msrHandle_ ); return ccs; } void addMSRHandle( std::shared_ptr handle ) { msrHandle_ = handle; } int32 threadID() const { return threadID_; } int32 osID() const { return osID_; } // We simply pass by value, this way the refcounting works best and as expected std::shared_ptr msrHandle() const { return msrHandle_; } bool isOnline() const { return (status_ == Status::Online); } private: PCM* pcm_; std::shared_ptr msrHandle_; int32 threadID_; int32 osID_; enum Status status_; }; class Core : public SystemObject { constexpr static int32 MAX_THREADS_PER_CORE = 4; public: Core( PCM* m, int32 coreID, int32 tileID, int32 socketID ) { pcm_ = m; coreID_ = coreID; tileID_ = tileID; socketID_ = socketID; } virtual ~Core() { pcm_ = nullptr; for ( auto& thread : threads_ ) delete thread; } virtual void accept( Visitor& v ) override { v.dispatch( this ); } CoreCounterState coreCounterState() const { CoreCounterState ccs; // Fill bcs for ( HyperThread* thread : threads_ ) { ccs += thread->coreCounterState(); } return ccs; } void addHyperThreadInfo( int32 threadID, int32 osID ) { if ( threadID >= MAX_THREADS_PER_CORE ) { std::stringstream ss; ss << "ERROR: Core: threadID cannot be larger than " << MAX_THREADS_PER_CORE << ".\n"; throw std::runtime_error( ss.str() ); } if ( threads_.size() == 0 || std::find_if( threads_.begin(), threads_.end(), [osID]( HyperThread const * ht ) -> bool { return ht->osID() == osID; } ) == threads_.end() ) { // std::cerr << "Core::addHyperThreadInfo: " << threadID << ", " << osID << "\n"; threads_.push_back( new HyperThread( pcm_, threadID, osID, Status::Online ) ); } } HyperThread* hyperThread( size_t threadNo ) const { if ( threadNo >= threads_.size() ) throw std::runtime_error( "ERROR: hyperThread: threadNo larger than vector." ); return threads_[ threadNo ]; } HyperThread* findThreadByOSID( int32 osID ) { for ( HyperThread* thread : threads_ ) { if ( thread->osID() == osID ) return thread; } return nullptr; } std::vector threads() const { return threads_; } std::shared_ptr msrHandle() const { if ( 0 == threads_.size() ) throw std::runtime_error("BUG: No threads yet but asking for a msrHandle!"); return threads_.front()->msrHandle(); } int32 coreID() const { return coreID_; } int32 tileID() const { return tileID_; } int32 socketID() const { return socketID_; } bool isOnline() const { for( auto& thread : threads_ ) if ( thread->isOnline() ) return true; return false; } private: PCM* pcm_; std::vector threads_; int32 coreID_; int32 tileID_; int32 socketID_; }; class Uncore : public SystemObject { public: Uncore( PCM* m, int32 socketID ) : pcm_( m ), refCore_( nullptr ), socketID_( socketID ) {} virtual ~Uncore() { pcm_ = nullptr; refCore_ = nullptr; } virtual void accept( Visitor& v ) = 0; virtual UncoreCounterState uncoreCounterState( void ) const = 0; Core* refCore() const { if ( refCore_ == nullptr ) throw std::runtime_error( "BUG: Uncore: refCore was never set!" ); return refCore_; } int32 socketID() const { return socketID_; } void setRefCore( Core* refCore ) { refCore_ = refCore; } private: PCM* pcm_; Core* refCore_; int32 socketID_; }; class ServerUncore : public Uncore { public: ServerUncore( PCM* m, int32 socketID ) : Uncore( m, socketID ) {} virtual ~ServerUncore() {} virtual void accept( Visitor& v ) override { v.dispatch( this ); } virtual UncoreCounterState uncoreCounterState( void ) const override; }; class ClientUncore : public Uncore { public: ClientUncore( PCM* m, int32 socketID ) : Uncore( m, socketID ) {} virtual ~ClientUncore() {} virtual void accept( Visitor& v ) override { v.dispatch( this ); } virtual UncoreCounterState uncoreCounterState( void ) const override { UncoreCounterState ucs; // TODO: Fill the ucs return ucs; } }; class Socket : public SystemObject { Socket(const Socket &) = delete; Socket & operator = (const Socket &) = delete; public: Socket( PCM* m, int32 apicID, int32 logicalID ); virtual ~Socket() { pcm_ = nullptr; for ( auto& core : cores_ ) delete core; refCore_ = nullptr; // cores_ is owner so it is already deleted by here delete uncore_; } virtual void accept( Visitor& v ) override { v.dispatch( this ); } void addCore( Core* c ) { cores_.push_back( c ); } HyperThread* findThreadByOSID( int32 osID ) { HyperThread* thread; for ( Core* core : cores_ ) { thread = core->findThreadByOSID(osID); if ( nullptr != thread ) return thread; } return nullptr; } void setRefCore() { if ( cores_.size() == 0 ) throw std::runtime_error("No cores added to the socket so cannot set reference core"); refCore_ = cores_.front(); // uncore_ cannot be null, it is set in the constructor uncore_->setRefCore( refCore_ ); } SocketCounterState socketCounterState( void ) const; Core* findCoreByTileID( int32 tileID ) { for ( auto& core : cores_ ) { if ( core->tileID() == tileID ) return core; } return nullptr; } std::vector const & cores( void ) const { return cores_; } Uncore* uncore( void ) const { return uncore_; } int32 apicId() const { return apicID_; } int32 socketID() const { return logicalID_; } bool isOnline() const { return refCore_->isOnline(); } private: std::vector cores_; PCM* pcm_; Core* refCore_; Uncore* uncore_; int32 apicID_; int32 logicalID_; }; class SystemRoot : public SystemObject { public: SystemRoot(PCM * p) : pcm_(p) {} SystemRoot( SystemRoot const & ) = delete; // do not try to copy this please SystemRoot & operator = ( SystemRoot const & ) = delete; // do not try to copy this please virtual ~SystemRoot() { pcm_ = nullptr; for ( auto& socket : sockets_ ) delete socket; for ( auto& thread : offlinedThreadsAtStart_ ) delete thread; } virtual void accept( Visitor& v ) override { v.dispatch( *this ); } void addSocket( int32 apic_id, int32 logical_id ) { Socket* s = new Socket( pcm_, apic_id, logical_id ); sockets_.push_back( s ); } // osID is the expected os_id, this is used in case te.os_id = -1 (offlined core) void addThread( int32 osID, TopologyEntry& te ) { // quick check during development to see if expected osId == te.os_id for onlined cores // assert( te.os_id != -1 && osID == te.os_id ); bool entryAdded = false; for ( auto& socket : sockets_ ) { if ( socket->apicId() == te.socket ) { Core* core = nullptr; if ( (core = socket->findCoreByTileID( te.tile_id )) == nullptr ) { // std::cerr << "SystemRoot::addThread: " << te.tile_id << ", " << osID << "\n"; core = new Core( pcm_, te.core_id, te.tile_id, te.socket ); // std::cerr << "new Core ThreadID: " << te.thread_id << "\n"; core->addHyperThreadInfo( te.thread_id, osID ); socket->addCore( core ); // std::cerr << "Added core " << te.core_id << " with os_id " << osID << ", threadid " << te.thread_id << " and tileid " << te.tile_id << " to socket " << te.socket << ".\n"; } else { // std::cerr << "existing Core ThreadID: " << te.thread_id << "\n"; core->addHyperThreadInfo( te.thread_id, osID ); // std::cerr << "Augmented core " << te.core_id << " with osID " << osID << " and threadid " << te.thread_id << " for the hyperthread to socket " << te.socket << ".\n"; } entryAdded = true; break; } } if ( !entryAdded ) { // if ( te.os_id == -1 ) // std::cerr << "TE not added because os_id == -1, core is offline\n"; offlinedThreadsAtStart_.push_back( new HyperThread( pcm_, -1, osID, Status::Offline ) ); } } HyperThread* findThreadByOSID( int32 osID ) { HyperThread* thread; for ( Socket* socket : sockets_ ) { thread = socket->findThreadByOSID( osID ); if ( nullptr != thread ) return thread; } for ( HyperThread* ht: offlinedThreadsAtStart_ ) if ( ht->osID() == osID ) return ht; return nullptr; } void addMSRHandleToOSThread( std::shared_ptr handle, uint32 osID ) { // std::cerr << "addMSRHandleToOSThread: osID: " << osID << "\n"; HyperThread* thread = findThreadByOSID( osID ); if ( nullptr == thread ) throw std::runtime_error( "SystemRoot::addMSRHandleToOSThread osID not found" ); thread->addMSRHandle( handle ); } SystemCounterState systemCounterState() const { SystemCounterState scs; // Fill scs // by iterating the sockets for ( auto& socket : sockets_ ) { scs += ( socket->socketCounterState() ); } return scs; } std::vector const & sockets( void ) const { return sockets_; } std::vector const & offlinedThreadsAtStart( void ) const { return offlinedThreadsAtStart_; } private: std::vector sockets_; std::vector offlinedThreadsAtStart_; PCM* pcm_; }; /* Method used here: while walking the tree and iterating the vector * elements, collect the counters. Once all elements have been walked * the vectors are filled with the aggregates. */ class Aggregator : Visitor { public: Aggregator(); virtual ~Aggregator() {} public: virtual void dispatch( SystemRoot const& syp ) override; virtual void dispatch( Socket* sop ) override { // std::cerr << "Aggregator::dispatch( Socket )\n"; // Fetch CoreCounterStates for ( auto* core : sop->cores() ) core->accept( *this ); // Fetch UncoreCounterState async result auto job = new LambdaJob( []( Socket* s ) -> UncoreCounterState { DBG( 3, "Lambda fetching UncoreCounterState async" ); UncoreCounterState ucs; if ( !s->isOnline() ) return ucs; return s->uncore()->uncoreCounterState(); }, sop ); ucsFutures_[ sop->socketID() ] = job->getFuture(); wq_.addWork( job ); // For now execute directly to compile test //job->execute(); } virtual void dispatch( Core* cop ) override { // std::cerr << "Aggregator::dispatch( Core )\n"; // Loop each HyperThread for ( auto* thread : cop->threads() ) { // Fetch the CoreCounterState thread->accept( *this ); } } virtual void dispatch( HyperThread* htp ) override { // std::cerr << "Aggregator::dispatch( HyperThread )\n"; // std::cerr << "Dispatch htp with osID=" << htp->osID() << "\n"; auto job = new LambdaJob( []( HyperThread* h ) -> CoreCounterState { DBG( 3, "Lambda fetching CoreCounterState async" ); CoreCounterState ccs; if ( !h->isOnline() ) return ccs; return h->coreCounterState(); }, htp ); ccsFutures_[ htp->osID() ] = job->getFuture(); wq_.addWork( job ); } virtual void dispatch( ServerUncore* /*sup*/ ) override { // std::cerr << "Aggregator::dispatch( ServerUncore )\n"; } virtual void dispatch( ClientUncore* /*cup*/ ) override { // std::cerr << "Aggregator::dispatch( ClientUncore )\n"; } std::vectorconst & coreCounterStates( void ) const { return ccsVector_; } std::vectorconst & socketCounterStates( void ) const { return socsVector_; } SystemCounterState const & systemCounterState( void ) const { return sycs_; } std::chrono::steady_clock::time_point dispatchedAt( void ) const { return dispatchedAt_; } private: std::vector ccsVector_; std::vector socsVector_; SystemCounterState sycs_; std::vector> ccsFutures_; std::vector> ucsFutures_; std::chrono::steady_clock::time_point dispatchedAt_{}; WorkQueue wq_; }; } // namespace pcm pcm-202307/src/topologyentry.h000066400000000000000000000016271445420033100162700ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2022-, Intel Corporation #pragma once #include "types.h" namespace pcm { struct PCM_API TopologyEntry // describes a core { int32 os_id; int32 thread_id; int32 core_id; int32 tile_id; // tile is a constalation of 1 or more cores sharing salem L2 cache. Unique for entire system int32 socket; int32 native_cpu_model = -1; enum CoreType { Atom = 0x20, Core = 0x40, Invalid = -1 }; CoreType core_type = Invalid; TopologyEntry() : os_id(-1), thread_id (-1), core_id(-1), tile_id(-1), socket(-1) { } const char* getCoreTypeStr() { switch (core_type) { case Atom: return "Atom"; case Core: return "Core"; case Invalid: return "invalid"; } return "unknown"; } }; } pcm-202307/src/types.h000066400000000000000000001444341445420033100145020ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // #ifndef CPUCounters_TYPES_H #define CPUCounters_TYPES_H /*! \file types.h \brief Internal type and constant definitions */ #undef PCM_DEBUG #ifndef KERNEL #include #include #include #include #include #ifdef _MSC_VER #include #endif #endif // #ifndef KERNEL namespace pcm { typedef unsigned long long uint64; typedef signed long long int64; typedef unsigned int uint32; typedef signed int int32; #define PCM_ULIMIT_RECOMMENDATION ("try executing 'ulimit -n 1000000' to increase the limit on the number of open files.\n") /* MSR addresses from "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: System Programming Guide, Part 2", Appendix A "PERFORMANCE-MONITORING EVENTS" */ #define INST_RETIRED_ADDR (0x309) #define CPU_CLK_UNHALTED_THREAD_ADDR (0x30A) #define CPU_CLK_UNHALTED_REF_ADDR (0x30B) #define TOPDOWN_SLOTS_ADDR (0x30C) #define PERF_METRICS_ADDR (0x329) #define IA32_CR_PERF_GLOBAL_CTRL (0x38F) #define IA32_CR_FIXED_CTR_CTRL (0x38D) #define IA32_PERFEVTSEL0_ADDR (0x186) #define IA32_PERFEVTSEL1_ADDR (IA32_PERFEVTSEL0_ADDR + 1) #define IA32_PERFEVTSEL2_ADDR (IA32_PERFEVTSEL0_ADDR + 2) #define IA32_PERFEVTSEL3_ADDR (IA32_PERFEVTSEL0_ADDR + 3) constexpr auto IA32_PERF_GLOBAL_STATUS = 0x38E; constexpr auto IA32_PERF_GLOBAL_OVF_CTRL = 0x390; constexpr auto IA32_PEBS_ENABLE_ADDR = 0x3F1; #define PERF_MAX_FIXED_COUNTERS (3) #define PERF_MAX_CUSTOM_COUNTERS (8) #define PERF_TOPDOWN_COUNTERS (5) #define PERF_MAX_COUNTERS (PERF_MAX_FIXED_COUNTERS + PERF_MAX_CUSTOM_COUNTERS + PERF_TOPDOWN_COUNTERS) #define IA32_DEBUGCTL (0x1D9) #define IA32_PMC0 (0xC1) #define IA32_PMC1 (0xC1 + 1) #define IA32_PMC2 (0xC1 + 2) #define IA32_PMC3 (0xC1 + 3) #define MSR_OFFCORE_RSP0 (0x1A6) #define MSR_OFFCORE_RSP1 (0x1A7) constexpr auto MSR_LOAD_LATENCY = 0x3F6; constexpr auto MSR_FRONTEND = 0x3F7; /* From Table B-5. of the above mentioned document */ #define PLATFORM_INFO_ADDR (0xCE) #define IA32_TIME_STAMP_COUNTER (0x10) // Event IDs // Nehalem/Westmere on-core events #define MEM_LOAD_RETIRED_L3_MISS_EVTNR (0xCB) #define MEM_LOAD_RETIRED_L3_MISS_UMASK (0x10) #define MEM_LOAD_RETIRED_L3_UNSHAREDHIT_EVTNR (0xCB) #define MEM_LOAD_RETIRED_L3_UNSHAREDHIT_UMASK (0x04) #define MEM_LOAD_RETIRED_L2_HITM_EVTNR (0xCB) #define MEM_LOAD_RETIRED_L2_HITM_UMASK (0x08) #define MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) #define MEM_LOAD_RETIRED_L2_HIT_UMASK (0x02) // Sandy Bridge on-core events #define MEM_LOAD_UOPS_MISC_RETIRED_LLC_MISS_EVTNR (0xD4) #define MEM_LOAD_UOPS_MISC_RETIRED_LLC_MISS_UMASK (0x02) #define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_EVTNR (0xD2) #define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_NONE_UMASK (0x08) #define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_HITM_EVTNR (0xD2) #define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_HITM_UMASK (0x04) #define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_EVTNR (0xD2) #define MEM_LOAD_UOPS_LLC_HIT_RETIRED_XSNP_UMASK (0x07) #define MEM_LOAD_UOPS_RETIRED_L2_HIT_EVTNR (0xD1) #define MEM_LOAD_UOPS_RETIRED_L2_HIT_UMASK (0x02) // Haswell on-core events constexpr auto HSX_L2_RQSTS_MISS_EVTNR = 0x24; constexpr auto HSX_L2_RQSTS_MISS_UMASK = 0x3f; constexpr auto HSX_L2_RQSTS_REFERENCES_EVTNR = 0x24; constexpr auto HSX_L2_RQSTS_REFERENCES_UMASK = 0xff; // Skylake on-core events #define SKL_MEM_LOAD_RETIRED_L3_MISS_EVTNR (0xD1) #define SKL_MEM_LOAD_RETIRED_L3_MISS_UMASK (0x20) #define SKL_MEM_LOAD_RETIRED_L3_HIT_EVTNR (0xD1) #define SKL_MEM_LOAD_RETIRED_L3_HIT_UMASK (0x04) #define SKL_MEM_LOAD_RETIRED_L2_MISS_EVTNR (0xD1) #define SKL_MEM_LOAD_RETIRED_L2_MISS_UMASK (0x10) #define SKL_MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xD1) #define SKL_MEM_LOAD_RETIRED_L2_HIT_UMASK (0x02) // architectural on-core events #define ARCH_LLC_REFERENCE_EVTNR (0x2E) #define ARCH_LLC_REFERENCE_UMASK (0x4F) #define ARCH_LLC_MISS_EVTNR (0x2E) #define ARCH_LLC_MISS_UMASK (0x41) // Atom on-core events #define ATOM_MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) #define ATOM_MEM_LOAD_RETIRED_L2_HIT_UMASK (0x01) #define ATOM_MEM_LOAD_RETIRED_L2_MISS_EVTNR (0xCB) #define ATOM_MEM_LOAD_RETIRED_L2_MISS_UMASK (0x02) #define ATOM_MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) #define ATOM_MEM_LOAD_RETIRED_L2_HIT_UMASK (0x01) #define ATOM_MEM_LOAD_RETIRED_L2_MISS_EVTNR (0xCB) #define ATOM_MEM_LOAD_RETIRED_L2_MISS_UMASK (0x02) #define ATOM_MEM_LOAD_RETIRED_L2_HIT_EVTNR (0xCB) #define ATOM_MEM_LOAD_RETIRED_L2_HIT_UMASK (0x01) #define ATOM_MEM_LOAD_RETIRED_L2_MISS_EVTNR (0xCB) #define ATOM_MEM_LOAD_RETIRED_L2_MISS_UMASK (0x02) // Offcore response events #define OFFCORE_RESPONSE_0_EVTNR (0xB7) #define OFFCORE_RESPONSE_1_EVTNR (0xBB) #define GLC_OFFCORE_RESPONSE_0_EVTNR (0x2A) #define GLC_OFFCORE_RESPONSE_1_EVTNR (0x2B) #define OFFCORE_RESPONSE_0_UMASK (1) #define OFFCORE_RESPONSE_1_UMASK (1) constexpr auto LOAD_LATENCY_EVTNR = 0xcd; constexpr auto LOAD_LATENCY_UMASK = 0x01; constexpr auto FRONTEND_EVTNR = 0xC6; constexpr auto FRONTEND_UMASK = 0x01; /* For Nehalem(-EP) processors from Intel(r) 64 and IA-32 Architectures Software Developer's Manual */ // Uncore msrs #define MSR_UNCORE_PERF_GLOBAL_CTRL_ADDR (0x391) #define MSR_UNCORE_PERFEVTSEL0_ADDR (0x3C0) #define MSR_UNCORE_PERFEVTSEL1_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 1) #define MSR_UNCORE_PERFEVTSEL2_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 2) #define MSR_UNCORE_PERFEVTSEL3_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 3) #define MSR_UNCORE_PERFEVTSEL4_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 4) #define MSR_UNCORE_PERFEVTSEL5_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 5) #define MSR_UNCORE_PERFEVTSEL6_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 6) #define MSR_UNCORE_PERFEVTSEL7_ADDR (MSR_UNCORE_PERFEVTSEL0_ADDR + 7) #define MSR_UNCORE_PMC0 (0x3B0) #define MSR_UNCORE_PMC1 (MSR_UNCORE_PMC0 + 1) #define MSR_UNCORE_PMC2 (MSR_UNCORE_PMC0 + 2) #define MSR_UNCORE_PMC3 (MSR_UNCORE_PMC0 + 3) #define MSR_UNCORE_PMC4 (MSR_UNCORE_PMC0 + 4) #define MSR_UNCORE_PMC5 (MSR_UNCORE_PMC0 + 5) #define MSR_UNCORE_PMC6 (MSR_UNCORE_PMC0 + 6) #define MSR_UNCORE_PMC7 (MSR_UNCORE_PMC0 + 7) // Uncore event IDs #define UNC_QMC_WRITES_FULL_ANY_EVTNR (0x2F) #define UNC_QMC_WRITES_FULL_ANY_UMASK (0x07) #define UNC_QMC_NORMAL_READS_ANY_EVTNR (0x2C) #define UNC_QMC_NORMAL_READS_ANY_UMASK (0x07) #define UNC_QHL_REQUESTS_EVTNR (0x20) #define UNC_QHL_REQUESTS_IOH_READS_UMASK (0x01) #define UNC_QHL_REQUESTS_IOH_WRITES_UMASK (0x02) #define UNC_QHL_REQUESTS_REMOTE_READS_UMASK (0x04) #define UNC_QHL_REQUESTS_REMOTE_WRITES_UMASK (0x08) #define UNC_QHL_REQUESTS_LOCAL_READS_UMASK (0x10) #define UNC_QHL_REQUESTS_LOCAL_WRITES_UMASK (0x20) /* From "Intel(r) Xeon(r) Processor 7500 Series Uncore Programming Guide" */ // Beckton uncore event IDs #define U_MSR_PMON_GLOBAL_CTL (0x0C00) #define MB0_MSR_PERF_GLOBAL_CTL (0x0CA0) #define MB0_MSR_PMU_CNT_0 (0x0CB1) #define MB0_MSR_PMU_CNT_CTL_0 (0x0CB0) #define MB0_MSR_PMU_CNT_1 (0x0CB3) #define MB0_MSR_PMU_CNT_CTL_1 (0x0CB2) #define MB0_MSR_PMU_ZDP_CTL_FVC (0x0CAB) #define MB1_MSR_PERF_GLOBAL_CTL (0x0CE0) #define MB1_MSR_PMU_CNT_0 (0x0CF1) #define MB1_MSR_PMU_CNT_CTL_0 (0x0CF0) #define MB1_MSR_PMU_CNT_1 (0x0CF3) #define MB1_MSR_PMU_CNT_CTL_1 (0x0CF2) #define MB1_MSR_PMU_ZDP_CTL_FVC (0x0CEB) #define BB0_MSR_PERF_GLOBAL_CTL (0x0C20) #define BB0_MSR_PERF_CNT_1 (0x0C33) #define BB0_MSR_PERF_CNT_CTL_1 (0x0C32) #define BB1_MSR_PERF_GLOBAL_CTL (0x0C60) #define BB1_MSR_PERF_CNT_1 (0x0C73) #define BB1_MSR_PERF_CNT_CTL_1 (0x0C72) #define R_MSR_PMON_CTL0 (0x0E10) #define R_MSR_PMON_CTR0 (0x0E11) #define R_MSR_PMON_CTL1 (0x0E12) #define R_MSR_PMON_CTR1 (0x0E13) #define R_MSR_PMON_CTL2 (0x0E14) #define R_MSR_PMON_CTR2 (0x0E15) #define R_MSR_PMON_CTL3 (0x0E16) #define R_MSR_PMON_CTR3 (0x0E17) #define R_MSR_PMON_CTL4 (0x0E18) #define R_MSR_PMON_CTR4 (0x0E19) #define R_MSR_PMON_CTL5 (0x0E1A) #define R_MSR_PMON_CTR5 (0x0E1B) #define R_MSR_PMON_CTL6 (0x0E1C) #define R_MSR_PMON_CTR6 (0x0E1D) #define R_MSR_PMON_CTL7 (0x0E1E) #define R_MSR_PMON_CTR7 (0x0E1F) #define R_MSR_PMON_CTL8 (0x0E30) #define R_MSR_PMON_CTR8 (0x0E31) #define R_MSR_PMON_CTL9 (0x0E32) #define R_MSR_PMON_CTR9 (0x0E33) #define R_MSR_PMON_CTL10 (0x0E34) #define R_MSR_PMON_CTR10 (0x0E35) #define R_MSR_PMON_CTL11 (0x0E36) #define R_MSR_PMON_CTR11 (0x0E37) #define R_MSR_PMON_CTL12 (0x0E38) #define R_MSR_PMON_CTR12 (0x0E39) #define R_MSR_PMON_CTL13 (0x0E3A) #define R_MSR_PMON_CTR13 (0x0E3B) #define R_MSR_PMON_CTL14 (0x0E3C) #define R_MSR_PMON_CTR14 (0x0E3D) #define R_MSR_PMON_CTL15 (0x0E3E) #define R_MSR_PMON_CTR15 (0x0E3F) #define R_MSR_PORT0_IPERF_CFG0 (0x0E04) #define R_MSR_PORT1_IPERF_CFG0 (0x0E05) #define R_MSR_PORT2_IPERF_CFG0 (0x0E06) #define R_MSR_PORT3_IPERF_CFG0 (0x0E07) #define R_MSR_PORT4_IPERF_CFG0 (0x0E08) #define R_MSR_PORT5_IPERF_CFG0 (0x0E09) #define R_MSR_PORT6_IPERF_CFG0 (0x0E0A) #define R_MSR_PORT7_IPERF_CFG0 (0x0E0B) #define R_MSR_PORT0_IPERF_CFG1 (0x0E24) #define R_MSR_PORT1_IPERF_CFG1 (0x0E25) #define R_MSR_PORT2_IPERF_CFG1 (0x0E26) #define R_MSR_PORT3_IPERF_CFG1 (0x0E27) #define R_MSR_PORT4_IPERF_CFG1 (0x0E28) #define R_MSR_PORT5_IPERF_CFG1 (0x0E29) #define R_MSR_PORT6_IPERF_CFG1 (0x0E2A) #define R_MSR_PORT7_IPERF_CFG1 (0x0E2B) #define R_MSR_PMON_GLOBAL_CTL_7_0 (0x0E00) #define R_MSR_PMON_GLOBAL_CTL_15_8 (0x0E20) #define W_MSR_PMON_GLOBAL_CTL (0xC80) #define W_MSR_PMON_FIXED_CTR_CTL (0x395) #define W_MSR_PMON_FIXED_CTR (0x394) /* * Platform QoS MSRs */ #define IA32_PQR_ASSOC (0xc8f) #define IA32_QM_EVTSEL (0xc8d) #define IA32_QM_CTR (0xc8e) #define PCM_INVALID_QOS_MONITORING_DATA ((std::numeric_limits::max)()) /* \brief Event Select Register format According to "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: System Programming Guide, Part 2", Figure 30-6. Layout of IA32_PERFEVTSELx MSRs Supporting Architectural Performance Monitoring Version 3 */ struct EventSelectRegister { union { struct { uint64 event_select : 8; uint64 umask : 8; uint64 usr : 1; uint64 os : 1; uint64 edge : 1; uint64 pin_control : 1; uint64 apic_int : 1; uint64 any_thread : 1; uint64 enable : 1; uint64 invert : 1; uint64 cmask : 8; uint64 in_tx : 1; uint64 in_txcp : 1; uint64 reservedX : 30; } fields; uint64 value; }; EventSelectRegister() : value(0) {} }; /* \brief Fixed Event Control Register format According to "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: System Programming Guide, Part 2", Figure 30-7. Layout of IA32_FIXED_CTR_CTRL MSR Supporting Architectural Performance Monitoring Version 3 */ struct FixedEventControlRegister { union { struct { // CTR0 uint64 os0 : 1; uint64 usr0 : 1; uint64 any_thread0 : 1; uint64 enable_pmi0 : 1; // CTR1 uint64 os1 : 1; uint64 usr1 : 1; uint64 any_thread1 : 1; uint64 enable_pmi1 : 1; // CTR2 uint64 os2 : 1; uint64 usr2 : 1; uint64 any_thread2 : 1; uint64 enable_pmi2 : 1; // CTR3 uint64 os3 : 1; uint64 usr3 : 1; uint64 any_thread3 : 1; uint64 enable_pmi3 : 1; uint64 reserved1 : 48; } fields; uint64 value; }; FixedEventControlRegister() : value(0) {} }; #ifndef KERNEL inline std::ostream & operator << (std::ostream & o, const FixedEventControlRegister & reg) { o << "os0\t\t" << reg.fields.os0 << "\n"; o << "usr0\t\t" << reg.fields.usr0 << "\n"; o << "any_thread0\t" << reg.fields.any_thread0 << "\n"; o << "enable_pmi0\t" << reg.fields.enable_pmi0 << "\n"; o << "os1\t\t" << reg.fields.os1 << "\n"; o << "usr1\t\t" << reg.fields.usr1 << "\n"; o << "any_thread1\t" << reg.fields.any_thread1 << "\n"; o << "enable_pmi10\t" << reg.fields.enable_pmi1 << "\n"; o << "os2\t\t" << reg.fields.os2 << "\n"; o << "usr2\t\t" << reg.fields.usr2 << "\n"; o << "any_thread2\t" << reg.fields.any_thread2 << "\n"; o << "enable_pmi2\t" << reg.fields.enable_pmi2 << "\n"; o << "reserved1\t" << reg.fields.reserved1 << "\n"; return o; } #endif // #ifndef KERNEL // UNCORE COUNTER CONTROL /* \brief Uncore Event Select Register Register format According to "Intel 64 and IA-32 Architectures Software Developers Manual Volume 3B: System Programming Guide, Part 2", Figure 30-20. Layout of MSR_UNCORE_PERFEVTSELx MSRs */ struct UncoreEventSelectRegister { union { struct { uint64 event_select : 8; uint64 umask : 8; uint64 reserved1 : 1; uint64 occ_ctr_rst : 1; uint64 edge : 1; uint64 reserved2 : 1; uint64 enable_pmi : 1; uint64 reserved3 : 1; uint64 enable : 1; uint64 invert : 1; uint64 cmask : 8; uint64 reservedx : 32; } fields; uint64 value; }; }; /* \brief Beckton Uncore PMU ZDP FVC Control Register format From "Intel(r) Xeon(r) Processor 7500 Series Uncore Programming Guide" Table 2-80. M_MSR_PMU_ZDP_CTL_FVC Register - Field Definitions */ struct BecktonUncorePMUZDPCTLFVCRegister { union { struct { uint64 fvid : 5; uint64 bcmd : 3; uint64 resp : 3; uint64 evnt0 : 3; uint64 evnt1 : 3; uint64 evnt2 : 3; uint64 evnt3 : 3; uint64 pbox_init_err : 1; } fields; // nehalem-ex version struct { uint64 fvid : 6; uint64 bcmd : 3; uint64 resp : 3; uint64 evnt0 : 3; uint64 evnt1 : 3; uint64 evnt2 : 3; uint64 evnt3 : 3; uint64 pbox_init_err : 1; } fields_wsm; // westmere-ex version uint64 value; }; }; /* \brief Beckton Uncore PMU Counter Control Register format From "Intel(r) Xeon(r) Processor 7500 Series Uncore Programming Guide" Table 2-67. M_MSR_PMU_CNT_CTL{5-0} Register - Field Definitions */ struct BecktonUncorePMUCNTCTLRegister { union { struct { uint64 en : 1; uint64 pmi_en : 1; uint64 count_mode : 2; uint64 storage_mode : 2; uint64 wrap_mode : 1; uint64 flag_mode : 1; uint64 rsv1 : 1; uint64 inc_sel : 5; uint64 rsv2 : 5; uint64 set_flag_sel : 3; } fields; uint64 value; }; }; #define MSR_SMI_COUNT (0x34) /* \brief Sandy Bridge energy counters */ #define MSR_PKG_ENERGY_STATUS (0x611) #define MSR_RAPL_POWER_UNIT (0x606) #define MSR_PKG_POWER_INFO (0x614) #define PCM_INTEL_PCI_VENDOR_ID (0x8086) #define PCM_PCI_VENDOR_ID_OFFSET (0) // server PCICFG uncore counters #define JKTIVT_MC0_CH0_REGISTER_DEV_ADDR (16) #define JKTIVT_MC0_CH1_REGISTER_DEV_ADDR (16) #define JKTIVT_MC0_CH2_REGISTER_DEV_ADDR (16) #define JKTIVT_MC0_CH3_REGISTER_DEV_ADDR (16) #define JKTIVT_MC0_CH0_REGISTER_FUNC_ADDR (4) #define JKTIVT_MC0_CH1_REGISTER_FUNC_ADDR (5) #define JKTIVT_MC0_CH2_REGISTER_FUNC_ADDR (0) #define JKTIVT_MC0_CH3_REGISTER_FUNC_ADDR (1) #define JKTIVT_MC1_CH0_REGISTER_DEV_ADDR (30) #define JKTIVT_MC1_CH1_REGISTER_DEV_ADDR (30) #define JKTIVT_MC1_CH2_REGISTER_DEV_ADDR (30) #define JKTIVT_MC1_CH3_REGISTER_DEV_ADDR (30) #define JKTIVT_MC1_CH0_REGISTER_FUNC_ADDR (4) #define JKTIVT_MC1_CH1_REGISTER_FUNC_ADDR (5) #define JKTIVT_MC1_CH2_REGISTER_FUNC_ADDR (0) #define JKTIVT_MC1_CH3_REGISTER_FUNC_ADDR (1) #define HSX_MC0_CH0_REGISTER_DEV_ADDR (20) #define HSX_MC0_CH1_REGISTER_DEV_ADDR (20) #define HSX_MC0_CH2_REGISTER_DEV_ADDR (21) #define HSX_MC0_CH3_REGISTER_DEV_ADDR (21) #define HSX_MC0_CH0_REGISTER_FUNC_ADDR (0) #define HSX_MC0_CH1_REGISTER_FUNC_ADDR (1) #define HSX_MC0_CH2_REGISTER_FUNC_ADDR (0) #define HSX_MC0_CH3_REGISTER_FUNC_ADDR (1) #define HSX_MC1_CH0_REGISTER_DEV_ADDR (23) #define HSX_MC1_CH1_REGISTER_DEV_ADDR (23) #define HSX_MC1_CH2_REGISTER_DEV_ADDR (24) #define HSX_MC1_CH3_REGISTER_DEV_ADDR (24) #define HSX_MC1_CH0_REGISTER_FUNC_ADDR (0) #define HSX_MC1_CH1_REGISTER_FUNC_ADDR (1) #define HSX_MC1_CH2_REGISTER_FUNC_ADDR (0) #define HSX_MC1_CH3_REGISTER_FUNC_ADDR (1) #define KNL_MC0_CH0_REGISTER_DEV_ADDR (8) #define KNL_MC0_CH1_REGISTER_DEV_ADDR (8) #define KNL_MC0_CH2_REGISTER_DEV_ADDR (8) #define KNL_MC0_CH0_REGISTER_FUNC_ADDR (2) #define KNL_MC0_CH1_REGISTER_FUNC_ADDR (3) #define KNL_MC0_CH2_REGISTER_FUNC_ADDR (4) #define SKX_MC0_CH0_REGISTER_DEV_ADDR (10) #define SKX_MC0_CH1_REGISTER_DEV_ADDR (10) #define SKX_MC0_CH2_REGISTER_DEV_ADDR (11) #define SKX_MC0_CH3_REGISTER_DEV_ADDR (-1) //Does not exist #define SKX_MC0_CH0_REGISTER_FUNC_ADDR (2) #define SKX_MC0_CH1_REGISTER_FUNC_ADDR (6) #define SKX_MC0_CH2_REGISTER_FUNC_ADDR (2) #define SKX_MC0_CH3_REGISTER_FUNC_ADDR (-1) //Does not exist #define SKX_MC1_CH0_REGISTER_DEV_ADDR (12) #define SKX_MC1_CH1_REGISTER_DEV_ADDR (12) #define SKX_MC1_CH2_REGISTER_DEV_ADDR (13) #define SKX_MC1_CH3_REGISTER_DEV_ADDR (-1) //Does not exist #define SKX_MC1_CH0_REGISTER_FUNC_ADDR (2) #define SKX_MC1_CH1_REGISTER_FUNC_ADDR (6) #define SKX_MC1_CH2_REGISTER_FUNC_ADDR (2) #define SKX_MC1_CH3_REGISTER_FUNC_ADDR (-1) //Does not exist #define SERVER_UBOX0_REGISTER_DEV_ADDR (0) #define SERVER_UBOX0_REGISTER_FUNC_ADDR (1) #define KNL_MC1_CH0_REGISTER_DEV_ADDR (9) #define KNL_MC1_CH1_REGISTER_DEV_ADDR (9) #define KNL_MC1_CH2_REGISTER_DEV_ADDR (9) #define KNL_MC1_CH0_REGISTER_FUNC_ADDR (2) #define KNL_MC1_CH1_REGISTER_FUNC_ADDR (3) #define KNL_MC1_CH2_REGISTER_FUNC_ADDR (4) #define KNL_EDC0_ECLK_REGISTER_DEV_ADDR (24) #define KNL_EDC0_ECLK_REGISTER_FUNC_ADDR (2) #define KNL_EDC1_ECLK_REGISTER_DEV_ADDR (25) #define KNL_EDC1_ECLK_REGISTER_FUNC_ADDR (2) #define KNL_EDC2_ECLK_REGISTER_DEV_ADDR (26) #define KNL_EDC2_ECLK_REGISTER_FUNC_ADDR (2) #define KNL_EDC3_ECLK_REGISTER_DEV_ADDR (27) #define KNL_EDC3_ECLK_REGISTER_FUNC_ADDR (2) #define KNL_EDC4_ECLK_REGISTER_DEV_ADDR (28) #define KNL_EDC4_ECLK_REGISTER_FUNC_ADDR (2) #define KNL_EDC5_ECLK_REGISTER_DEV_ADDR (29) #define KNL_EDC5_ECLK_REGISTER_FUNC_ADDR (2) #define KNL_EDC6_ECLK_REGISTER_DEV_ADDR (30) #define KNL_EDC6_ECLK_REGISTER_FUNC_ADDR (2) #define KNL_EDC7_ECLK_REGISTER_DEV_ADDR (31) #define KNL_EDC7_ECLK_REGISTER_FUNC_ADDR (2) #define HSX_HA0_REGISTER_DEV_ADDR (18) #define HSX_HA0_REGISTER_FUNC_ADDR (1) #define HSX_HA1_REGISTER_DEV_ADDR (18) #define HSX_HA1_REGISTER_FUNC_ADDR (5) #define XPF_HA_PCI_PMON_BOX_CTL_ADDR (0xF4) #define XPF_HA_PCI_PMON_CTL0_ADDR (0xD8 + 4*0) #define XPF_HA_PCI_PMON_CTL1_ADDR (0xD8 + 4*1) #define XPF_HA_PCI_PMON_CTL2_ADDR (0xD8 + 4*2) #define XPF_HA_PCI_PMON_CTL3_ADDR (0xD8 + 4*3) #define XPF_HA_PCI_PMON_CTR0_ADDR (0xA0 + 8*0) #define XPF_HA_PCI_PMON_CTR1_ADDR (0xA0 + 8*1) #define XPF_HA_PCI_PMON_CTR2_ADDR (0xA0 + 8*2) #define XPF_HA_PCI_PMON_CTR3_ADDR (0xA0 + 8*3) /** * XPF_ for Xeons: SNB, IVT, HSX, BDW, etc. * KNX_ for Xeon Phi (Knights *) processors */ #define XPF_MC_CH_PCI_PMON_BOX_CTL_ADDR (0x0F4) #define KNX_MC_CH_PCI_PMON_BOX_CTL_ADDR (0xB30) #define KNX_EDC_CH_PCI_PMON_BOX_CTL_ADDR (0xA30) //! for Xeons #define XPF_MC_CH_PCI_PMON_FIXED_CTL_ADDR (0x0F0) #define XPF_MC_CH_PCI_PMON_CTL3_ADDR (0x0E4) #define XPF_MC_CH_PCI_PMON_CTL2_ADDR (0x0E0) #define XPF_MC_CH_PCI_PMON_CTL1_ADDR (0x0DC) #define XPF_MC_CH_PCI_PMON_CTL0_ADDR (0x0D8) //! KNL IMC #define KNX_MC_CH_PCI_PMON_FIXED_CTL_ADDR (0xB44) #define KNX_MC_CH_PCI_PMON_CTL3_ADDR (0xB2C) #define KNX_MC_CH_PCI_PMON_CTL2_ADDR (0xB28) #define KNX_MC_CH_PCI_PMON_CTL1_ADDR (0xB24) #define KNX_MC_CH_PCI_PMON_CTL0_ADDR (0xB20) //! KNL EDC ECLK #define KNX_EDC_CH_PCI_PMON_FIXED_CTL_ADDR (0xA44) #define KNX_EDC_CH_PCI_PMON_CTL3_ADDR (0xA2C) #define KNX_EDC_CH_PCI_PMON_CTL2_ADDR (0xA28) #define KNX_EDC_CH_PCI_PMON_CTL1_ADDR (0xA24) #define KNX_EDC_CH_PCI_PMON_CTL0_ADDR (0xA20) #define KNX_EDC_ECLK_PMON_UNIT_CTL_REG (0xA30) //! for Xeons #define XPF_MC_CH_PCI_PMON_FIXED_CTR_ADDR (0x0D0) #define XPF_MC_CH_PCI_PMON_CTR3_ADDR (0x0B8) #define XPF_MC_CH_PCI_PMON_CTR2_ADDR (0x0B0) #define XPF_MC_CH_PCI_PMON_CTR1_ADDR (0x0A8) #define XPF_MC_CH_PCI_PMON_CTR0_ADDR (0x0A0) //! for KNL IMC #define KNX_MC_CH_PCI_PMON_FIXED_CTR_ADDR (0xB3C) #define KNX_MC_CH_PCI_PMON_CTR3_ADDR (0xB18) #define KNX_MC_CH_PCI_PMON_CTR2_ADDR (0xB10) #define KNX_MC_CH_PCI_PMON_CTR1_ADDR (0xB08) #define KNX_MC_CH_PCI_PMON_CTR0_ADDR (0xB00) //! for KNL EDC ECLK #define KNX_EDC_CH_PCI_PMON_FIXED_CTR_ADDR (0xA3C) #define KNX_EDC_CH_PCI_PMON_CTR3_ADDR (0xA18) #define KNX_EDC_CH_PCI_PMON_CTR2_ADDR (0xA10) #define KNX_EDC_CH_PCI_PMON_CTR1_ADDR (0xA08) #define KNX_EDC_CH_PCI_PMON_CTR0_ADDR (0xA00) #define SERVER_HBM_CH_PMON_BASE_ADDR (0x141c00) #define SERVER_HBM_CH_PMON_STEP (0x4000) #define SERVER_HBM_CH_PMON_SIZE (0x1000) #define SERVER_HBM_BOX_PMON_STEP (0x9000) #define SERVER_MC_CH_PMON_BASE_ADDR (0x22800) #define SERVER_MC_CH_PMON_STEP (0x4000) #define SERVER_MC_CH_PMON_SIZE (0x1000) #define SERVER_MC_CH_PMON_BOX_CTL_OFFSET (0x00) #define SERVER_MC_CH_PMON_CTL0_OFFSET (0x40) #define SERVER_MC_CH_PMON_CTL1_OFFSET (SERVER_MC_CH_PMON_CTL0_OFFSET + 4*1) #define SERVER_MC_CH_PMON_CTL2_OFFSET (SERVER_MC_CH_PMON_CTL0_OFFSET + 4*2) #define SERVER_MC_CH_PMON_CTL3_OFFSET (SERVER_MC_CH_PMON_CTL0_OFFSET + 4*3) #define SERVER_MC_CH_PMON_CTR0_OFFSET (0x08) #define SERVER_MC_CH_PMON_CTR1_OFFSET (SERVER_MC_CH_PMON_CTR0_OFFSET + 8*1) #define SERVER_MC_CH_PMON_CTR2_OFFSET (SERVER_MC_CH_PMON_CTR0_OFFSET + 8*2) #define SERVER_MC_CH_PMON_CTR3_OFFSET (SERVER_MC_CH_PMON_CTR0_OFFSET + 8*3) #define SERVER_MC_CH_PMON_FIXED_CTL_OFFSET (0x54) #define SERVER_MC_CH_PMON_FIXED_CTR_OFFSET (0x38) #define JKTIVT_QPI_PORT0_REGISTER_DEV_ADDR (8) #define JKTIVT_QPI_PORT0_REGISTER_FUNC_ADDR (2) #define JKTIVT_QPI_PORT1_REGISTER_DEV_ADDR (9) #define JKTIVT_QPI_PORT1_REGISTER_FUNC_ADDR (2) #define JKTIVT_QPI_PORT2_REGISTER_DEV_ADDR (24) #define JKTIVT_QPI_PORT2_REGISTER_FUNC_ADDR (2) #define HSX_QPI_PORT0_REGISTER_DEV_ADDR (8) #define HSX_QPI_PORT0_REGISTER_FUNC_ADDR (2) #define HSX_QPI_PORT1_REGISTER_DEV_ADDR (9) #define HSX_QPI_PORT1_REGISTER_FUNC_ADDR (2) #define HSX_QPI_PORT2_REGISTER_DEV_ADDR (10) #define HSX_QPI_PORT2_REGISTER_FUNC_ADDR (2) #define SKX_QPI_PORT0_REGISTER_DEV_ADDR (14) #define SKX_QPI_PORT0_REGISTER_FUNC_ADDR (0) #define SKX_QPI_PORT1_REGISTER_DEV_ADDR (15) #define SKX_QPI_PORT1_REGISTER_FUNC_ADDR (0) #define SKX_QPI_PORT2_REGISTER_DEV_ADDR (16) #define SKX_QPI_PORT2_REGISTER_FUNC_ADDR (0) #define CPX_QPI_PORT3_REGISTER_DEV_ADDR (14) #define CPX_QPI_PORT3_REGISTER_FUNC_ADDR (4) #define CPX_QPI_PORT4_REGISTER_DEV_ADDR (15) #define CPX_QPI_PORT4_REGISTER_FUNC_ADDR (4) #define CPX_QPI_PORT5_REGISTER_DEV_ADDR (16) #define CPX_QPI_PORT5_REGISTER_FUNC_ADDR (4) #define ICX_QPI_PORT0_REGISTER_DEV_ADDR (2) #define ICX_QPI_PORT0_REGISTER_FUNC_ADDR (1) #define ICX_QPI_PORT1_REGISTER_DEV_ADDR (3) #define ICX_QPI_PORT1_REGISTER_FUNC_ADDR (1) #define ICX_QPI_PORT2_REGISTER_DEV_ADDR (4) #define ICX_QPI_PORT2_REGISTER_FUNC_ADDR (1) #define SPR_QPI_PORT0_REGISTER_DEV_ADDR (1) #define SPR_QPI_PORT0_REGISTER_FUNC_ADDR (1) #define SPR_QPI_PORT1_REGISTER_DEV_ADDR (2) #define SPR_QPI_PORT1_REGISTER_FUNC_ADDR (1) #define SPR_QPI_PORT2_REGISTER_DEV_ADDR (3) #define SPR_QPI_PORT2_REGISTER_FUNC_ADDR (1) #define SPR_QPI_PORT3_REGISTER_DEV_ADDR (4) #define SPR_QPI_PORT3_REGISTER_FUNC_ADDR (1) #define QPI_PORT0_MISC_REGISTER_FUNC_ADDR (0) #define QPI_PORT1_MISC_REGISTER_FUNC_ADDR (0) #define QPI_PORT2_MISC_REGISTER_FUNC_ADDR (0) constexpr auto SKX_M3UPI_PORT0_REGISTER_DEV_ADDR = (0x12); constexpr auto SKX_M3UPI_PORT0_REGISTER_FUNC_ADDR = (1); constexpr auto SKX_M3UPI_PORT1_REGISTER_DEV_ADDR = (0x12); constexpr auto SKX_M3UPI_PORT1_REGISTER_FUNC_ADDR = (2); constexpr auto SKX_M3UPI_PORT2_REGISTER_DEV_ADDR = (0x12); constexpr auto SKX_M3UPI_PORT2_REGISTER_FUNC_ADDR = (5); constexpr auto CPX_M3UPI_PORT0_REGISTER_DEV_ADDR = (0x12); constexpr auto CPX_M3UPI_PORT0_REGISTER_FUNC_ADDR = (1); constexpr auto CPX_M3UPI_PORT1_REGISTER_DEV_ADDR = (0x12); constexpr auto CPX_M3UPI_PORT1_REGISTER_FUNC_ADDR = (2); constexpr auto CPX_M3UPI_PORT2_REGISTER_DEV_ADDR = (0x13); constexpr auto CPX_M3UPI_PORT2_REGISTER_FUNC_ADDR = (1); constexpr auto CPX_M3UPI_PORT3_REGISTER_DEV_ADDR = (0x13); constexpr auto CPX_M3UPI_PORT3_REGISTER_FUNC_ADDR = (2); constexpr auto CPX_M3UPI_PORT4_REGISTER_DEV_ADDR = (0x14); constexpr auto CPX_M3UPI_PORT4_REGISTER_FUNC_ADDR = (1); constexpr auto CPX_M3UPI_PORT5_REGISTER_DEV_ADDR = (0x14); constexpr auto CPX_M3UPI_PORT5_REGISTER_FUNC_ADDR = (2); constexpr auto ICX_M3UPI_PORT0_REGISTER_DEV_ADDR = (5); constexpr auto ICX_M3UPI_PORT1_REGISTER_DEV_ADDR = (6); constexpr auto ICX_M3UPI_PORT2_REGISTER_DEV_ADDR = (7); constexpr auto ICX_M3UPI_PORT0_REGISTER_FUNC_ADDR = (1); constexpr auto ICX_M3UPI_PORT1_REGISTER_FUNC_ADDR = (1); constexpr auto ICX_M3UPI_PORT2_REGISTER_FUNC_ADDR = (1); #define SKX_M2M_0_REGISTER_DEV_ADDR (8) #define SKX_M2M_0_REGISTER_FUNC_ADDR (0) #define SKX_M2M_1_REGISTER_DEV_ADDR (9) #define SKX_M2M_1_REGISTER_FUNC_ADDR (0) #define SERVER_M2M_0_REGISTER_DEV_ADDR (12) #define SERVER_M2M_0_REGISTER_FUNC_ADDR (0) #define SERVER_M2M_1_REGISTER_DEV_ADDR (13) #define SERVER_M2M_1_REGISTER_FUNC_ADDR (0) #define SERVER_M2M_2_REGISTER_DEV_ADDR (14) #define SERVER_M2M_2_REGISTER_FUNC_ADDR (0) #define SERVER_M2M_3_REGISTER_DEV_ADDR (15) #define SERVER_M2M_3_REGISTER_FUNC_ADDR (0) constexpr auto SERVER_HBM_M2M_0_REGISTER_DEV_ADDR = 12; constexpr auto SERVER_HBM_M2M_0_REGISTER_FUNC_ADDR = 1; constexpr auto SERVER_HBM_M2M_1_REGISTER_DEV_ADDR = 13; constexpr auto SERVER_HBM_M2M_1_REGISTER_FUNC_ADDR = 1; constexpr auto SERVER_HBM_M2M_2_REGISTER_DEV_ADDR = 14; constexpr auto SERVER_HBM_M2M_2_REGISTER_FUNC_ADDR = 1; constexpr auto SERVER_HBM_M2M_3_REGISTER_DEV_ADDR = 15; constexpr auto SERVER_HBM_M2M_3_REGISTER_FUNC_ADDR = 1; constexpr auto SERVER_HBM_M2M_4_REGISTER_DEV_ADDR = 12; constexpr auto SERVER_HBM_M2M_4_REGISTER_FUNC_ADDR = 2; constexpr auto SERVER_HBM_M2M_5_REGISTER_DEV_ADDR = 13; constexpr auto SERVER_HBM_M2M_5_REGISTER_FUNC_ADDR = 2; constexpr auto SERVER_HBM_M2M_6_REGISTER_DEV_ADDR = 14; constexpr auto SERVER_HBM_M2M_6_REGISTER_FUNC_ADDR = 2; constexpr auto SERVER_HBM_M2M_7_REGISTER_DEV_ADDR = 15; constexpr auto SERVER_HBM_M2M_7_REGISTER_FUNC_ADDR = 2; constexpr auto SERVER_HBM_M2M_8_REGISTER_DEV_ADDR = 12; constexpr auto SERVER_HBM_M2M_8_REGISTER_FUNC_ADDR = 3; constexpr auto SERVER_HBM_M2M_9_REGISTER_DEV_ADDR = 13; constexpr auto SERVER_HBM_M2M_9_REGISTER_FUNC_ADDR = 3; constexpr auto SERVER_HBM_M2M_10_REGISTER_DEV_ADDR = 14; constexpr auto SERVER_HBM_M2M_10_REGISTER_FUNC_ADDR = 3; constexpr auto SERVER_HBM_M2M_11_REGISTER_DEV_ADDR = 15; constexpr auto SERVER_HBM_M2M_11_REGISTER_FUNC_ADDR = 3; constexpr auto SERVER_HBM_M2M_12_REGISTER_DEV_ADDR = 12; constexpr auto SERVER_HBM_M2M_12_REGISTER_FUNC_ADDR = 4; constexpr auto SERVER_HBM_M2M_13_REGISTER_DEV_ADDR = 13; constexpr auto SERVER_HBM_M2M_13_REGISTER_FUNC_ADDR = 4; constexpr auto SERVER_HBM_M2M_14_REGISTER_DEV_ADDR = 14; constexpr auto SERVER_HBM_M2M_14_REGISTER_FUNC_ADDR = 4; constexpr auto SERVER_HBM_M2M_15_REGISTER_DEV_ADDR = 15; constexpr auto SERVER_HBM_M2M_15_REGISTER_FUNC_ADDR = 4; #define SKX_M2M_PCI_PMON_BOX_CTL_ADDR (0x258) #define SKX_M2M_PCI_PMON_CTL0_ADDR (0x228) #define SKX_M2M_PCI_PMON_CTL1_ADDR (0x230) #define SKX_M2M_PCI_PMON_CTL2_ADDR (0x238) #define SKX_M2M_PCI_PMON_CTL3_ADDR (0x240) #define SKX_M2M_PCI_PMON_CTR0_ADDR (0x200) #define SKX_M2M_PCI_PMON_CTR1_ADDR (0x208) #define SKX_M2M_PCI_PMON_CTR2_ADDR (0x210) #define SKX_M2M_PCI_PMON_CTR3_ADDR (0x218) #define SERVER_M2M_PCI_PMON_BOX_CTL_ADDR (0x438) #define SERVER_M2M_PCI_PMON_CTL0_ADDR (0x468) #define SERVER_M2M_PCI_PMON_CTL1_ADDR (SERVER_M2M_PCI_PMON_CTL0_ADDR + 1*8) #define SERVER_M2M_PCI_PMON_CTL2_ADDR (SERVER_M2M_PCI_PMON_CTL0_ADDR + 2*8) #define SERVER_M2M_PCI_PMON_CTL3_ADDR (SERVER_M2M_PCI_PMON_CTL0_ADDR + 3*8) #define SERVER_M2M_PCI_PMON_CTR0_ADDR (0x440) #define SERVER_M2M_PCI_PMON_CTR1_ADDR (SERVER_M2M_PCI_PMON_CTR0_ADDR + 1*8) #define SERVER_M2M_PCI_PMON_CTR2_ADDR (SERVER_M2M_PCI_PMON_CTR0_ADDR + 2*8) #define SERVER_M2M_PCI_PMON_CTR3_ADDR (SERVER_M2M_PCI_PMON_CTR0_ADDR + 3*8) constexpr auto M3UPI_PCI_PMON_BOX_CTL_ADDR = (0xF4); constexpr auto M3UPI_PCI_PMON_CTL0_ADDR = (0xD8); constexpr auto M3UPI_PCI_PMON_CTL1_ADDR = (0xDC); constexpr auto M3UPI_PCI_PMON_CTL2_ADDR = (0xE0); constexpr auto M3UPI_PCI_PMON_CTR0_ADDR = (0xA0); constexpr auto M3UPI_PCI_PMON_CTR1_ADDR = (0xA8); constexpr auto M3UPI_PCI_PMON_CTR2_ADDR = (0xB0); constexpr auto ICX_M3UPI_PCI_PMON_BOX_CTL_ADDR = (0xA0); constexpr auto ICX_M3UPI_PCI_PMON_CTL0_ADDR = (0xD8); constexpr auto ICX_M3UPI_PCI_PMON_CTL1_ADDR = (0xDC); constexpr auto ICX_M3UPI_PCI_PMON_CTL2_ADDR = (0xE0); constexpr auto ICX_M3UPI_PCI_PMON_CTL3_ADDR = (0xE4); constexpr auto ICX_M3UPI_PCI_PMON_CTR0_ADDR = (0xA8); constexpr auto ICX_M3UPI_PCI_PMON_CTR1_ADDR = (0xB0); constexpr auto ICX_M3UPI_PCI_PMON_CTR2_ADDR = (0xB8); constexpr auto ICX_M3UPI_PCI_PMON_CTR3_ADDR = (0xC0); constexpr auto MSR_UNCORE_PMON_GLOBAL_CTL = 0x700; constexpr auto IVT_MSR_UNCORE_PMON_GLOBAL_CTL = 0x0C00; constexpr auto SPR_MSR_UNCORE_PMON_GLOBAL_CTL = 0x2FF0; #define PCM_INVALID_DEV_ADDR (~(uint32)0UL) #define PCM_INVALID_FUNC_ADDR (~(uint32)0UL) #define Q_P_PCI_PMON_BOX_CTL_ADDR (0x0F4) #define Q_P_PCI_PMON_CTL3_ADDR (0x0E4) #define Q_P_PCI_PMON_CTL2_ADDR (0x0E0) #define Q_P_PCI_PMON_CTL1_ADDR (0x0DC) #define Q_P_PCI_PMON_CTL0_ADDR (0x0D8) #define Q_P_PCI_PMON_CTR3_ADDR (0x0B8) #define Q_P_PCI_PMON_CTR2_ADDR (0x0B0) #define Q_P_PCI_PMON_CTR1_ADDR (0x0A8) #define Q_P_PCI_PMON_CTR0_ADDR (0x0A0) #define QPI_RATE_STATUS_ADDR (0x0D4) #define U_L_PCI_PMON_BOX_CTL_ADDR (0x378) #define U_L_PCI_PMON_CTL3_ADDR (0x368) #define U_L_PCI_PMON_CTL2_ADDR (0x360) #define U_L_PCI_PMON_CTL1_ADDR (0x358) #define U_L_PCI_PMON_CTL0_ADDR (0x350) #define U_L_PCI_PMON_CTR3_ADDR (0x330) #define U_L_PCI_PMON_CTR2_ADDR (0x328) #define U_L_PCI_PMON_CTR1_ADDR (0x320) #define U_L_PCI_PMON_CTR0_ADDR (0x318) #define ICX_UPI_PCI_PMON_BOX_CTL_ADDR (0x318) #define ICX_UPI_PCI_PMON_CTL3_ADDR (0x368) #define ICX_UPI_PCI_PMON_CTL2_ADDR (0x360) #define ICX_UPI_PCI_PMON_CTL1_ADDR (0x358) #define ICX_UPI_PCI_PMON_CTL0_ADDR (0x350) #define ICX_UPI_PCI_PMON_CTR3_ADDR (0x338) #define ICX_UPI_PCI_PMON_CTR2_ADDR (0x330) #define ICX_UPI_PCI_PMON_CTR1_ADDR (0x328) #define ICX_UPI_PCI_PMON_CTR0_ADDR (0x320) constexpr auto SPR_UPI_PCI_PMON_BOX_CTL_ADDR = 0x318; constexpr auto SPR_UPI_PCI_PMON_CTL0_ADDR = 0x350; constexpr auto SPR_UPI_PCI_PMON_CTR0_ADDR = 0x320; #define UCLK_FIXED_CTR_ADDR (0x704) #define UCLK_FIXED_CTL_ADDR (0x703) #define UBOX_MSR_PMON_CTL0_ADDR (0x705) #define UBOX_MSR_PMON_CTL1_ADDR (0x706) #define UBOX_MSR_PMON_CTR0_ADDR (0x709) #define UBOX_MSR_PMON_CTR1_ADDR (0x70a) constexpr auto SPR_UCLK_FIXED_CTR_ADDR = 0x2FDF; constexpr auto SPR_UCLK_FIXED_CTL_ADDR = 0x2FDE; constexpr auto SPR_UBOX_MSR_PMON_BOX_CTL_ADDR = 0x2FD0; constexpr auto SPR_UBOX_MSR_PMON_CTL0_ADDR = 0x2FD2; constexpr auto SPR_UBOX_MSR_PMON_CTL1_ADDR = 0x2FD3; constexpr auto SPR_UBOX_MSR_PMON_CTR0_ADDR = 0X2FD8; constexpr auto SPR_UBOX_MSR_PMON_CTR1_ADDR = 0X2FD9; constexpr auto JKTIVT_UCLK_FIXED_CTR_ADDR = (0x0C09); constexpr auto JKTIVT_UCLK_FIXED_CTL_ADDR = (0x0C08); constexpr auto JKTIVT_UBOX_MSR_PMON_CTL0_ADDR = (0x0C10); constexpr auto JKTIVT_UBOX_MSR_PMON_CTL1_ADDR = (0x0C11); constexpr auto JKTIVT_UBOX_MSR_PMON_CTR0_ADDR = (0x0C16); constexpr auto JKTIVT_UBOX_MSR_PMON_CTR1_ADDR = (0x0C17); #define JKTIVT_PCU_MSR_PMON_CTR3_ADDR (0x0C39) #define JKTIVT_PCU_MSR_PMON_CTR2_ADDR (0x0C38) #define JKTIVT_PCU_MSR_PMON_CTR1_ADDR (0x0C37) #define JKTIVT_PCU_MSR_PMON_CTR0_ADDR (0x0C36) #define JKTIVT_PCU_MSR_PMON_BOX_FILTER_ADDR (0x0C34) #define JKTIVT_PCU_MSR_PMON_CTL3_ADDR (0x0C33) #define JKTIVT_PCU_MSR_PMON_CTL2_ADDR (0x0C32) #define JKTIVT_PCU_MSR_PMON_CTL1_ADDR (0x0C31) #define JKTIVT_PCU_MSR_PMON_CTL0_ADDR (0x0C30) #define JKTIVT_PCU_MSR_PMON_BOX_CTL_ADDR (0x0C24) #define HSX_PCU_MSR_PMON_CTR3_ADDR (0x071A) #define HSX_PCU_MSR_PMON_CTR2_ADDR (0x0719) #define HSX_PCU_MSR_PMON_CTR1_ADDR (0x0718) #define HSX_PCU_MSR_PMON_CTR0_ADDR (0x0717) #define HSX_PCU_MSR_PMON_BOX_FILTER_ADDR (0x0715) #define HSX_PCU_MSR_PMON_CTL3_ADDR (0x0714) #define HSX_PCU_MSR_PMON_CTL2_ADDR (0x0713) #define HSX_PCU_MSR_PMON_CTL1_ADDR (0x0712) #define HSX_PCU_MSR_PMON_CTL0_ADDR (0x0711) #define HSX_PCU_MSR_PMON_BOX_CTL_ADDR (0x0710) #define UNC_PMON_UNIT_CTL_RST_CONTROL (1 << 0) #define UNC_PMON_UNIT_CTL_RST_COUNTERS (1 << 1) #define UNC_PMON_UNIT_CTL_FRZ (1 << 8) #define UNC_PMON_UNIT_CTL_FRZ_EN (1 << 16) #define UNC_PMON_UNIT_CTL_RSV ((1 << 16) + (1 << 17)) #define SPR_UNC_PMON_UNIT_CTL_FRZ (1 << 0) #define SPR_UNC_PMON_UNIT_CTL_RST_CONTROL (1 << 8) #define SPR_UNC_PMON_UNIT_CTL_RST_COUNTERS (1 << 9) #define UNC_PMON_UNIT_CTL_VALID_BITS_MASK ((1 << 17) - 1) #define MC_CH_PCI_PMON_FIXED_CTL_RST (1 << 19) #define MC_CH_PCI_PMON_FIXED_CTL_EN (1 << 22) #define EDC_CH_PCI_PMON_FIXED_CTL_EN (1 << 0) #define MC_CH_PCI_PMON_CTL_EVENT(x) (x << 0) #define MC_CH_PCI_PMON_CTL_UMASK(x) (x << 8) #define MC_CH_PCI_PMON_CTL_RST (1 << 17) #define MC_CH_PCI_PMON_CTL_EDGE_DET (1 << 18) #define MC_CH_PCI_PMON_CTL_EN (1 << 22) #define MC_CH_PCI_PMON_CTL_INVERT (1 << 23) #define MC_CH_PCI_PMON_CTL_THRESH(x) (x << 24UL) #define Q_P_PCI_PMON_CTL_EVENT(x) (x << 0) #define Q_P_PCI_PMON_CTL_UMASK(x) (x << 8) #define Q_P_PCI_PMON_CTL_RST (1 << 17) #define Q_P_PCI_PMON_CTL_EDGE_DET (1 << 18) #define Q_P_PCI_PMON_CTL_EVENT_EXT (1 << 21) #define Q_P_PCI_PMON_CTL_EN (1 << 22) #define Q_P_PCI_PMON_CTL_INVERT (1 << 23) #define Q_P_PCI_PMON_CTL_THRESH(x) (x << 24UL) #define PCU_MSR_PMON_BOX_FILTER_BAND_0(x) (x << 0) #define PCU_MSR_PMON_BOX_FILTER_BAND_1(x) (x << 8) #define PCU_MSR_PMON_BOX_FILTER_BAND_2(x) (x << 16) #define PCU_MSR_PMON_BOX_FILTER_BAND_3(x) (x << 24) #define PCU_MSR_PMON_CTL_EVENT(x) (x << 0) #define PCU_MSR_PMON_CTL_OCC_SEL(x) (x << 14) #define PCU_MSR_PMON_CTL_RST (1 << 17) #define PCU_MSR_PMON_CTL_EDGE_DET (1 << 18) #define PCU_MSR_PMON_CTL_EXTRA_SEL (1 << 21) #define PCU_MSR_PMON_CTL_EN (1 << 22) #define PCU_MSR_PMON_CTL_INVERT (1 << 23) #define PCU_MSR_PMON_CTL_THRESH(x) (x << 24UL) #define PCU_MSR_PMON_CTL_OCC_INVERT (1UL << 30UL) #define PCU_MSR_PMON_CTL_OCC_EDGE_DET (1UL << 31UL) #define JKT_C0_MSR_PMON_CTR3 0x0D19 // CBo 0 PMON Counter 3 #define JKT_C0_MSR_PMON_CTR2 0x0D18 // CBo 0 PMON Counter 2 #define JKT_C0_MSR_PMON_CTR1 0x0D17 // CBo 0 PMON Counter 1 #define JKT_C0_MSR_PMON_CTR0 0x0D16 // CBo 0 PMON Counter 0 #define JKT_C0_MSR_PMON_BOX_FILTER 0x0D14 // CBo 0 PMON Filter #define JKT_C0_MSR_PMON_CTL3 0x0D13 // CBo 0 PMON Control for Counter 3 #define JKT_C0_MSR_PMON_CTL2 0x0D12 // CBo 0 PMON Control for Counter 2 #define JKT_C0_MSR_PMON_CTL1 0x0D11 // CBo 0 PMON Control for Counter 1 #define JKT_C0_MSR_PMON_CTL0 0x0D10 // CBo 0 PMON Control for Counter 0 #define JKT_C0_MSR_PMON_BOX_CTL 0x0D04 // CBo 0 PMON Box-Wide Control #define JKTIVT_CBO_MSR_STEP 0x0020 // CBo MSR Step #define IVT_C0_MSR_PMON_BOX_FILTER1 0x0D1A // CBo 0 PMON Filter 1 #define HSX_C0_MSR_PMON_CTR3 0x0E0B // CBo 0 PMON Counter 3 #define HSX_C0_MSR_PMON_CTR2 0x0E0A // CBo 0 PMON Counter 2 #define HSX_C0_MSR_PMON_CTR1 0x0E09 // CBo 0 PMON Counter 1 #define HSX_C0_MSR_PMON_CTR0 0x0E08 // CBo 0 PMON Counter 0 #define HSX_C0_MSR_PMON_BOX_FILTER1 0x0E06 // CBo 0 PMON Filter1 #define HSX_C0_MSR_PMON_BOX_FILTER 0x0E05 // CBo 0 PMON Filter0 #define HSX_C0_MSR_PMON_CTL3 0x0E04 // CBo 0 PMON Control for Counter 3 #define HSX_C0_MSR_PMON_CTL2 0x0E03 // CBo 0 PMON Control for Counter 2 #define HSX_C0_MSR_PMON_CTL1 0x0E02 // CBo 0 PMON Control for Counter 1 #define HSX_C0_MSR_PMON_CTL0 0x0E01 // CBo 0 PMON Control for Counter 0 #define HSX_C0_MSR_PMON_BOX_STATUS 0x0E07 // CBo 0 PMON Box-Wide Status #define HSX_C0_MSR_PMON_BOX_CTL 0x0E00 // CBo 0 PMON Box-Wide Control #define HSX_CBO_MSR_STEP 0x0010 // CBo MSR Step #define KNL_CHA_MSR_STEP 0x000C // CHA MSR Step #define KNL_CHA0_MSR_PMON_BOX_CTRL 0x0E00 // CHA 0 PMON Control #define KNL_CHA0_MSR_PMON_EVT_SEL0 0x0E01 // CHA 0 PMON Event Select for Counter 0 #define KNL_CHA0_MSR_PMON_EVT_SEL1 0x0E02 // CHA 0 PMON Event Select for Counter 1 #define KNL_CHA0_MSR_PMON_EVT_SEL2 0x0E03 // CHA 0 PMON Event Select for Counter 2 #define KNL_CHA0_MSR_PMON_EVT_SEL3 0x0E04 // CHA 0 PMON Event Select for Counter 3 #define KNL_CHA0_MSR_PMON_BOX_CTL 0x0E05 // PERF_UNIT_CTL_CHA_0 #define KNL_CHA0_MSR_PMON_BOX_CTL1 0x0E06 // PERF_UNIT_CTL_1_CHA_0 #define KNL_CHA0_MSR_PMON_BOX_STATUS 0x0E07 // CHA 0 PMON Status #define KNL_CHA0_MSR_PMON_CTR0 0x0E08 // CHA 0 PMON Counter 0 #define KNL_CHA0_MSR_PMON_CTR1 0x0E09 // CHA 0 PMON Counter 1 #define KNL_CHA0_MSR_PMON_CTR2 0x0E0A // CHA 0 PMON Counter 2 #define KNL_CHA0_MSR_PMON_CTR3 0x0E0B // CHA 0 PMON Counter 3 static const uint32 ICX_CHA_MSR_PMON_BOX_CTL[] = { 0x0E00, 0x0E0E, 0x0E1C, 0x0E2A, 0x0E38, 0x0E46, 0x0E54, 0x0E62, 0x0E70, 0x0E7E, 0x0E8C, 0x0E9A, 0x0EA8, 0x0EB6, 0x0EC4, 0x0ED2, 0x0EE0, 0x0EEE, 0x0F0A, 0x0F18, 0x0F26, 0x0F34, 0x0F42, 0x0F50, 0x0F5E, 0x0F6C, 0x0F7A, 0x0F88, 0x0F96, 0x0FA4, 0x0FB2, 0x0FC0, 0x0FCE, 0x0FDC, 0x0B60, 0x0B6E, 0x0B7C, 0x0B8A, 0x0B98, 0x0BA6, 0x0BB4, 0x0BC2 }; static const uint32 SNR_CHA_MSR_PMON_BOX_CTL[] = { 0x1C00, 0x1C10, 0x1C20, 0x1C30, 0x1C40, 0x1C50 }; #define SERVER_CHA_MSR_PMON_CTL0_OFFSET (1) /* #define SERVER_CHA_MSR_PMON_CTL1_OFFSET (2) #define SERVER_CHA_MSR_PMON_CTL2_OFFSET (3) #define SERVER_CHA_MSR_PMON_CTL3_OFFSET (4) */ #define SERVER_CHA_MSR_PMON_BOX_FILTER_OFFSET (5) #define SERVER_CHA_MSR_PMON_CTR0_OFFSET (8) /* #define SERVER_CHA_MSR_PMON_CTR1_OFFSET (9) #define SERVER_CHA_MSR_PMON_CTR2_OFFSET (10) #define SERVER_CHA_MSR_PMON_CTR3_OFFSET (11) */ constexpr auto SPR_CHA0_MSR_PMON_BOX_CTRL = 0x2000; constexpr auto SPR_CHA0_MSR_PMON_CTL0 = 0x2002; constexpr auto SPR_CHA0_MSR_PMON_CTR0 = 0x2008; constexpr auto SPR_CHA0_MSR_PMON_BOX_FILTER = 0x200E; constexpr auto SPR_CHA_MSR_STEP = 0x10; #define CBO_MSR_PMON_CTL_EVENT(x) (x << 0) #define CBO_MSR_PMON_CTL_UMASK(x) (x << 8) #define CBO_MSR_PMON_CTL_RST (1 << 17) #define CBO_MSR_PMON_CTL_EDGE_DET (1 << 18) #define CBO_MSR_PMON_CTL_TID_EN (1 << 19) #define CBO_MSR_PMON_CTL_EN (1 << 22) #define CBO_MSR_PMON_CTL_INVERT (1 << 23) #define CBO_MSR_PMON_CTL_THRESH(x) (x << 24UL) #define UNC_PMON_CTL_UMASK_EXT(x) (uint64(x) << 32ULL) #define UNC_PMON_CTL_EVENT(x) (x << 0) #define UNC_PMON_CTL_UMASK(x) (x << 8) #define JKT_CBO_MSR_PMON_BOX_FILTER_OPC(x) (x << 23UL) #define IVTHSX_CBO_MSR_PMON_BOX_FILTER1_OPC(x) (x << 20UL) #define BDX_CBO_MSR_PMON_BOX_GET_OPC0(x) ((x >> 20) & 0x3FF) #define BDX_CBO_MSR_PMON_BOX_GET_FLT(x) ((x >> 0x10) & 0x1) #define BDX_CBO_MSR_PMON_BOX_GET_TID(x) ((x >> 0x11) & 0x1) #define SKX_CHA_MSR_PMON_BOX_FILTER1_REM(x) (x << 0UL) #define SKX_CHA_MSR_PMON_BOX_FILTER1_LOC(x) (x << 1UL) #define SKX_CHA_MSR_PMON_BOX_FILTER1_NM(x) (x << 4UL) #define SKX_CHA_MSR_PMON_BOX_FILTER1_NOT_NM(x) (x << 5UL) #define SKX_CHA_MSR_PMON_BOX_FILTER1_OPC0(x) ((x) << 9UL) #define SKX_CHA_MSR_PMON_BOX_FILTER1_OPC1(x) ((x) << 19UL) #define SKX_CHA_MSR_PMON_BOX_FILTER1_NC(x) (x << 30UL) #define SKX_CHA_MSR_PMON_BOX_FILTER1_RSV(x) (x << 2UL) #define SKX_CHA_MSR_PMON_BOX_GET_OPC0(x) ((x >> 9) & 0x3FF) #define SKX_CHA_MSR_PMON_BOX_GET_NC(x) ((x >> 0x1e) & 0x1) #define SKX_CHA_TOR_INSERTS_UMASK_IRQ(x) (x << 0) #define SKX_CHA_TOR_INSERTS_UMASK_PRQ(x) (x << 2) #define SKX_CHA_TOR_INSERTS_UMASK_HIT(x) (x << 4) #define SKX_CHA_TOR_INSERTS_UMASK_MISS(x) (x << 5) /*ICX UmaskExt Filter*/ #define ICX_CHA_UMASK_EXT(x) (x << 32UL) #define SKX_IIO_CBDMA_UNIT_STATUS (0x0A47) #define SKX_IIO_CBDMA_UNIT_CTL (0x0A40) #define SKX_IIO_CBDMA_CTR0 (0x0A41) #define SKX_IIO_CBDMA_CLK (0x0A45) #define SKX_IIO_CBDMA_CTL0 (0x0A48) #define SKX_IIO_PM_REG_STEP (0x0020) #define ICX_IIO_CBDMA_UNIT_STATUS (0x0A57) #define ICX_IIO_CTL_REG_OFFSET (0x0008) #define ICX_IIO_CTR_REG_OFFSET (0x0001) /* * M2IOSF MSRs in order: * M2IOSF0 - PCIe0 stack * M2IOSF1 - PCIe1 stack * M2IOSF2 - MCP stack * M2IOSF3 - PCIe2 stack * M2IOSF4 - PCIe3 stack * M2IOSF5 - CBDMA/DMI stack */ static const uint32 ICX_IIO_UNIT_CTL[] = { 0x0A50, 0x0A70, 0x0A90, 0x0AE0, 0x0B00, 0x0B20 }; static const uint32 SPR_IRP_UNIT_CTL[] = { 0x3400, 0x3410, 0x3420, 0x3430, 0x3440, 0x3450, 0x3460, 0x3470, 0x3480, 0x3490, 0x34A0, 0x34B0 }; #define SPR_IRP_CTL_REG_OFFSET (0x0002) #define SPR_IRP_CTR_REG_OFFSET (0x0008) static const uint32 ICX_IRP_UNIT_CTL[] = { 0x0A4A, 0x0A6A, 0x0A8A, 0x0ADA, 0x0AFA, 0x0B1A }; #define ICX_IRP_CTL_REG_OFFSET (0x0003) #define ICX_IRP_CTR_REG_OFFSET (0x0001) static const uint32 SNR_IRP_UNIT_CTL[] = { 0x1EA0, 0x1EB0, 0x1EC0, 0x1ED0, 0x1EE0 }; #define SNR_IRP_CTL_REG_OFFSET (0x0008) #define SNR_IRP_CTR_REG_OFFSET (0x0001) static const uint32 SKX_IRP_UNIT_CTL[] = { 0x0A58, 0x0A78, 0x0A98, 0x0AB8, 0x0AD8, 0x0AF8 }; #define SKX_IRP_CTL_REG_OFFSET (0x0003) #define SKX_IRP_CTR_REG_OFFSET (0x0001) #define SNR_IIO_CBDMA_UNIT_STATUS (0x1E07) #define SNR_IIO_CBDMA_UNIT_CTL (0x1E00) #define SNR_IIO_CBDMA_CTR0 (0x1E01) #define SNR_IIO_CBDMA_CTL0 (0x1E08) #define SNR_IIO_PM_REG_STEP (0x0010) constexpr auto SPR_M2IOSF_IIO_UNIT_CTL = 0x3000; constexpr auto SPR_M2IOSF_IIO_CTR0 = 0x3008; constexpr auto SPR_M2IOSF_IIO_CTL0 = 0x3002; constexpr auto SPR_M2IOSF_REG_STEP = 0x10; constexpr auto SPR_M2IOSF_NUM = 12; constexpr auto CXL_PMON_SIZE = 0x1000; #define IIO_MSR_PMON_CTL_EVENT(x) ((x) << 0) #define IIO_MSR_PMON_CTL_UMASK(x) ((x) << 8) #define IIO_MSR_PMON_CTL_RST (1 << 17) #define IIO_MSR_PMON_CTL_EDGE_DET (1 << 18) #define IIO_MSR_PMON_CTL_OV_EN (1 << 20) #define IIO_MSR_PMON_CTL_EN (1 << 22) #define IIO_MSR_PMON_CTL_INVERT (1 << 23) #define IIO_MSR_PMON_CTL_THRESH(x) ((x) << 24ULL) #define IIO_MSR_PMON_CTL_CH_MASK(x) ((x) << 36ULL) #define IIO_MSR_PMON_CTL_FC_MASK(x) ((x) << 44ULL) #define ICX_IIO_MSR_PMON_CTL_EVENT(x) ((x) << 0) #define ICX_IIO_MSR_PMON_CTL_UMASK(x) ((x) << 8) #define ICX_IIO_MSR_PMON_CTL_RST (1 << 17) #define ICX_IIO_MSR_PMON_CTL_EDGE_DET (1 << 18) #define ICX_IIO_MSR_PMON_CTL_OV_EN (1 << 20) #define ICX_IIO_MSR_PMON_CTL_EN (1 << 22) #define ICX_IIO_MSR_PMON_CTL_INVERT (1 << 23) #define ICX_IIO_MSR_PMON_CTL_THRESH(x) ((x) << 24ULL) #define ICX_IIO_MSR_PMON_CTL_CH_MASK(x) ((x) << 36ULL) #define ICX_IIO_MSR_PMON_CTL_FC_MASK(x) ((x) << 48ULL) #define M2M_PCI_PMON_CTL_EVENT(x) ((x) << 0) #define M2M_PCI_PMON_CTL_UMASK(x) ((x) << 8) #define M2M_PCI_PMON_CTL_RST (1 << 17) #define M2M_PCI_PMON_CTL_EDGE_DET (1 << 18) #define M2M_PCI_PMON_CTL_OV_EN (1 << 20) #define M2M_PCI_PMON_CTL_EN (1 << 22) #define M2M_PCI_PMON_CTL_INVERT (1 << 23) #define M2M_PCI_PMON_CTL_THRESH(x) ((x) << 24ULL) #define HA_PCI_PMON_CTL_EVENT(x) ((x) << 0) #define HA_PCI_PMON_CTL_UMASK(x) ((x) << 8) #define HA_PCI_PMON_CTL_RST (1 << 17) #define HA_PCI_PMON_CTL_EDGE_DET (1 << 18) #define HA_PCI_PMON_CTL_OV_EN (1 << 20) #define HA_PCI_PMON_CTL_EN (1 << 22) #define HA_PCI_PMON_CTL_INVERT (1 << 23) #define HA_PCI_PMON_CTL_THRESH(x) ((x) << 24ULL) #define UCLK_FIXED_CTL_OV_EN (1 << 20) #define UCLK_FIXED_CTL_EN (1 << 22) /* \brief IIO Performance Monitoring Control Register format IIOn_MSR_PMON_CTL{3-0} Register- Field Definitions */ struct IIOPMUCNTCTLRegister { union { struct { uint64 event_select : 8; uint64 umask : 8; uint64 reserved1 : 1; uint64 reset : 1; uint64 edge_det : 1; uint64 ignored : 1; uint64 overflow_enable : 1; uint64 reserved2 : 1; uint64 enable : 1; uint64 invert : 1; uint64 thresh : 12; uint64 ch_mask : 8; uint64 fc_mask : 3; uint64 reservedX : 17; } fields; uint64 value; }; IIOPMUCNTCTLRegister() : value(0) { } IIOPMUCNTCTLRegister(const uint64 v) : value(v) { } operator uint64() { return value; } }; struct ICX_IIOPMUCNTCTLRegister { union { struct { uint64 event_select : 8; uint64 umask : 8; uint64 reserved1 : 1; uint64 reset : 1; uint64 edge_det : 1; uint64 ignored : 1; uint64 overflow_enable : 1; uint64 reserved2 : 1; uint64 enable : 1; uint64 invert : 1; uint64 thresh : 12; uint64 ch_mask : 12; uint64 fc_mask : 3; uint64 reservedX : 13; } fields; uint64 value; }; ICX_IIOPMUCNTCTLRegister() : value(0) { } }; #define MSR_PACKAGE_THERM_STATUS (0x01B1) #define MSR_IA32_THERM_STATUS (0x019C) #define PCM_INVALID_THERMAL_HEADROOM ((std::numeric_limits::min)()) #define MSR_IA32_BIOS_SIGN_ID (0x8B) #define MSR_DRAM_ENERGY_STATUS (0x0619) #define MSR_PKG_C2_RESIDENCY (0x60D) #define MSR_PKG_C3_RESIDENCY (0x3F8) #define MSR_PKG_C6_RESIDENCY (0x3F9) #define MSR_PKG_C7_RESIDENCY (0x3FA) #define MSR_CORE_C3_RESIDENCY (0x3FC) #define MSR_CORE_C6_RESIDENCY (0x3FD) #define MSR_CORE_C7_RESIDENCY (0x3FE) #define MSR_PERF_GLOBAL_INUSE (0x392) #define MSR_IA32_SPEC_CTRL (0x48) #define MSR_IA32_ARCH_CAPABILITIES (0x10A) #define MSR_TSX_FORCE_ABORT (0x10f) #define MSR_PERF_CAPABILITIES (0x345) // data structure for converting two uint32s <-> uin64 union cvt_ds { #ifndef _MSC_VER typedef uint64 UINT64; typedef uint32 DWORD; #endif UINT64 ui64; struct { DWORD low; DWORD high; } ui32; }; #ifndef KERNEL struct MCFGRecord { unsigned long long baseAddress; unsigned short PCISegmentGroupNumber; unsigned char startBusNumber; unsigned char endBusNumber; char reserved[4]; MCFGRecord() : baseAddress(0), PCISegmentGroupNumber(0), startBusNumber(0), endBusNumber(0) { std::fill(reserved, reserved + 4, 0); } void print() { std::cout << "BaseAddress=" << (std::hex) << "0x" << baseAddress << " PCISegmentGroupNumber=0x" << PCISegmentGroupNumber << " startBusNumber=0x" << (unsigned)startBusNumber << " endBusNumber=0x" << (unsigned)endBusNumber << "\n" << std::dec; } }; struct MCFGHeader { char signature[4]; unsigned length; unsigned char revision; unsigned char checksum; char OEMID[6]; char OEMTableID[8]; unsigned OEMRevision; unsigned creatorID; unsigned creatorRevision; char reserved[8]; unsigned nrecords() const { return (length - sizeof(MCFGHeader)) / sizeof(MCFGRecord); } void print() { std::cout << "Header: length=" << length << " nrecords=" << nrecords() << "\n"; } }; #endif // #ifndef KERNEL //IDX accel device/func number(PCIe). //The device/function number from SPR register guide. #define SPR_IDX_IAA_REGISTER_DEV_ADDR (2) #define SPR_IDX_IAA_REGISTER_FUNC_ADDR (0) #define SPR_IDX_DSA_REGISTER_DEV_ADDR (1) #define SPR_IDX_DSA_REGISTER_FUNC_ADDR (0) #define SPR_IDX_QAT_REGISTER_DEV_ADDR (0) #define SPR_IDX_QAT_REGISTER_FUNC_ADDR (0) //IDX accel perfmon register offset //The offset of register from DSA external architecture spec(intel-data-streaming-accelerator-preliminary-architecture-specification). #define SPR_IDX_ACCEL_PCICMD_OFFSET (0x4) #define SPR_IDX_ACCEL_BAR0_OFFSET (0x10) #define SPR_IDX_ACCEL_BAR0_SIZE (0x10000) #define SPR_IDX_ACCEL_TABLE_OFFSET (0x60) #define SPR_IDX_ACCEL_PMON_BASE_OFFSET (0x68) #define SPR_IDX_ACCEL_PMON_BASE_MASK (0xFFFF) #define SPR_IDX_ACCEL_PMON_BASE_RATIO (0x100) #define SPR_IDX_ACCEL_PMCSR_OFFSET (0x94) #define SPR_IDX_PMON_RESET_CTL_OFFSET (0x10) #define SPR_IDX_PMON_FREEZE_CTL_OFFSET (0x20) #define SPR_IDX_PMON_CTL_OFFSET(x) (0x100 + ((x)*8)) #define SPR_IDX_PMON_CTR_OFFSET(x) (0x200 + ((x)*8)) #define SPR_IDX_PMON_FILTER_WQ_OFFSET(x) (0x300 + ((x)*32)) #define SPR_IDX_PMON_FILTER_TC_OFFSET(x) (0x304 + ((x)*32)) #define SPR_IDX_PMON_FILTER_PGSZ_OFFSET(x) (0x308 + ((x)*32)) #define SPR_IDX_PMON_FILTER_XFERSZ_OFFSET(x) (0x30C + ((x)*32)) #define SPR_IDX_PMON_FILTER_ENG_OFFSET(x) (0x310 + ((x)*32)) //MSM device/func number and register offset from SPR register guide. constexpr auto SPR_MSM_DEV_ID = 0x09a6; constexpr auto SPR_MSM_DEV_ADDR = 0x03; constexpr auto SPR_MSM_FUNC_ADDR = 0x00; constexpr auto SPR_MSM_REG_CPUBUSNO_VALID_OFFSET = 0x1a0; constexpr auto SPR_MSM_REG_CPUBUSNO0_OFFSET = 0x190; constexpr auto SPR_MSM_REG_CPUBUSNO4_OFFSET = 0x1c0; constexpr auto SPR_MSM_CPUBUSNO_MAX = 32; //SAD register offset from SPR register guide. constexpr auto SPR_SAD_REG_CTL_CFG_OFFSET = 0x3F4; } // namespace pcm #endif pcm-202307/src/uncore_pmu_discovery.cpp000066400000000000000000000143301445420033100201230ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2021-2022, Intel Corporation #include "uncore_pmu_discovery.h" #include "pci.h" #include "mmio.h" #include "iostream" #include "utils.h" namespace pcm { constexpr auto UNCORE_DISCOVERY_MAP_SIZE = 0x80000; UncorePMUDiscovery::UncorePMUDiscovery() { if (safe_getenv("PCM_NO_UNCORE_PMU_DISCOVERY") == std::string("1")) { return; } unsigned socket = 0; auto processTables = [&socket,this](const uint64 bar) { constexpr size_t UncoreDiscoverySize = 3UL; union UncoreGlobalDiscovery { GlobalPMU pmu; uint64 table[UncoreDiscoverySize]; }; MMIORange range(bar, UNCORE_DISCOVERY_MAP_SIZE); // mmio range with UNCORE_DISCOVERY_MAP_SIZE bytes UncoreGlobalDiscovery global; auto copyTable = [&range,&UncoreDiscoverySize,&bar](uint64 * table, const size_t offset) { for (size_t i = 0; i < UncoreDiscoverySize; ++i) { const auto pos = offset + i * sizeof(uint64); assert(pos < UNCORE_DISCOVERY_MAP_SIZE); table[i] = range.read64(pos); if (table[i] == ~0ULL) { std::cerr << "Failed to read memory at 0x" << std::hex << bar << " + 0x" << pos << std::dec << "\n"; throw std::exception(); } } }; copyTable(global.table, 0); globalPMUs.push_back(global.pmu); union UncoreUnitDiscovery { BoxPMU pmu; uint64 table[UncoreDiscoverySize]; }; UncoreUnitDiscovery unit; const auto step = global.pmu.stride * 8; BoxPMUMap boxPMUMap; for (size_t u = 0; u < global.pmu.maxUnits; ++u) { copyTable(unit.table, (u+1) * step); if (unit.table[0] == 0 && unit.table[1] == 0) { // invalid entry continue; } // unit.pmu.print(); boxPMUMap[unit.pmu.boxType].push_back(unit.pmu); } boxPMUs.push_back(boxPMUMap); ++socket; }; try { forAllIntelDevices( [&processTables](const uint32 group, const uint32 bus, const uint32 device, const uint32 function, const uint32 /* device_id */) { uint32 status{0}; PciHandleType h(group, bus, device, function); h.read32(6, &status); // read status if (status & 0x10) // has capability list { // std::cout << "Intel device scan. found "<< std::hex << group << ":" << bus << ":" << device << ":" << function << " " << device_id << " with capability list\n" << std::dec; union { struct { uint32 id:16; uint32 version:4; uint32 next:12; } fields; uint32 value; } header; uint64 offset = 0x100; do { if (offset == 0 || h.read32(offset, &header.value) != sizeof(uint32) || header.value == 0) { return; } // std::cout << "offset " << offset << "\n"; if (header.fields.id == 0x23) // UNCORE_EXT_CAP_ID_DISCOVERY { // std::cout << "found UNCORE_EXT_CAP_ID_DISCOVERY\n"; uint32 entryID = 0; constexpr auto UNCORE_DISCOVERY_DVSEC_OFFSET = 8; if (h.read32(offset + UNCORE_DISCOVERY_DVSEC_OFFSET, &entryID) == sizeof(uint32)) // read at UNCORE_DISCOVERY_DVSEC_OFFSET { entryID &= 0xffff; // apply UNCORE_DISCOVERY_DVSEC_ID_MASK if (entryID == 1) // UNCORE_DISCOVERY_DVSEC_ID_PMON { // std::cout << "found UNCORE_DISCOVERY_DVSEC_ID_PMON\n"; uint32 bir = 0; if (h.read32(offset + UNCORE_DISCOVERY_DVSEC_OFFSET + 4, &bir) == sizeof(uint32)) // read "bir" value (2:0) { bir &= 7; auto barOffset = 0x10 + bir * 4; uint32 bar = 0; if (h.read32(barOffset, &bar) == sizeof(uint32) && bar != 0) // read bar { bar &= ~4095; processTables(bar); return; } else { std::cerr << "Error: can't read bar from offset " << barOffset << " \n"; } } else { std::cerr << "Error: can't read bir\n"; } } } } offset = header.fields.next & ~3; } while (1); } }); } catch (...) { std::cerr << "WARNING: enumeration of devices in UncorePMUDiscovery failed\n"; } if (safe_getenv("PCM_PRINT_UNCORE_PMU_DISCOVERY") == std::string("1")) { for (size_t s = 0; s < boxPMUs.size(); ++s) { std::cout << "Socket " << s << " global PMU:\n"; std::cout << " "; globalPMUs[s].print(); std::cout << "Socket " << s << " unit PMUs:\n"; for (const auto & pmuType : boxPMUs[s]) { const auto n = pmuType.second.size(); std::cout << " PMU type " << pmuType.first << " (" << n << " boxes)"<< "\n"; for (size_t i = 0; i < n ; ++i) { std::cout << " "; pmuType.second[i].print(); } } } } } } // namespace pcm pcm-202307/src/uncore_pmu_discovery.h000066400000000000000000000123031445420033100175660ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2021-2022, Intel Corporation #pragma once #include #include #include #include "types.h" namespace pcm { constexpr auto SPR_PCU_BOX_TYPE = 4U; constexpr auto SPR_MDF_BOX_TYPE = 11U; constexpr auto SPR_CXLCM_BOX_TYPE = 12U; constexpr auto SPR_CXLDP_BOX_TYPE = 13U; class UncorePMUDiscovery { public: enum accessTypeEnum { MSR = 0, MMIO = 1, PCICFG = 2, unknownAccessType = 255 }; static const char * accessTypeStr(const uint64 t) { switch (t) { case MSR: return "MSR"; case MMIO: return "MMIO"; case PCICFG: return "PCICFG"; } return "unknown"; }; protected: struct GlobalPMU { uint64 type:8; uint64 stride:8; uint64 maxUnits:10; uint64 __reserved1:36; uint64 accessType:2; uint64 globalCtrlAddr; uint64 statusOffset:8; uint64 numStatus:16; uint64 __reserved2:40; void print() const { std::cout << "global PMU " << " of type " << std::dec << type << " globalCtrl: 0x" << std::hex << globalCtrlAddr << " with access type " << std::dec << accessTypeStr(accessType) << " stride: " << std::dec << stride << "\n"; } }; struct BoxPMU { uint64 numRegs : 8; uint64 ctrlOffset : 8; uint64 bitWidth : 8; uint64 ctrOffset : 8; uint64 statusOffset : 8; uint64 __reserved1 : 22; uint64 accessType : 2; uint64 boxCtrlAddr; uint64 boxType : 16; uint64 boxID : 16; uint64 __reserved2 : 32; void print() const { std::cout << "unit PMU " << " of type " << std::dec << boxType << " ID " << boxID << " box ctrl: 0x" << std::hex << boxCtrlAddr << " width " << std::dec << bitWidth << " with access type " << accessTypeStr(accessType) << " numRegs " << numRegs << " ctrlOffset " << ctrlOffset << " ctrOffset " << ctrOffset << "\n"; } }; typedef std::vector BoxPMUs; typedef std::unordered_map BoxPMUMap; // boxType -> BoxPMUs std::vector boxPMUs; std::vector globalPMUs; bool validBox(const size_t boxType, const size_t socket, const size_t pos) { return socket < boxPMUs.size() && pos < boxPMUs[socket][boxType].size(); } size_t registerStep(const size_t boxType, const size_t socket, const size_t pos) { const auto width = boxPMUs[socket][boxType][pos].bitWidth; switch (boxPMUs[socket][boxType][pos].accessType) { case MSR: if (width <= 64) { return 1; } break; case PCICFG: case MMIO: if (width <= 8) { return 1; } else if (width <= 16) { return 2; } else if (width <= 32) { return 4; } else if (width <= 64) { return 8; } break; } return 0; } public: UncorePMUDiscovery(); size_t getNumBoxes(const size_t boxType, const size_t socket) { if (socket < boxPMUs.size()) { return boxPMUs[socket][boxType].size(); } return 0; } uint64 getBoxCtlAddr(const size_t boxType, const size_t socket, const size_t pos) { if (validBox(boxType, socket, pos)) { return boxPMUs[socket][boxType][pos].boxCtrlAddr; } return 0; } uint64 getBoxCtlAddr(const size_t boxType, const size_t socket, const size_t pos, const size_t c) { if (validBox(boxType, socket, pos) && c < boxPMUs[socket][boxType][pos].numRegs) { return boxPMUs[socket][boxType][pos].boxCtrlAddr + boxPMUs[socket][boxType][pos].ctrlOffset + c * registerStep(boxType, socket, pos); } return 0; } uint64 getBoxCtrAddr(const size_t boxType, const size_t socket, const size_t pos, const size_t c) { if (validBox(boxType, socket, pos) && c < boxPMUs[socket][boxType][pos].numRegs) { return boxPMUs[socket][boxType][pos].boxCtrlAddr + boxPMUs[socket][boxType][pos].ctrOffset + c * registerStep(boxType, socket, pos); } return 0; } accessTypeEnum getBoxAccessType(const size_t boxType, const size_t socket, const size_t pos) { if (validBox(boxType, socket, pos)) { return static_cast(boxPMUs[socket][boxType][pos].accessType); } return unknownAccessType; } uint64 getBoxNumRegs(const size_t boxType, const size_t socket, const size_t pos) { if (validBox(boxType, socket, pos)) { return boxPMUs[socket][boxType][pos].numRegs; } return 0; } }; } // namespace pcm pcm-202307/src/utils.cpp000066400000000000000000001137541445420033100150320ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Andrey Semin and many others #include #include #include #include #ifdef _MSC_VER #include #include #else #include // for waitpid() #include // for ::sleep #endif #include "utils.h" #include "cpucounters.h" #include #ifndef _MSC_VER #include #endif namespace pcm { void (*post_cleanup_callback)(void) = NULL; //! \brief handler of exit() call void exit_cleanup(void) { std::cout << std::flush; restore_signal_handlers(); // this replaces same call in cleanup() from util.h if (PCM::isInitialized()) PCM::getInstance()->cleanup(); // this replaces same call in cleanup() from util.h //TODO: delete other shared objects.... if any. if(post_cleanup_callback != NULL) { post_cleanup_callback(); } } void print_cpu_details() { const auto m = PCM::getInstance(); std::cerr << "\nDetected " << m->getCPUBrandString() << " \"Intel(r) microarchitecture codename " << m->getUArchCodename() << "\" stepping " << m->getCPUStepping(); const auto ucode_level = m->getCPUMicrocodeLevel(); if (ucode_level >= 0) { std::cerr << " microcode level 0x" << std::hex << ucode_level << std::dec; } std::cerr << "\n"; } #ifdef _MSC_VER ThreadGroupTempAffinity::ThreadGroupTempAffinity(uint32 core_id, bool checkStatus, const bool restore_) : restore(restore_) { GROUP_AFFINITY NewGroupAffinity; SecureZeroMemory(&NewGroupAffinity, sizeof(GROUP_AFFINITY)); SecureZeroMemory(&PreviousGroupAffinity, sizeof(GROUP_AFFINITY)); DWORD currentGroupSize = 0; while ((DWORD)core_id >= (currentGroupSize = GetActiveProcessorCount(NewGroupAffinity.Group))) { if (currentGroupSize == 0) { std::cerr << "ERROR: GetActiveProcessorCount for core " << core_id << " failed with error " << GetLastError() << "\n"; throw std::exception(); } core_id -= (uint32)currentGroupSize; ++NewGroupAffinity.Group; } NewGroupAffinity.Mask = 1ULL << core_id; if (GetThreadGroupAffinity(GetCurrentThread(), &PreviousGroupAffinity) && (std::memcmp(&NewGroupAffinity, &PreviousGroupAffinity, sizeof(GROUP_AFFINITY)) == 0)) { restore = false; return; } const auto res = SetThreadGroupAffinity(GetCurrentThread(), &NewGroupAffinity, &PreviousGroupAffinity); if (res == FALSE && checkStatus) { std::cerr << "ERROR: SetThreadGroupAffinity for core " << core_id << " failed with error " << GetLastError() << "\n"; throw std::exception(); } } ThreadGroupTempAffinity::~ThreadGroupTempAffinity() { if (restore) SetThreadGroupAffinity(GetCurrentThread(), &PreviousGroupAffinity, NULL); } LONG unhandled_exception_handler(LPEXCEPTION_POINTERS p) { std::cerr << "DEBUG: Unhandled Exception event\n"; exit(EXIT_FAILURE); } /** * \brief version of interrupt handled for Windows */ BOOL sigINT_handler(DWORD fdwCtrlType) { // output for DEBUG only std::cerr << "DEBUG: caught signal to interrupt: "; switch (fdwCtrlType) { // Handle the CTRL-C signal. case CTRL_C_EVENT: std::cerr << "Ctrl-C event\n"; break; // CTRL-CLOSE: confirm that the user wants to exit. case CTRL_CLOSE_EVENT: std::cerr << "Ctrl-Close event\n"; break; // Pass other signals to the next handler. case CTRL_BREAK_EVENT: std::cerr << "Ctrl-Break event\n"; break; case CTRL_LOGOFF_EVENT: std::cerr << "Ctrl-Logoff event\n"; break; case CTRL_SHUTDOWN_EVENT: std::cerr << "Ctrl-Shutdown event\n"; break; default: std::cerr << "Unknown event\n"; break; } // TODO: dump summary, if needed // in case PCM is blocked just return and summary will be dumped in // calling function, if needed if (PCM::isInitialized() && PCM::getInstance()->isBlocked()) { return FALSE; } else { exit_cleanup(); _exit(EXIT_SUCCESS); return FALSE; // to prevent Warning } } /** * \brief started in a separate thread and blocks waiting for child application to exit. * After child app exits: -> print Child's termination status and terminates PCM */ void waitForChild(void * proc_id) { intptr_t procHandle = (intptr_t)proc_id; int termstat; _cwait(&termstat, procHandle, _WAIT_CHILD); std::cerr << "Program exited with status " << termstat << "\n"; exit(EXIT_SUCCESS); } #else /** * \brief handles signals that lead to termination of the program * such as SIGINT, SIGQUIT, SIGABRT, SIGSEGV, SIGTERM, SIGCHLD * this function specifically works when the client application launched * by pcm -- terminates */ void sigINT_handler(int signum) { // output for DEBUG only std::cerr << "DEBUG: caught signal to interrupt (" << strsignal(signum) << ").\n"; // TODO: dump summary, if needed // in case PCM is blocked just return and summary will be dumped in // calling function, if needed if (PCM::isInitialized() && PCM::getInstance()->isBlocked()) { return; } else { exit_cleanup(); if (signum == SIGABRT || signum == SIGSEGV) { _exit(EXIT_FAILURE); } else { _exit(EXIT_SUCCESS); } } } /** * \brief handles SIGSEGV signals that lead to termination of the program * this function specifically works when the client application launched * by pcm -- terminates */ constexpr auto BACKTRACE_MAX_STACK_FRAME = 30; void sigSEGV_handler(int signum) { void *backtrace_buffer[BACKTRACE_MAX_STACK_FRAME] = {0}; char **backtrace_strings = NULL; size_t backtrace_size = 0; backtrace_size = backtrace(backtrace_buffer, BACKTRACE_MAX_STACK_FRAME); backtrace_strings = backtrace_symbols(backtrace_buffer, backtrace_size); if (backtrace_strings == NULL) { std::cerr << "Debug: backtrace empty. \n"; } else { std::cerr << "Debug: backtrace dump(" << backtrace_size << " stack frames).\n"; for (size_t i = 0; i < backtrace_size; i++) { std::cerr << backtrace_strings[i] << "\n"; } free(backtrace_strings); backtrace_strings = NULL; } sigINT_handler(signum); } /** * \brief handles signals that lead to restart the application * such as SIGHUP. * for example to re-read environment variables controlling PCM execution */ void sigHUP_handler(int /*signum*/) { // output for DEBUG only std::cerr << "DEBUG: caught signal to hangup. Reloading configuration and continue...\n"; // TODO: restart; so far do nothing return; // continue program execution } /** * \brief handles signals that lead to update of configuration * such as SIGUSR1 and SIGUSR2. * for the future extensions */ void sigUSR_handler(int /*signum*/) { std::cerr << "DEBUG: caught USR signal. Continue.\n"; // TODO: reload configurationa, reset accumulative counters; return; } /** * \brief handles signals that lead to update of configuration * such as SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU */ void sigSTOP_handler(int /*signum*/) { PCM * m = PCM::getInstance(); int runState = m->getRunState(); std::string state = (runState == 1 ? "suspend" : "continue"); std::cerr << "DEBUG: caught signal to " << state << " execution.\n"; // debug of signals only if (runState == 1) { // stop counters and sleep... almost forever; m->setRunState(0); sleep(INT_MAX); } else { // resume m->setRunState(1); alarm(1); } return; } /** * \brief handles signals that lead to update of configuration * such as SIGCONT */ void sigCONT_handler(int /*signum*/) { std::cout << "DEBUG: caught signal to continue execution.\n"; // debug of signals only // TODO: clear counters, resume counting. return; } #endif // ifdef _MSC_VER //! \brief install various handlers for system signals void set_signal_handlers(void) { if (atexit(exit_cleanup) != 0) { std::cerr << "ERROR: Failed to install exit handler.\n"; return; } #ifdef _MSC_VER BOOL handlerStatus; // Increase the priority a bit to improve context switching delays on Windows SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); // to fix Cygwin/BASH setting Ctrl+C handler need first to restore the default one handlerStatus = SetConsoleCtrlHandler(NULL, FALSE); // restores normal processing of CTRL+C input if (handlerStatus == 0) { tcerr << "Failed to set Ctrl+C handler. Error code: " << GetLastError() << " "; const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage(); if (errorStr) tcerr << errorStr; tcerr << "\n"; _exit(EXIT_FAILURE); } handlerStatus = SetConsoleCtrlHandler((PHANDLER_ROUTINE)sigINT_handler, TRUE); if (handlerStatus == 0) { tcerr << "Failed to set Ctrl+C handler. Error code: " << GetLastError() << " "; const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage(); if (errorStr) tcerr << errorStr; tcerr << "\n"; _exit(EXIT_FAILURE); } SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&unhandled_exception_handler); char *envPath; if (_dupenv_s(&envPath, NULL, "_")) { std::cerr << "\nPCM ERROR: _dupenv_s failed.\n"; _exit(EXIT_FAILURE); } free(envPath); if (envPath) { std::cerr << "\nPCM ERROR: Detected cygwin/mingw environment which does not allow to setup PMU clean-up handlers on Ctrl-C and other termination signals.\n"; std::cerr << "See https://www.mail-archive.com/cygwin@cygwin.com/msg74817.html\n"; std::cerr << "As a workaround please run pcm directly from a native windows shell (e.g. cmd).\n"; std::cerr << "Exiting...\n\n"; _exit(EXIT_FAILURE); } std::cerr << "DEBUG: Setting Ctrl+C done.\n"; #else struct sigaction saINT, saHUP, saUSR, saSTOP, saCONT; // install handlers that interrupt execution saINT.sa_handler = sigINT_handler; sigemptyset(&saINT.sa_mask); saINT.sa_flags = SA_RESTART; sigaction(SIGINT, &saINT, NULL); sigaction(SIGQUIT, &saINT, NULL); sigaction(SIGABRT, &saINT, NULL); sigaction(SIGTERM, &saINT, NULL); saINT.sa_flags = SA_RESTART | SA_NOCLDSTOP; sigaction(SIGCHLD, &saINT, NULL); // get there is our child exits. do nothing if it stopped/continued saINT.sa_handler = sigSEGV_handler; sigemptyset(&saINT.sa_mask); saINT.sa_flags = SA_RESTART; sigaction(SIGSEGV, &saINT, NULL); // install SIGHUP handler to restart saHUP.sa_handler = sigHUP_handler; sigemptyset(&saHUP.sa_mask); saHUP.sa_flags = SA_RESTART; sigaction(SIGHUP, &saHUP, NULL); // install SIGHUP handler to restart saUSR.sa_handler = sigUSR_handler; sigemptyset(&saUSR.sa_mask); saUSR.sa_flags = SA_RESTART; sigaction(SIGUSR1, &saUSR, NULL); sigaction(SIGUSR2, &saUSR, NULL); // install SIGSTOP handler: pause/resume saSTOP.sa_handler = sigSTOP_handler; sigemptyset(&saSTOP.sa_mask); saSTOP.sa_flags = SA_RESTART; sigaction(SIGSTOP, &saSTOP, NULL); sigaction(SIGTSTP, &saSTOP, NULL); sigaction(SIGTTIN, &saSTOP, NULL); sigaction(SIGTTOU, &saSTOP, NULL); // install SIGCONT & SIGALRM handler saCONT.sa_handler = sigCONT_handler; sigemptyset(&saCONT.sa_mask); saCONT.sa_flags = SA_RESTART; sigaction(SIGCONT, &saCONT, NULL); sigaction(SIGALRM, &saCONT, NULL); #endif return; } //! \brief Restores default signal handlers under Linux/UNIX void restore_signal_handlers(void) { #ifndef _MSC_VER struct sigaction action; action.sa_handler = SIG_DFL; action.sa_flags = 0; sigemptyset(&action.sa_mask); sigaction(SIGINT, &action, NULL); sigaction(SIGQUIT, &action, NULL); sigaction(SIGABRT, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGSEGV, &action, NULL); sigaction(SIGCHLD, &action, NULL); // restore SIGHUP handler to restart sigaction(SIGHUP, &action, NULL); // restore SIGHUP handler to restart sigaction(SIGUSR1, &action, NULL); sigaction(SIGUSR2, &action, NULL); // restore SIGSTOP handler: pause/resume // sigaction(SIGSTOP, &action, NULL); // cannot catch this // handle SUSP character: normally C-z) sigaction(SIGTSTP, &action, NULL); sigaction(SIGTTIN, &action, NULL); sigaction(SIGTTOU, &action, NULL); // restore SIGCONT & SIGALRM handler sigaction(SIGCONT, &action, NULL); sigaction(SIGALRM, &action, NULL); #endif return; } void set_real_time_priority(const bool & silent) { if (!silent) { std::cerr << "Setting real time priority for the process\n"; } #ifdef _MSC_VER if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) { std::cerr << "ERROR: SetPriorityClass with REALTIME_PRIORITY_CLASS failed with error " << GetLastError() << "\n"; } if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)) { std::cerr << "ERROR: SetThreadPriority with THREAD_PRIORITY_TIME_CRITICAL failed with error " << GetLastError() << "\n"; } #elif __linux__ const auto priority = sched_get_priority_max(SCHED_RR); if (priority == -1) { std::cerr << "ERROR: Could not get SCHED_RR max priority: " << strerror(errno) << "\n"; } else { struct sched_param sp = { .sched_priority = priority }; if (sched_setscheduler(0, SCHED_RR, &sp) == -1) { const auto errnosave = errno; std::cerr << "ERROR: Could not set scheduler to realtime! Errno: " << errnosave << " Error message: \"" << strerror(errnosave) << "\"\n"; } else { if (!silent) { std::cerr << "Scheduler changed to SCHED_RR and priority to " << priority << "\n"; } } } #else std::cerr << "Setting real time priority for the process not implemented on your OS.\n"; #endif } void set_post_cleanup_callback(void(*cb)(void)) { post_cleanup_callback = cb; } //!\brief launches external program in a separate process void MySystem(char * sysCmd, char ** sysArgv) { if (sysCmd == NULL) { assert("No program provided. NULL pointer"); exit(EXIT_FAILURE); } std::cerr << "\nExecuting \""; std::cerr << sysCmd; std::cerr << "\" command:\n"; #ifdef _MSC_VER intptr_t ret; char cbuf[128]; if (PCM::getInstance()->isBlocked()) { // synchronous start: wait for child process completion // in case PCM should be blocked waiting for the child application to end // 1. returns and ret = -1 in case of error creating process is encountered // 2. ret = _spawnvp(_P_WAIT, sysCmd, sysArgv); if (ret == -1) { // process creation failed. strerror_s(cbuf, 128, errno); std::cerr << "Failed to start program \"" << sysCmd << "\". " << cbuf << "\n"; exit(EXIT_FAILURE); } else { // process created, worked, and completed with exist code in ret. ret=0 -> Success std::cerr << "Program exited with status " << ret << "\n"; } } else { // async start: PCM works in parallel with the child process, and exits when ret = _spawnvp(_P_NOWAIT, sysCmd, sysArgv); if (ret == -1) { strerror_s(cbuf, 128, errno); std::cerr << "Failed to start program \"" << sysCmd << "\". " << cbuf << "\n"; exit(EXIT_FAILURE); } else { // ret here is the new process handle. // start new thread which will wait for child completion, and continue PCM's execution if (_beginthread(waitForChild, 0, (void *)ret) == -1L) { strerror_s(cbuf, 128, errno); std::cerr << "WARNING: Failed to set waitForChild. PCM will continue infinitely: finish it manually! " << cbuf << "\n"; } } } #else pid_t child_pid = fork(); if (child_pid == 0) { execvp(sysCmd, sysArgv); std::cerr << "Failed to start program \"" << sysCmd << "\"\n"; exit(EXIT_FAILURE); } else { if (PCM::getInstance()->isBlocked()) { int res; waitpid(child_pid, &res, 0); std::cerr << "Program " << sysCmd << " launched with PID: " << std::dec << child_pid << "\n"; if (WIFEXITED(res)) { std::cerr << "Program exited with status " << WEXITSTATUS(res) << "\n"; } else if (WIFSIGNALED(res)) { std::cerr << "Process " << child_pid << " was terminated with status " << WTERMSIG(res) << "\n"; } } } #endif } #ifdef _MSC_VER #define HORIZONTAL char(196) #define VERTICAL char(179) #define DOWN_AND_RIGHT char(218) #define DOWN_AND_LEFT char(191) #define UP_AND_RIGHT char(192) #define UP_AND_LEFT char(217) #else #define HORIZONTAL u8"\u2500" #define VERTICAL u8"\u2502" #define DOWN_AND_RIGHT u8"\u250C" #define DOWN_AND_LEFT u8"\u2510" #define UP_AND_RIGHT u8"\u2514" #define UP_AND_LEFT u8"\u2518" #endif template void drawBar(const int nempty, const T & first, const int width, const T & last) { for (int c = 0; c < nempty; ++c) { std::cout << ' '; } std::cout << first; for (int c = 0; c < width; ++c) { std::cout << HORIZONTAL; } std::cout << last << '\n'; } void drawStackedBar(const std::string & label, std::vector & h, const int width) { int real_width = 0; auto scale = [&width](double fraction) { return int(round(fraction * double(width))); }; for (const auto & i : h) { real_width += scale(i.fraction); } if (real_width > 2*width) { std::cout << "ERROR: sum of fractions > 2 ("<< real_width << " > " << width << ")\n"; return; } drawBar((int)label.length(), DOWN_AND_RIGHT, real_width, DOWN_AND_LEFT); std::cout << label << VERTICAL; for (const auto & i : h) { const int c_width = scale(i.fraction); for (int c = 0; c < c_width; ++c) { std::cout << i.fill; } } std::cout << VERTICAL << "\n"; drawBar((int)label.length(), UP_AND_RIGHT, real_width, UP_AND_LEFT); } bool CheckAndForceRTMAbortMode(const char * arg, PCM * m) { if (check_argument_equals(arg, {"-force-rtm-abort-mode"})) { if (nullptr == m) { m = PCM::getInstance(); assert(m); } m->enableForceRTMAbortMode(); return true; } return false; } std::vector split(const std::string & str, const char delim) { std::string token; std::vector result; std::istringstream strstr(str); while (std::getline(strstr, token, delim)) { result.push_back(token); } return result; } uint64 read_number(const char* str) { std::istringstream stream(str); if (strstr(str, "x")) stream >> std::hex; uint64 result = 0; stream >> result; return result; } // emulates scanf %i for hex 0x prefix otherwise assumes dec (no oct support) bool match(const std::string& subtoken, const std::string& sname, uint64* result) { if (pcm_sscanf(subtoken) >> s_expect(sname + "0x") >> std::hex >> *result) return true; if (pcm_sscanf(subtoken) >> s_expect(sname) >> std::dec >> *result) return true; return false; } #define PCM_CALIBRATION_INTERVAL 50 // calibrate clock only every 50th iteration int calibratedSleep(const double delay, const char* sysCmd, const MainLoop& mainLoop, PCM* m) { static uint64 TimeAfterSleep = 0; int delay_ms = int(delay * 1000); if (TimeAfterSleep) delay_ms -= (int)(m->getTickCount() - TimeAfterSleep); if (delay_ms < 0) delay_ms = 0; if (sysCmd == NULL || mainLoop.getNumberOfIterations() != 0 || m->isBlocked() == false) { if (delay_ms > 0) { // std::cerr << "DEBUG: sleeping for " << std::dec << delay_ms << " ms...\n"; MySleepMs(delay_ms); } } TimeAfterSleep = m->getTickCount(); return delay_ms; }; void print_help_force_rtm_abort_mode(const int alignment, const char * separator) { const auto m = PCM::getInstance(); if (m->isForceRTMAbortModeAvailable() && (m->getMaxCustomCoreEvents() < 4)) { std::cout << " -force-rtm-abort-mode"; for (int i = 0; i < (alignment - 23); ++i) { std::cout << " "; } assert(separator); std::cout << separator << " force RTM transaction abort mode to enable more programmable counters\n"; } } #ifdef _MSC_VER std::string safe_getenv(const char* env) { char * buffer; std::string result; if (_dupenv_s(&buffer, NULL, env) == 0 && buffer != nullptr) { result = buffer; free(buffer); } return result; } #else std::string safe_getenv(const char* env) { const auto getenvResult = std::getenv(env); return getenvResult ? std::string(getenvResult) : std::string(""); } #endif void print_pid_collection_message(int pid) { if (pid != -1) { std::cerr << "Collecting core metrics for process ID " << std::dec << pid << "\n"; } } double parse_delay(const char *arg, const std::string& progname, print_usage_func print_usage_func) { // any other options positional that is a floating point number is treated as , // while the other options are ignored with a warning issues to stderr double delay_input = 0.0; std::istringstream is_str_stream(arg); is_str_stream >> std::noskipws >> delay_input; if(is_str_stream.eof() && !is_str_stream.fail()) { if (delay_input < 0) { std::cerr << "Invalid delay specified: \"" << *arg << "\". Delay should be positive.\n"; if(print_usage_func) { print_usage_func(progname); } exit(EXIT_FAILURE); } return delay_input; } else { std::cerr << "WARNING: unknown command-line option: \"" << *arg << "\". Ignoring it.\n"; if(print_usage_func) { print_usage_func(progname); } exit(EXIT_FAILURE); } } bool extract_argument_value(const char* arg, std::initializer_list arg_names, std::string& value) { const auto arg_len = strlen(arg); for (const auto& arg_name: arg_names) { const auto arg_name_len = strlen(arg_name); if (arg_len > arg_name_len && strncmp(arg, arg_name, arg_name_len) == 0 && arg[arg_name_len] == '=') { value = arg + arg_name_len + 1; const auto last_pos = value.find_last_not_of("\""); if (last_pos != std::string::npos) { value.erase(last_pos + 1); } const auto first_pos = value.find_first_not_of("\""); if (first_pos != std::string::npos) { value.erase(0, first_pos); } return true; } } return false; } bool check_argument_equals(const char* arg, std::initializer_list arg_names) { const auto arg_len = strlen(arg); for (const auto& arg_name: arg_names) { if (arg_len == strlen(arg_name) && strncmp(arg, arg_name, arg_len) == 0) { return true; } } return false; } void check_and_set_silent(int argc, char * argv[], null_stream &nullStream2) { if (argc > 1) do { argv++; argc--; if (check_argument_equals(*argv, {"--help", "-h", "/h"}) || check_argument_equals(*argv, {"-silent", "/silent"})) { std::cerr.rdbuf(&nullStream2); return; } } while (argc > 1); } bool check_for_injections(const std::string & str) { const std::array symbols = {'=', '+', '-', '@'}; if (std::find(std::begin(symbols), std::end(symbols), str[0]) != std::end(symbols)) { std::cerr << "ERROR: First letter in event name: " << str << " cannot be \"" << str[0] << "\" , please use escape \"\\\" or remove it\n"; return true; } return false; } void print_enforce_flush_option_help() { std::cout << " -f | /f => enforce flushing output\n"; } bool print_version(int argc, char * argv[]) { if (argc > 1) do { argv++; argc--; if (check_argument_equals(*argv, {"--version"})) { std::cout << "version: " << PCM_VERSION << "\n"; return true; } } while (argc > 1); return false; } std::string dos2unix(std::string in) { if (in.length() > 0 && int(in[in.length() - 1]) == 13) { in.erase(in.length() - 1); } return in; } std::string a_title(const std::string &init, const std::string &name) { char begin = init[0]; std::string row = init; row += name; return row + begin; } std::string a_data (std::string init, struct data d) { char begin = init[0]; std::string row = init; std::string str_d = unit_format(d.value); row += str_d; if (str_d.size() > d.width) throw std::length_error("counter value > event_name length"); row += std::string(d.width - str_d.size(), ' '); return row + begin; } std::string build_line(std::string init, std::string name, bool last_char = true, char this_char = '_') { char begin = init[0]; std::string row = init; row += std::string(name.size(), this_char); if (last_char == true) row += begin; return row; } std::string a_header_footer (std::string init, std::string name) { return build_line(init, name); } std::string build_csv_row(const std::vector& chunks, const std::string& delimiter) { return std::accumulate(chunks.begin(), chunks.end(), std::string(""), [delimiter](const std::string &left, const std::string &right){ return left.empty() ? right : left + delimiter + right; }); } std::vector prepare_data(const std::vector &values, const std::vector &headers) { std::vector v; uint32_t idx = 0; for (std::vector::const_iterator iunit = std::next(headers.begin()); iunit != headers.end() && idx < values.size(); ++iunit, idx++) { struct data d; d.width = (uint32_t)iunit->size(); d.value = values[idx]; v.push_back(d); } return v; } void display(const std::vector &buff, std::ostream& stream) { for (std::vector::const_iterator iunit = buff.begin(); iunit != buff.end(); ++iunit) stream << *iunit << "\n"; stream << std::flush; } void print_nameMap(std::map>>& nameMap) { for (std::map>>::const_iterator iunit = nameMap.begin(); iunit != nameMap.end(); ++iunit) { std::string h_name = iunit->first; std::pair> value = iunit->second; uint32_t hid = value.first; std::map vMap = value.second; std::cout << "H name: " << h_name << " id =" << hid << " vMap size:" << vMap.size() << "\n"; for (std::map::const_iterator junit = vMap.begin(); junit != vMap.end(); ++junit) { std::string v_name = junit->first; uint32_t vid = junit->second; std::cout << "V name: " << v_name << " id =" << vid << "\n"; } } } //! \brief load_events: parse the evt config file. //! \param fn:event config file name. //! \param ofm: operation field map struct. //! \param pfn_evtcb: see below. //! \param evtcb_ctx: pointer of the callback context(user define). //! \param nameMap: human readable metrics names. //! \return -1 means fail, 0 means success. //! \brief pfn_evtcb: call back func of event config file processing, app should provide it. //! \param void *: pointer of the callback context. //! \param counter &: common base counter struct. //! \param std::map &: operation field map struct. //! \param std::string: event field name. //! \param uint64: event field value. //! \return -1 means fail with app exit, 0 means success or fail with continue. int load_events(const std::string &fn, std::map &ofm, int (*pfn_evtcb)(evt_cb_type, void *, counter &, std::map &, std::string, uint64), void *evtcb_ctx, std::map>> &nameMap) { struct counter ctr; std::ifstream in(fn); std::string line, item; if (!in.is_open()) { const auto alt_fn = std::string("/usr/share/pcm/") + fn; in.open(alt_fn); if (!in.is_open()) { in.close(); const auto err_msg = std::string("event config file ") + fn + " or " + alt_fn + " is not available, you can try to manually copy it from PCM source package."; throw std::invalid_argument(err_msg); } } while (std::getline(in, line)) { //TODO: substring until #, if len == 0, skip, else parse normally //Set default value if the item is NOT available in cfg file. ctr.h_event_name = "INVALID"; ctr.v_event_name = "INVALID"; ctr.ccr = 0; ctr.idx = 0; ctr.multiplier = 1; ctr.divider = 1; ctr.h_id = 0; ctr.v_id = 0; if (pfn_evtcb(EVT_LINE_START, evtcb_ctx, ctr, ofm, "", 0)) { in.close(); const auto err_msg = std::string("event line processing(start) fault.\n"); throw std::invalid_argument(err_msg); } /* Ignore anyline with # */ if (line.find("#") != std::string::npos) continue; /* If line does not have any deliminator, we ignore it as well */ if (line.find("=") == std::string::npos) continue; std::string h_name, v_name; std::istringstream iss(line); while (std::getline(iss, item, ',')) { std::string key, value; uint64 numValue; /* assume the token has the format = */ key = item.substr(0,item.find("=")); value = item.substr(item.find("=")+1); if (key.empty() || value.empty()) continue; //skip the item if the token invalid std::istringstream iss2(value); iss2 >> std::setbase(0) >> numValue; switch (ofm[key]) { case PCM::H_EVENT_NAME: h_name = dos2unix(value); ctr.h_event_name = h_name; if (nameMap.find(h_name) == nameMap.end()) { /* It's a new horizontal event name */ uint32_t next_h_id = (uint32_t)nameMap.size(); std::pair> nameMap_value(next_h_id, std::map()); nameMap[h_name] = nameMap_value; } ctr.h_id = (uint32_t)nameMap.size() - 1; //cout << "h_name:" << ctr.h_event_name << "h_id: "<< ctr.h_id << "\n"; break; case PCM::V_EVENT_NAME: { v_name = dos2unix(value); ctr.v_event_name = v_name; //XXX: If h_name comes after v_name, we'll have a problem. //XXX: It's very weird, I forgot to assign nameMap[h_name] = nameMap_value earlier (:298), but this part still works? std::map &v_nameMap = nameMap[h_name].second; if (v_nameMap.find(v_name) == v_nameMap.end()) { v_nameMap[v_name] = (unsigned int)v_nameMap.size() - 1; //cout << "v_name(" << v_name << ")="<< v_nameMap[v_name] << "\n"; } else { in.close(); const auto err_msg = std::string("Detect duplicated v_name:") + v_name + "\n"; throw std::invalid_argument(err_msg); } ctr.v_id = (uint32_t)v_nameMap.size() - 1; //cout << "h_name:" << ctr.h_event_name << ",hid=" << ctr.h_id << ",v_name:" << ctr.v_event_name << ",v_id: "<< ctr.v_id << "\n"; break; } //TODO: double type for multiplier. drop divider variable case PCM::MULTIPLIER: ctr.multiplier = (int)numValue; break; case PCM::DIVIDER: ctr.divider = (int)numValue; break; case PCM::COUNTER_INDEX: ctr.idx = (int)numValue; break; default: if (pfn_evtcb(EVT_LINE_FIELD, evtcb_ctx, ctr, ofm, key, numValue)) { in.close(); const auto err_msg = std::string("event line processing(field) fault.\n"); throw std::invalid_argument(err_msg); } break; } } //std::cout << "Finish parsing: " << line << "\n"; if (pfn_evtcb(EVT_LINE_COMPLETE, evtcb_ctx, ctr, ofm, "", 0)) { in.close(); const auto err_msg = std::string("event line processing(end) fault.\n"); throw std::invalid_argument(err_msg); } } //print_nameMap(nameMap); //DEBUG purpose in.close(); return 0; } int load_events(const std::string &fn, std::map &ofm, int (*pfn_evtcb)(evt_cb_type, void *, counter &, std::map &, std::string, uint64), void *evtcb_ctx) { std::map>> nm; return load_events(fn, ofm, pfn_evtcb, evtcb_ctx, nm); } bool get_cpu_bus(uint32 msmDomain, uint32 msmBus, uint32 msmDev, uint32 msmFunc, uint32 &cpuBusValid, std::vector &cpuBusNo, int &cpuPackageId) { int cpuBusNo0 = 0x0; uint32 sadControlCfg = 0x0; uint32 busNo = 0x0; //std::cout << "get_cpu_bus: d=" << std::hex << msmDomain << ",b=" << msmBus << ",d=" << msmDev << ",f=" << msmFunc << std::dec << " \n"; PciHandleType h(msmDomain, msmBus, msmDev, msmFunc); h.read32(SPR_MSM_REG_CPUBUSNO_VALID_OFFSET, &cpuBusValid); if (cpuBusValid == (std::numeric_limits::max)()) { std::cerr << "Failed to read CPUBUSNO_VALID" << std::endl; return false; } for (int i = 0; i < 8; ++i) { busNo = 0x00; if (i <= 3) { h.read32(SPR_MSM_REG_CPUBUSNO0_OFFSET + i*4, &busNo); } else { h.read32(SPR_MSM_REG_CPUBUSNO4_OFFSET + (i-4)*4, &busNo); } if (busNo == (std::numeric_limits::max)()) { std::cerr << "Failed to read CPUBUSNO" << std::endl; return false; } cpuBusNo.push_back(busNo); //std::cout << std::hex << "get_cpu_bus: busNo=0x" << busNo << std::dec << "\n"; } cpuBusNo0 = cpuBusNo[0] & 0xff; PciHandleType sad_cfg_handler(msmDomain, cpuBusNo0, 0, 0); sad_cfg_handler.read32(SPR_SAD_REG_CTL_CFG_OFFSET, &sadControlCfg); if (sadControlCfg == (std::numeric_limits::max)()) { std::cerr << "Failed to read SAD_CONTROL_CFG" << std::endl; return false; } cpuPackageId = sadControlCfg & 0xf; return true; } #ifdef __linux__ FILE * tryOpen(const char * path, const char * mode) { FILE * f = fopen(path, mode); if (!f) { f = fopen((std::string("/pcm") + path).c_str(), mode); } return f; } std::string readSysFS(const char * path, bool silent) { FILE * f = tryOpen(path, "r"); if (!f) { if (silent == false) std::cerr << "ERROR: Can not open " << path << " file.\n"; return std::string(); } char buffer[1024]; if(NULL == fgets(buffer, 1024, f)) { if (silent == false) std::cerr << "ERROR: Can not read from " << path << ".\n"; fclose(f); return std::string(); } fclose(f); return std::string(buffer); } bool writeSysFS(const char * path, const std::string & value, bool silent) { FILE * f = tryOpen(path, "w"); if (!f) { if (silent == false) std::cerr << "ERROR: Can not open " << path << " file.\n"; return false; } if (fputs(value.c_str(), f) < 0) { if (silent == false) std::cerr << "ERROR: Can not write to " << path << ".\n"; fclose(f); return false; } fclose(f); return true; } int readMaxFromSysFS(const char * path) { std::string content = readSysFS(path); const char * buffer = content.c_str(); int result = -1; pcm_sscanf(buffer) >> s_expect("0-") >> result; if(result == -1) { pcm_sscanf(buffer) >> result; } return result; } bool readMapFromSysFS(const char * path, std::unordered_map &result, bool silent) { FILE * f = tryOpen(path, "r"); if (!f) { if (silent == false) std::cerr << "ERROR: Can not open " << path << " file.\n"; return false; } char buffer[1024]; while(fgets(buffer, 1024, f) != NULL) { std::string key, value, item; uint32 numValue = 0; item = std::string(buffer); key = item.substr(0,item.find(" ")); value = item.substr(item.find(" ")+1); if (key.empty() || value.empty()) continue; //skip the item if the token invalid std::istringstream iss2(value); iss2 >> std::setbase(0) >> numValue; result.insert(std::pair(key, numValue)); //std::cerr << "readMapFromSysFS:" << key << "=" << numValue << ".\n"; } fclose(f); return true; } #endif } // namespace pcm pcm-202307/src/utils.h000066400000000000000000000410711445420033100144670ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev /*! \file utils.h \brief Some common utility routines */ #pragma once #include #include #include #include #include "types.h" #include #include #include #include #ifndef _MSC_VER #include #include #include #else #include #endif #include #include #define PCM_MAIN_NOTHROW \ int mainThrows(int argc, char * argv[]); \ int main(int argc, char * argv[]) \ { \ try { \ return mainThrows(argc, argv); \ } catch(const std::runtime_error & e) \ { \ std::cerr << "PCM ERROR. Exception " << e.what() << "\n"; \ } catch(const std::exception & e) \ { \ std::cerr << "PCM ERROR. Exception " << e.what() << "\n"; \ } catch (...) \ { \ std::cerr << "PCM ERROR. Exception detected (no further details available).\n"; \ } \ return -1; \ } namespace pcm { #ifdef _MSC_VER using tstring = std::basic_string; #ifdef UNICODE static auto& tcerr = std::wcerr; #else static auto& tcerr = std::cerr; #endif #endif // _MSC_VER typedef void (* print_usage_func)(const std::string & progname); double parse_delay(const char * arg, const std::string & progname, print_usage_func print_usage_func); bool extract_argument_value(const char * arg, std::initializer_list arg_names, std::string & value); bool check_argument_equals(const char * arg, std::initializer_list arg_names); bool check_for_injections(const std::string & str); void exit_cleanup(void); void set_signal_handlers(void); void set_real_time_priority(const bool & silent); void restore_signal_handlers(void); #ifndef _MSC_VER void sigINT_handler(int signum); void sigHUP_handler(int signum); void sigUSR_handler(int signum); void sigSTOP_handler(int signum); void sigCONT_handler(int signum); #endif void set_post_cleanup_callback(void(*cb)(void)); inline void MySleep(int delay) { #ifdef _MSC_VER if (delay) Sleep(delay * 1000); #else ::sleep(delay); #endif } inline void MySleepMs(int delay_ms) { #ifdef _MSC_VER if (delay_ms) Sleep((DWORD)delay_ms); #else struct timespec sleep_intrval; double complete_seconds; sleep_intrval.tv_nsec = static_cast(1000000000.0 * (::modf(delay_ms / 1000.0, &complete_seconds))); sleep_intrval.tv_sec = static_cast(complete_seconds); ::nanosleep(&sleep_intrval, NULL); #endif } void MySystem(char * sysCmd, char ** argc); #ifdef _MSC_VER #pragma warning (disable : 4068 ) // disable unknown pragma warning #endif #ifdef __GCC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverloaded-virtual" #elif defined __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Woverloaded-virtual" #endif struct null_stream : public std::streambuf { int_type overflow(int_type) override { return {}; } }; #ifdef __GCC__ #pragma GCC diagnostic pop #elif defined __clang__ #pragma clang diagnostic pop #endif template inline std::string unit_format(IntType n) { char buffer[1024]; if (n <= 9999ULL) { snprintf(buffer, 1024, "%4d ", int32(n)); return std::string{buffer}; } if (n <= 9999999ULL) { snprintf(buffer, 1024, "%4d K", int32(n / 1000ULL)); return std::string{buffer}; } if (n <= 9999999999ULL) { snprintf(buffer, 1024, "%4d M", int32(n / 1000000ULL)); return std::string{buffer}; } if (n <= 9999999999999ULL) { snprintf(buffer, 1024, "%4d G", int32(n / 1000000000ULL)); return std::string{buffer}; } snprintf(buffer, 1024, "%4d T", int32(n / (1000000000ULL * 1000ULL))); return std::string{buffer}; } void print_cpu_details(); #define PCM_UNUSED(x) (void)(x) #define PCM_COMPILE_ASSERT(condition) \ typedef char pcm_compile_assert_failed[(condition) ? 1 : -1]; \ pcm_compile_assert_failed pcm_compile_assert_failed_; \ PCM_UNUSED(pcm_compile_assert_failed_); #ifdef _MSC_VER class ThreadGroupTempAffinity { GROUP_AFFINITY PreviousGroupAffinity; bool restore; ThreadGroupTempAffinity(); // forbidden ThreadGroupTempAffinity(const ThreadGroupTempAffinity &); // forbidden ThreadGroupTempAffinity & operator = (const ThreadGroupTempAffinity &); // forbidden public: ThreadGroupTempAffinity(uint32 core_id, bool checkStatus = true, const bool restore_ = false); ~ThreadGroupTempAffinity(); }; #endif class checked_uint64 // uint64 with checking for overflows when computing differences { uint64 data; uint64 overflows; public: checked_uint64() : data(0), overflows(0) {} checked_uint64(const uint64 d, const uint64 o) : data(d), overflows(o) {} const checked_uint64& operator += (const checked_uint64& o) { data += o.data; overflows += o.overflows; return *this; } uint64 operator - (const checked_uint64& o) const { // computing data - o.data constexpr uint64 counter_width = 48; return data + overflows * (1ULL << counter_width) - o.data; } uint64 getRawData_NoOverflowProtection() const { return data; } }; // a secure (but partial) alternative for sscanf // see example usage in pcm-core.cpp typedef std::istringstream pcm_sscanf; class s_expect : public std::string { public: explicit s_expect(const char * s) : std::string(s) {} explicit s_expect(const std::string & s) : std::string(s) {} friend std::istream & operator >> (std::istream & istr, s_expect && s); friend std::istream & operator >> (std::istream && istr, s_expect && s); private: void match(std::istream & istr) const { istr >> std::noskipws; const auto len = length(); char * buffer = new char[len + 2]; buffer[0] = 0; istr.get(buffer, len+1); if (*this != std::string(buffer)) { istr.setstate(std::ios_base::failbit); } delete [] buffer; } }; inline std::istream & operator >> (std::istream & istr, s_expect && s) { s.match(istr); return istr; } inline std::istream & operator >> (std::istream && istr, s_expect && s) { s.match(istr); return istr; } inline std::pair pcm_localtime() // returns { const auto durationSinceEpoch = std::chrono::system_clock::now().time_since_epoch(); const auto durationSinceEpochInSeconds = std::chrono::duration_cast(durationSinceEpoch); time_t now = durationSinceEpochInSeconds.count(); tm result; #ifdef _MSC_VER localtime_s(&result, &now); #else localtime_r(&now, &result); #endif return std::make_pair(result, std::chrono::duration_cast(durationSinceEpoch- durationSinceEpochInSeconds).count()); } enum CsvOutputType { Header1, Header2, Data, Header21, // merged headers 2 and 1 Json }; template inline void choose(const CsvOutputType outputType, H1 h1Func, H2 h2Func, D dataFunc) { switch (outputType) { case Header1: case Header21: h1Func(); break; case Header2: h2Func(); break; case Data: case Json: dataFunc(); break; default: std::cerr << "PCM internal error: wrong CSvOutputType\n"; } } inline void printDateForCSV(const CsvOutputType outputType, std::string separator = std::string(",")) { choose(outputType, [&separator]() { std::cout << separator << separator; // Time }, [&separator]() { std::cout << "Date" << separator << "Time" << separator; }, [&separator]() { std::pair tt{ pcm_localtime() }; std::cout.precision(3); char old_fill = std::cout.fill('0'); std::cout << std::setw(4) << 1900 + tt.first.tm_year << '-' << std::setw(2) << 1 + tt.first.tm_mon << '-' << std::setw(2) << tt.first.tm_mday << separator << std::setw(2) << tt.first.tm_hour << ':' << std::setw(2) << tt.first.tm_min << ':' << std::setw(2) << tt.first.tm_sec << '.' << std::setw(3) << tt.second << separator; // milliseconds std::cout.fill(old_fill); std::cout.setf(std::ios::fixed); std::cout.precision(2); }); } inline void printDateForJson(const std::string& separator, const std::string &jsonSeparator) { std::pair tt{ pcm_localtime() }; std::cout.precision(3); char old_fill = std::cout.fill('0'); std::cout << "Date" << jsonSeparator << "\"" << std::setw(4) << 1900 + tt.first.tm_year << '-' << std::setw(2) << 1 + tt.first.tm_mon << '-' << std::setw(2) << tt.first.tm_mday << "\"" << separator << "Time" << jsonSeparator << "\"" << std::setw(2) << tt.first.tm_hour << ':' << std::setw(2) << tt.first.tm_min << ':' << std::setw(2) << tt.first.tm_sec << '.' << std::setw(3) << tt.second << "\"" << separator; // milliseconds std::cout.fill(old_fill); std::cout.setf(std::ios::fixed); std::cout.precision(2); } std::vector split(const std::string & str, const char delim); class PCM; bool CheckAndForceRTMAbortMode(const char * argv, PCM * m); void print_help_force_rtm_abort_mode(const int alignment, const char * separator = "=>"); template void parseParam(int argc, char* argv[], const char* param, F f) { if (argc > 1) do { argv++; argc--; if ((std::string("-") + param == *argv) || (std::string("/") + param == *argv)) { argv++; argc--; if (argc == 0) { std::cerr << "ERROR: no parameter provided for option " << param << "\n"; exit(EXIT_FAILURE); } f(*argv); continue; } } while (argc > 1); // end of command line parsing loop } class MainLoop { unsigned numberOfIterations = 0; public: MainLoop() {} bool parseArg(const char * arg) { std::string arg_value; if (extract_argument_value(arg, {"-i", "/i"}, arg_value)) { numberOfIterations = (unsigned int)atoi(arg_value.c_str()); return true; } return false; } unsigned getNumberOfIterations() const { return numberOfIterations; } template void operator ()(const Body & body) { unsigned int i = 1; // std::cerr << "DEBUG: numberOfIterations: " << numberOfIterations << "\n"; while ((i <= numberOfIterations) || (numberOfIterations == 0)) { if (body() == false) { break; } ++i; } } }; #ifdef __linux__ FILE * tryOpen(const char * path, const char * mode); std::string readSysFS(const char * path, bool silent); bool writeSysFS(const char * path, const std::string & value, bool silent); #endif int calibratedSleep(const double delay, const char* sysCmd, const MainLoop& mainLoop, PCM* m); struct StackedBarItem { double fraction{0.0}; std::string label{""}; // not used currently char fill{'0'}; StackedBarItem() {} StackedBarItem(double fraction_, const std::string & label_, char fill_) : fraction(fraction_), label(label_), fill(fill_) {} }; void drawStackedBar(const std::string & label, std::vector & h, const int width = 80); // emulates scanf %i for hex 0x prefix otherwise assumes dec (no oct support) bool match(const std::string& subtoken, const std::string& sname, uint64* result); uint64 read_number(const char* str); union PCM_CPUID_INFO { int array[4]; struct { unsigned int eax, ebx, ecx, edx; } reg; }; inline void pcm_cpuid(int leaf, PCM_CPUID_INFO& info) { #ifdef _MSC_VER // version for Windows __cpuid(info.array, leaf); #else __asm__ __volatile__("cpuid" : \ "=a" (info.reg.eax), "=b" (info.reg.ebx), "=c" (info.reg.ecx), "=d" (info.reg.edx) : "a" (leaf)); #endif } inline void clear_screen() { #ifdef _MSC_VER system("cls"); #else std::cout << "\033[2J\033[0;0H"; #endif } inline uint32 build_bit_ui(uint32 beg, uint32 end) { assert(end <= 31); uint32 myll = 0; if (end == 31) { myll = (uint32)(-1); } else { myll = (1 << (end + 1)) - 1; } myll = myll >> beg; return myll; } inline uint32 extract_bits_ui(uint32 myin, uint32 beg, uint32 end) { uint32 myll = 0; uint32 beg1, end1; // Let the user reverse the order of beg & end. if (beg <= end) { beg1 = beg; end1 = end; } else { beg1 = end; end1 = beg; } myll = myin >> beg1; myll = myll & build_bit_ui(beg1, end1); return myll; } inline uint64 build_bit(uint32 beg, uint32 end) { uint64 myll = 0; if (end == 63) { myll = static_cast(-1); } else { myll = (1LL << (end + 1)) - 1; } myll = myll >> beg; return myll; } inline uint64 extract_bits(uint64 myin, uint32 beg, uint32 end) { uint64 myll = 0; uint32 beg1, end1; // Let the user reverse the order of beg & end. if (beg <= end) { beg1 = beg; end1 = end; } else { beg1 = end; end1 = beg; } myll = myin >> beg1; myll = myll & build_bit(beg1, end1); return myll; } std::string safe_getenv(const char* env); #ifdef _MSC_VER inline HANDLE openMSRDriver() { return CreateFile(TEXT("\\\\.\\RDMSR"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); } #endif #define PCM_ENFORCE_FLUSH_OPTION else if (check_argument_equals(*argv, { "-f", "/f" })) { enforceFlush = true; continue; } void print_enforce_flush_option_help(); // called before everything else to read '-s' arg and // silence all following err output void check_and_set_silent(int argc, char * argv[], null_stream &nullStream2); void print_pid_collection_message(int pid); bool print_version(int argc, char * argv[]); inline bool isPIDOption(char * argv []) { return check_argument_equals(*argv, {"-pid", "/pid"}); } inline void parsePID(int argc, char* argv[], int& pid) { parseParam(argc, argv, "pid", [&pid](const char* p) { if (p) pid = atoi(p); }); } struct counter { std::string h_event_name = ""; std::string v_event_name = ""; uint64_t ccr = 0; int idx = 0; /* Some counters need to be placed in specific index */ int multiplier = 0; int divider = 0; uint32_t h_id = 0; uint32_t v_id = 0; }; struct data{ uint32_t width; uint64_t value; }; typedef enum{ EVT_LINE_START, EVT_LINE_FIELD, EVT_LINE_COMPLETE }evt_cb_type; std::string dos2unix(std::string in); std::string a_title (const std::string &init, const std::string &name); std::string a_data (std::string init, struct data d); std::string a_header_footer(std::string init, std::string name); std::string build_line(std::string init, std::string name, bool last_char, char this_char); std::string build_csv_row(const std::vector& chunks, const std::string& delimiter); std::vector prepare_data(const std::vector &values, const std::vector &headers); void display(const std::vector &buff, std::ostream& stream); void print_nameMap(std::map>>& nameMap); int load_events(const std::string &fn, std::map &ofm, int (*p_fn_evtcb)(evt_cb_type, void *, counter &, std::map &, std::string, uint64), void *evtcb_ctx, std::map>> &nameMap); int load_events(const std::string &fn, std::map &ofm, int (*pfn_evtcb)(evt_cb_type, void *, counter &, std::map &, std::string, uint64), void *evtcb_ctx); bool get_cpu_bus(uint32 msmDomain, uint32 msmBus, uint32 msmDev, uint32 msmFunc, uint32 &cpuBusValid, std::vector &cpuBusNo, int &cpuPackageId); #ifdef __linux__ FILE * tryOpen(const char * path, const char * mode); std::string readSysFS(const char * path, bool silent = false); bool writeSysFS(const char * path, const std::string & value, bool silent = false); int readMaxFromSysFS(const char * path); bool readMapFromSysFS(const char * path, std::unordered_map &result, bool silent = false); #endif } // namespace pcm pcm-202307/src/version.h000066400000000000000000000000761445420033100150140ustar00rootroot00000000000000#define PCM_VERSION "(2023-07-14 10:01:29 +0200 ID=ccc53261)" pcm-202307/src/width_extender.h000066400000000000000000000126761445420033100163550ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // written by Roman Dementiev // Austen Ott #ifndef WIDTH_EXTENDER_HEADER_ #define WIDTH_EXTENDER_HEADER_ /*! \file width_extender.h \brief Provides 64-bit "virtual" counters from underlying 32-bit HW counters */ #include #include "cpucounters.h" #include "utils.h" #include "bw.h" #include "mutex.h" #include #ifndef _MSC_VER // the header can not be included into code using CLR #include #else namespace std { class thread; } #endif namespace pcm { class CounterWidthExtender { public: struct AbstractRawCounter { virtual uint64 operator () () = 0; virtual ~AbstractRawCounter() { } }; struct MsrHandleCounter : public AbstractRawCounter { std::shared_ptr msr; uint64 msr_addr; MsrHandleCounter(std::shared_ptr msr_, uint64 msr_addr_) : msr(msr_), msr_addr(msr_addr_) { } uint64 operator () () { uint64 value = 0; msr->read(msr_addr, &value); return value; } }; template struct ClientImcCounter : public AbstractRawCounter { std::shared_ptr clientBW; ClientImcCounter(std::shared_ptr clientBW_) : clientBW(clientBW_) { } uint64 operator () () { return (clientBW.get()->*F)(); } }; typedef ClientImcCounter<&FreeRunningBWCounters::getImcReads> ClientImcReadsCounter; typedef ClientImcCounter<&FreeRunningBWCounters::getImcWrites> ClientImcWritesCounter; typedef ClientImcCounter<&FreeRunningBWCounters::getGtRequests> ClientGtRequestsCounter; typedef ClientImcCounter<&FreeRunningBWCounters::getIaRequests> ClientIaRequestsCounter; typedef ClientImcCounter<&FreeRunningBWCounters::getIoRequests> ClientIoRequestsCounter; struct MBLCounter : public AbstractRawCounter { std::shared_ptr msr; MBLCounter(std::shared_ptr msr_) : msr(msr_) { } uint64 operator () () { msr->lock(); uint64 event = 3; // L3 Local External Bandwidth uint64 msr_qm_evtsel = 0, value = 0; msr->read(IA32_QM_EVTSEL, &msr_qm_evtsel); //std::cout << "MBLCounter reading IA32_QM_EVTSEL 0x"<< std::hex << msr_qm_evtsel << std::dec << "\n"; msr_qm_evtsel &= 0xfffffffffffffff0ULL; msr_qm_evtsel |= event & ((1ULL << 8) - 1ULL); //std::cout << "MBL event " << msr_qm_evtsel << "\n"; //std::cout << "MBLCounter writing IA32_QM_EVTSEL 0x"<< std::hex << msr_qm_evtsel << std::dec << "\n"; msr->write(IA32_QM_EVTSEL, msr_qm_evtsel); msr->read(IA32_QM_CTR, &value); //std::cout << "MBLCounter reading IA32_QM_CTR "<< std::dec << value << std::dec << "\n"; msr->unlock(); return value; } }; struct MBTCounter : public AbstractRawCounter { std::shared_ptr msr; MBTCounter(std::shared_ptr msr_) : msr(msr_) { } uint64 operator () () { msr->lock(); uint64 event = 2; // L3 Total External Bandwidth uint64 msr_qm_evtsel = 0, value = 0; msr->read(IA32_QM_EVTSEL, &msr_qm_evtsel); //std::cout << "MBTCounter reading IA32_QM_EVTSEL 0x"<< std::hex << msr_qm_evtsel << std::dec << "\n"; msr_qm_evtsel &= 0xfffffffffffffff0ULL; msr_qm_evtsel |= event & ((1ULL << 8) - 1ULL); //std::cout << "MBR event " << msr_qm_evtsel << "\n"; //std::cout << "MBTCounter writing IA32_QM_EVTSEL 0x"<< std::hex << msr_qm_evtsel << std::dec << "\n"; msr->write(IA32_QM_EVTSEL, msr_qm_evtsel); msr->read(IA32_QM_CTR, &value); //std::cout << "MBTCounter reading IA32_QM_CTR "<< std::dec << value << std::dec << "\n"; msr->unlock(); return value; } }; private: std::thread * UpdateThread; Mutex CounterMutex; AbstractRawCounter * raw_counter; uint64 extended_value; uint64 last_raw_value; uint64 counter_width; uint32 watchdog_delay_ms; CounterWidthExtender(); // forbidden CounterWidthExtender(CounterWidthExtender &); // forbidden CounterWidthExtender & operator = (const CounterWidthExtender &); // forbidden uint64 internal_read() { uint64 result = 0, new_raw_value = 0; CounterMutex.lock(); new_raw_value = (*raw_counter)(); if (new_raw_value < last_raw_value) { extended_value += ((1ULL << counter_width) - last_raw_value) + new_raw_value; } else { extended_value += (new_raw_value - last_raw_value); } last_raw_value = new_raw_value; result = extended_value; CounterMutex.unlock(); return result; } public: CounterWidthExtender(AbstractRawCounter * raw_counter_, uint64 counter_width_, uint32 watchdog_delay_ms_); virtual ~CounterWidthExtender(); uint64 read() // read extended value { return internal_read(); } void reset() { CounterMutex.lock(); extended_value = last_raw_value = (*raw_counter)(); CounterMutex.unlock(); } }; } // namespace pcm #endif pcm-202307/src/windows/000077500000000000000000000000001445420033100146455ustar00rootroot00000000000000pcm-202307/src/windows/AssemblyInfo.cpp000066400000000000000000000024721445420033100177510ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman */ using namespace System; using namespace System::Reflection; using namespace System::Runtime::CompilerServices; using namespace System::Runtime::InteropServices; using namespace System::Security::Permissions; // // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. // [assembly:AssemblyTitleAttribute("PCMService")]; [assembly:AssemblyDescriptionAttribute("")]; [assembly:AssemblyConfigurationAttribute("")]; [assembly:AssemblyCompanyAttribute("Intel Corp")]; [assembly:AssemblyProductAttribute("PCMService")]; [assembly:AssemblyCopyrightAttribute("Copyright (c) Intel Corp 2010-2022")]; [assembly:AssemblyTrademarkAttribute("")]; [assembly:AssemblyCultureAttribute("")]; // // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the value or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly:AssemblyVersionAttribute("1.0.*")]; [assembly:ComVisible(false)]; [assembly:CLSCompliantAttribute(true)]; pcm-202307/src/windows/PCM-Service.exe.config000066400000000000000000000003431445420033100206310ustar00rootroot00000000000000 pcm-202307/src/windows/PCMInstaller.cpp000066400000000000000000000002261445420033100176460ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman */ #include "PCMInstaller.h" pcm-202307/src/windows/PCMInstaller.h000066400000000000000000000052131445420033100173140ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman */ #pragma once using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Configuration::Install; namespace PMUService { [RunInstaller(true)] /// /// Summary for ProjectInstaller /// public ref class ProjectInstaller : public System::Configuration::Install::Installer { public: ProjectInstaller(void) { InitializeComponent(); // //TODO: Add the constructor code here // } protected: /// /// Clean up any resources being used. /// ~ProjectInstaller() { if (components) { delete components; } } private: System::ServiceProcess::ServiceProcessInstaller^ serviceProcessInstaller1; protected: private: System::ServiceProcess::ServiceInstaller^ serviceInstaller1; private: /// /// Required designer variable. /// System::ComponentModel::Container ^components; #pragma region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// void InitializeComponent(void) { this->serviceProcessInstaller1 = (gcnew System::ServiceProcess::ServiceProcessInstaller()); this->serviceInstaller1 = (gcnew System::ServiceProcess::ServiceInstaller()); // // serviceProcessInstaller1 // this->serviceProcessInstaller1->Account = System::ServiceProcess::ServiceAccount::LocalSystem; this->serviceProcessInstaller1->Password = nullptr; this->serviceProcessInstaller1->Username = nullptr; // // serviceInstaller1 // this->serviceInstaller1->Description = L"This service provides performance counters for perfmon to show hardware events ov" L"er time such as Clockticks, Instruction Retired, Cache Misses and Memory Bandwi" L"dth."; this->serviceInstaller1->DisplayName = L"Intel(r) Performance Counter Monitor Service"; this->serviceInstaller1->ServiceName = L"PCMService"; this->serviceInstaller1->StartType = System::ServiceProcess::ServiceStartMode::Automatic; // // PCMInstaller // this->Installers->AddRange(gcnew cli::array< System::Configuration::Install::Installer^ >(2) {this->serviceProcessInstaller1, this->serviceInstaller1}); } #pragma endregion }; } pcm-202307/src/windows/PCMService.cpp000066400000000000000000000040041445420033100173070ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman and Roman Dementiev */ // PCMService.cpp : main Windows Service project file. #include "PCMService.h" #include #include #include using namespace PCMServiceNS; using namespace System::Text; using namespace System::Security::Policy; using namespace System::Reflection; //To install/uninstall the service, type: "PCM-Service.exe [-Install/-Uninstall]" int _tmain(int argc, _TCHAR* argv[]) { if (argc >= 2) { if (argv[1][0] == _T('/')) { argv[1][0] = _T('-'); } if (_tcsicmp(argv[1], _T("-Install")) == 0) { array^ myargs = System::Environment::GetCommandLineArgs(); array^ args = gcnew array(myargs->Length - 1); // Set args[0] with the full path to the assembly, Assembly^ assem = Assembly::GetExecutingAssembly(); args[0] = assem->Location; Array::Copy(myargs, 2, args, 1, args->Length - 1); AppDomain^ dom = AppDomain::CreateDomain(L"execDom"); Type^ type = System::Object::typeid; String^ path = type->Assembly->Location; StringBuilder^ sb = gcnew StringBuilder(path->Substring(0, path->LastIndexOf(L"\\"))); sb->Append(L"\\InstallUtil.exe"); dom->ExecuteAssembly(sb->ToString(), args); } else if (_tcsicmp(argv[1], _T("-Uninstall")) == 0) { array^ myargs = System::Environment::GetCommandLineArgs(); array^ args = gcnew array(2); args[0] = L"-u"; // Set args[0] with the full path to the assembly, Assembly^ assem = Assembly::GetExecutingAssembly(); args[1] = assem->Location; AppDomain^ dom = AppDomain::CreateDomain(L"execDom"); Type^ type = System::Object::typeid; String^ path = type->Assembly->Location; StringBuilder^ sb = gcnew StringBuilder(path->Substring(0, path->LastIndexOf(L"\\"))); sb->Append(L"\\InstallUtil.exe"); dom->ExecuteAssembly(sb->ToString(), args); } } else { ServiceBase::Run(gcnew PCMService); } } pcm-202307/src/windows/PCMService.h000077500000000000000000001560741445420033100167760ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman, Roman Dementiev */ #pragma once #pragma unmanaged #include "pcm-lib.h" #include "windriver.h" #include #ifndef UNICODE #include #include #endif #pragma managed using namespace pcm; using namespace System; using namespace System::Collections; using namespace System::ServiceProcess; using namespace System::ComponentModel; using namespace System::Diagnostics; using namespace System::Threading; using namespace System::Runtime::InteropServices; namespace PCMServiceNS { ref struct Globals { static initonly String^ ServiceName = gcnew String(L"PCMService"); }; ref struct CollectionInformation { CollectionInformation() { core = true; socket = true; qpi = true; } CollectionInformation(const CollectionInformation^ ©able) { core = copyable->core; socket = copyable->socket; qpi = copyable->qpi; } bool core; bool socket; bool qpi; }; ref class MeasureThread { public: MeasureThread(System::Diagnostics::EventLog^ log, int sampleRate, CollectionInformation^ collectionInformation) : log_(log), sampleRate_(sampleRate), collectionInformation_(collectionInformation) { // Get a Monitor instance which sets up the PMU, it also figures out the number of cores and sockets which we need later on to create the performance counters m_ = PCM::getInstance(); if ( !m_->good() ) { log_->WriteEntry(Globals::ServiceName, "Monitor Instance could not be created.", EventLogEntryType::Error); m_->cleanup(); String^ s = gcnew String(m_->getErrorMessage().c_str()); throw gcnew Exception(s); } log_->WriteEntry(Globals::ServiceName, "PCM: Number of cores detected: " + UInt32(m_->getNumCores()).ToString()); m_->program(); log_->WriteEntry(Globals::ServiceName, "PMU Programmed."); CountersQpi = gcnew String(L"PCM " + gcnew System::String(m_->xPI()) + L" Counters"); MetricQpiBand = gcnew String(gcnew System::String(m_->xPI()) + L" Link Bandwidth"); // This here will only create the necessary registry entries, the actual counters are created later. // New unified category if (PerformanceCounterCategory::Exists(CountersCore)) { PerformanceCounterCategory::Delete(CountersCore); } if (PerformanceCounterCategory::Exists(CountersSocket)) { PerformanceCounterCategory::Delete(CountersSocket); } if (PerformanceCounterCategory::Exists(CountersQpi)) { PerformanceCounterCategory::Delete(CountersQpi); } log_->WriteEntry(Globals::ServiceName, "Old categories deleted."); // First create the collection, then add counters to it so we add them all at once CounterCreationDataCollection^ counterCollection = gcnew CounterCreationDataCollection; // Here we add the counters one by one, need list of counters currently collected. // This is a stub: copy and paste when new counters are added to ipcustat "library". // CounterCreationData^ counter = gcnew CounterCreationData( "counter", "helptext for counter", PerformanceCounterType::NumberOfItems64 ); // counterCollection->Add( counter ); CounterCreationData^ counter; if (collectionInformation_->core) { counter = gcnew CounterCreationData(MetricCoreClocktick, "Displays the number of clockticks elapsed since previous measurement.", PerformanceCounterType::CounterDelta64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreRetired, "Displays the number of instructions retired since previous measurement.", PerformanceCounterType::CounterDelta64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreMissL2, "Displays the L2 Cache Misses caused by this core.", PerformanceCounterType::CounterDelta64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreMissL3, "Displays the L3 Cache Misses caused by this core.", PerformanceCounterType::CounterDelta64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreIpc, "Displays the instructions per clocktick executed for this core.", PerformanceCounterType::AverageCount64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreBaseIpc, "Not visible", PerformanceCounterType::AverageBase); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreFreqRel, "Displays the current frequency of the core to its rated frequency in percent.", PerformanceCounterType::SampleFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreFreqNom, "Not visible", PerformanceCounterType::SampleBase); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreHeadroom, "Displays temperature reading in 1 degree Celsius relative to the TjMax temperature. 0 corresponds to the max temperature.", PerformanceCounterType::NumberOfItems64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreResC0, "Displays the residency of core or socket in core C0-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreResC0Base, "", PerformanceCounterType::RawBase); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreResC1, "Displays the residency of core or socket in core C1-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreResC1Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricCoreResC3, "Displays the residency of core or socket in core C3-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreResC3Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricCoreResC6, "Displays the residency of core or socket in core C6-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreResC6Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricCoreResC7, "Displays the residency of core or socket in core C7-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricCoreResC7Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); PerformanceCounterCategory::Create(CountersCore, "Intel(r) Performance Counter Monitor", PerformanceCounterCategoryType::MultiInstance, counterCollection); } if (collectionInformation_->socket) { counterCollection->Clear(); counter = gcnew CounterCreationData(MetricSocketBandRead, "Displays the memory read bandwidth in bytes/s of this socket.", PerformanceCounterType::NumberOfItems64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketBandWrite, "Displays the memory write bandwidth in bytes/s of this socket.", PerformanceCounterType::NumberOfItems64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketEnergyPack, "Displays the energy in Joules consumed by this socket.", PerformanceCounterType::NumberOfItems64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketEnergyDram, "Displays the energy in Joules consumed by DRAM memory attached to the memory controller of this socket.", PerformanceCounterType::NumberOfItems64); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC0, "Displays the residency of socket in package C0-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC0Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricSocketResC2, "Displays the residency of socket in package C2-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC2Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricSocketResC3, "Displays the residency of socket in package C3-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC3Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricSocketResC6, "Displays the residency of socket in package C6-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC6Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricSocketResC7, "Displays the residency of socket in package C7-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC7Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricSocketResC8, "Displays the residency of socket in package C8-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC8Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricSocketResC9, "Displays the residency of socket in package C9-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC9Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); counter = gcnew CounterCreationData(MetricSocketResC10, "Displays the residency of socket in package C10-state in percent.", PerformanceCounterType::RawFraction); counterCollection->Add( counter ); counter = gcnew CounterCreationData(MetricSocketResC10Base, "", PerformanceCounterType::RawBase); counterCollection->Add(counter); PerformanceCounterCategory::Create(CountersSocket, "Intel(r) Performance Counter Monitor", PerformanceCounterCategoryType::MultiInstance, counterCollection); } if (collectionInformation_->qpi) { counterCollection->Clear(); counter = gcnew CounterCreationData(MetricQpiBand, L"Displays the incoming bandwidth in bytes/s of this " + gcnew System::String(m_->xPI()) + L" link", PerformanceCounterType::CounterDelta64); counterCollection->Add( counter ); PerformanceCounterCategory::Create(CountersQpi, "Intel(r) Performance Counter Monitor", PerformanceCounterCategoryType::MultiInstance, counterCollection); } log_->WriteEntry(Globals::ServiceName, "New categories added."); // Registry entries created, now we need to create the programmatic counters. For some things you may want one instance for every core/thread/socket/qpilink so create in a loop. // PerformanceCounter^ pc1 = gcnew PerformanceCounter( "SomeCounterName", "nameOfCounterAsEnteredInTheRegistry", "instanceNameOfCounterAsANumber" ); // Create #processors instances of the core specific performance counters String^ s; // Used for creating the instance name and the string to search for in the hashtable PerformanceCounter^ pc; for ( unsigned int i = 0; i < m_->getNumCores(); ++i ) { s = UInt32(i).ToString(); // For core counters we use just the number of the core if (collectionInformation_->core) { ticksHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreClocktick, s, false)); instRetHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreRetired, s, false)); l2CacheMissHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreMissL2, s, false)); l3CacheMissHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreMissL3, s, false)); ipcHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreIpc, s, false)); baseTicksForIpcHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreBaseIpc, s, false)); relFreqHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreFreqRel, s, false)); baseTicksForRelFreqHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreFreqNom, s, false)); thermalHeadroomHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreHeadroom, s, false)); CoreC0StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC0, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC0Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC1StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC1, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC1Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC3StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC3, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC3Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC6StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC6, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC6Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC7StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC7, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC7Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); } } // Create socket instances of the common core counters, names are Socket+number for ( unsigned int i=0; igetNumSockets(); ++i ) { s = "Socket"+UInt32(i).ToString(); if (collectionInformation_->core) { ticksHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreClocktick, s, false)); instRetHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreRetired, s, false)); l2CacheMissHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreMissL2, s, false)); l3CacheMissHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreMissL3, s, false)); ipcHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreIpc, s, false)); baseTicksForIpcHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreBaseIpc, s, false)); relFreqHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreFreqRel, s, false)); baseTicksForRelFreqHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreFreqNom, s, false)); thermalHeadroomHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreHeadroom, s, false)); CoreC0StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC0, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC0Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC1StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC1, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC1Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC3StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC3, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC3Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC6StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC6, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC6Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC7StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC7, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC7Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); } if (collectionInformation_->socket) { mrbHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketBandRead, s, false)); mwbHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketBandWrite, s, false)); packageEnergyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketEnergyPack, s, false)); DRAMEnergyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketEnergyDram, s, false)); PackageC0StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC0, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC0Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC2StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC2, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC2Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC3StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC3, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC3Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC6StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC6, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC6Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC7StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC7, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC7Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC8StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC8, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC8Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC9StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC9, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC9Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC10StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC10, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC10Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); } if (collectionInformation_->qpi) { qpiHash_.Add(s, gcnew PerformanceCounter(CountersQpi, MetricQpiBand, s, false)); // Socket aggregate String^ t; for ( unsigned int j=0; jgetQPILinksPerSocket(); ++j ) { t = s + "_Link" + UInt32(j).ToString(); qpiHash_.Add(t, gcnew PerformanceCounter(CountersQpi, MetricQpiBand, t, false)); } } } // Create #system instances of the system specific performance counters, just kidding, there is only one system so 1 instance s = "Total_"; if (collectionInformation_->core) { ticksHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreClocktick, s, false)); instRetHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreRetired, s, false)); l2CacheMissHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreMissL2, s, false)); l3CacheMissHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreMissL3, s, false)); ipcHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreIpc, s, false)); baseTicksForIpcHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreBaseIpc, s, false)); relFreqHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreFreqRel, s, false)); baseTicksForRelFreqHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreFreqNom, s, false)); thermalHeadroomHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreHeadroom, s, false)); CoreC0StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC0, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC0Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC1StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC1, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC1Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC3StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC3, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC3Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC6StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC6, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC6Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); CoreC7StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersCore, MetricCoreResC7, s, false)); pc = gcnew PerformanceCounter(CountersCore, MetricCoreResC7Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); } if (collectionInformation_->socket) { mrbHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketBandRead, s, false)); mwbHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketBandWrite, s, false)); packageEnergyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketEnergyPack, s, false)); DRAMEnergyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketEnergyDram, s, false)); PackageC0StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC0, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC0Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC2StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC2, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC2Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC3StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC3, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC3Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC6StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC6, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC6Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC7StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC7, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC7Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC8StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC8, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC8Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC9StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC9, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC9Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); PackageC10StateResidencyHash_.Add(s, gcnew PerformanceCounter(CountersSocket, MetricSocketResC10, s, false)); pc = gcnew PerformanceCounter(CountersSocket, MetricSocketResC10Base, s, false); pc->RawValue = 1000; baseArrayList_.Add(pc); } if (collectionInformation_->qpi) { qpiHash_.Add(s, gcnew PerformanceCounter(CountersQpi, MetricQpiBand, s, false)); } log_->WriteEntry(Globals::ServiceName, "All instances of the performance counter categories have been created."); } void doMeasurements( void ) { // FIXME: Do we care about hot swappability of CPUs? const size_t numSockets = m_->getNumSockets(); const size_t numCores = m_->getNumCores(); const size_t numQpiLinks = (size_t) m_->getQPILinksPerSocket(); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); // The structures SystemCounterState oldSystemState; std::vector oldSocketStates; std::vector oldCoreStates; SystemCounterState systemState; std::vector socketStates; std::vector coreStates; ULONGLONG BeforeTime = 0, AfterTime = 0; m_->getAllCounterStates(oldSystemState, oldSocketStates, oldCoreStates); BeforeTime = GetTickCount64(); // labmda functions are not allowed in managed code, using a macro #define toBW(val) (val * 1000ULL / (AfterTime - BeforeTime)) try { while (true) { Thread::Sleep(sampleRate_); // Fetch counter data here and store in the PerformanceCounter instances m_->getAllCounterStates(systemState, socketStates, coreStates); AfterTime = GetTickCount64(); // Set system performance counters String^ s = "Total_"; if (collectionInformation_->core) { __int64 totalTicks = getCycles(systemState); __int64 totalRefTicks = m_->getNominalFrequency() * numCores; __int64 totalInstr = getInstructionsRetired(systemState); ((PerformanceCounter^)ticksHash_[s])->RawValue = totalTicks; ((PerformanceCounter^)instRetHash_[s])->RawValue = totalInstr; ((PerformanceCounter^)l2CacheMissHash_[s])->IncrementBy(getL2CacheMisses(oldSystemState, systemState)); ((PerformanceCounter^)l3CacheMissHash_[s])->IncrementBy(getL3CacheMisses(oldSystemState, systemState)); ((PerformanceCounter^)ipcHash_[s])->RawValue = totalInstr >> 17; ((PerformanceCounter^)baseTicksForIpcHash_[s])->RawValue = totalTicks >> 17; ((PerformanceCounter^)relFreqHash_[s])->RawValue = totalTicks >> 17; ((PerformanceCounter^)baseTicksForRelFreqHash_[s])->IncrementBy(totalRefTicks >> 17); ((PerformanceCounter^)thermalHeadroomHash_[s])->RawValue = systemState.getThermalHeadroom(); ((PerformanceCounter^)CoreC0StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(0,oldSystemState, systemState)); ((PerformanceCounter^)CoreC1StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(1,oldSystemState, systemState)); ((PerformanceCounter^)CoreC3StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(3,oldSystemState, systemState)); ((PerformanceCounter^)CoreC6StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(6,oldSystemState, systemState)); ((PerformanceCounter^)CoreC7StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(7,oldSystemState, systemState)); //log_->WriteEntry(Globals::ServiceName, "Std: " + UInt64(totalTicks).ToString()); //log_->WriteEntry(Globals::ServiceName, "Ref: " + UInt64(totalRefTicks).ToString()); } if (collectionInformation_->socket) { ((PerformanceCounter^)mrbHash_[s])->RawValue = toBW(getBytesReadFromMC(oldSystemState, systemState)); ((PerformanceCounter^)mwbHash_[s])->RawValue = toBW(getBytesWrittenToMC(oldSystemState, systemState)); ((PerformanceCounter^)packageEnergyHash_[s])->RawValue = (__int64)getConsumedJoules(oldSystemState, systemState); ((PerformanceCounter^)DRAMEnergyHash_[s])->RawValue = (__int64)getDRAMConsumedJoules(oldSystemState, systemState); ((PerformanceCounter^)PackageC0StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 0, oldSystemState, systemState)); ((PerformanceCounter^)PackageC2StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 2, oldSystemState, systemState)); ((PerformanceCounter^)PackageC3StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 3, oldSystemState, systemState)); ((PerformanceCounter^)PackageC6StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 6, oldSystemState, systemState)); ((PerformanceCounter^)PackageC7StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 7, oldSystemState, systemState)); ((PerformanceCounter^)PackageC8StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 8, oldSystemState, systemState)); ((PerformanceCounter^)PackageC9StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 9, oldSystemState, systemState)); ((PerformanceCounter^)PackageC10StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency(10, oldSystemState, systemState)); } if (collectionInformation_->qpi) { ((PerformanceCounter^)qpiHash_[s])->RawValue = getAllIncomingQPILinkBytes(systemState); } // Set socket performance counters for ( unsigned int i = 0; i < numSockets; ++i ) { s = "Socket"+UInt32(i).ToString(); const SocketCounterState & socketState = socketStates[i]; if (collectionInformation_->core) { __int64 socketTicks = getCycles(socketState); __int64 socketRefTicks = m_->getNominalFrequency()* numCores / numSockets; __int64 socketInstr = getInstructionsRetired(socketState); ((PerformanceCounter^)instRetHash_[s])->RawValue = socketInstr; ((PerformanceCounter^)ipcHash_[s])->RawValue = socketInstr >> 17; ((PerformanceCounter^)l2CacheMissHash_[s])->IncrementBy(getL2CacheMisses(oldSocketStates[i], socketState)); ((PerformanceCounter^)l3CacheMissHash_[s])->IncrementBy(getL3CacheMisses(oldSocketStates[i], socketState)); ((PerformanceCounter^)ticksHash_[s])->RawValue = socketTicks; ((PerformanceCounter^)baseTicksForIpcHash_[s])->RawValue = socketTicks >> 17; ((PerformanceCounter^)relFreqHash_[s])->RawValue = socketTicks >> 17; ((PerformanceCounter^)baseTicksForRelFreqHash_[s])->IncrementBy(socketRefTicks >> 17); ((PerformanceCounter^)thermalHeadroomHash_[s])->RawValue = socketState.getThermalHeadroom(); ((PerformanceCounter^)CoreC0StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(0, oldSocketStates[i], socketState)); ((PerformanceCounter^)CoreC1StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(1, oldSocketStates[i], socketState)); ((PerformanceCounter^)CoreC3StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(3, oldSocketStates[i], socketState)); ((PerformanceCounter^)CoreC6StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(6, oldSocketStates[i], socketState)); ((PerformanceCounter^)CoreC7StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(7, oldSocketStates[i], socketState)); } if (collectionInformation_->socket) { ((PerformanceCounter^)mrbHash_[s])->RawValue = toBW(getBytesReadFromMC(oldSocketStates[i], socketState)); ((PerformanceCounter^)mwbHash_[s])->RawValue = toBW(getBytesWrittenToMC(oldSocketStates[i], socketState)); ((PerformanceCounter^)packageEnergyHash_[s])->RawValue = (__int64)getConsumedJoules(oldSocketStates[i], socketState); ((PerformanceCounter^)DRAMEnergyHash_[s])->RawValue = (__int64)getDRAMConsumedJoules(oldSocketStates[i], socketState); ((PerformanceCounter^)PackageC0StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 0,oldSocketStates[i], socketState)); ((PerformanceCounter^)PackageC2StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 2,oldSocketStates[i], socketState)); ((PerformanceCounter^)PackageC3StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 3,oldSocketStates[i], socketState)); ((PerformanceCounter^)PackageC6StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 6,oldSocketStates[i], socketState)); ((PerformanceCounter^)PackageC7StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 7,oldSocketStates[i], socketState)); ((PerformanceCounter^)PackageC8StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 8,oldSocketStates[i], socketState)); ((PerformanceCounter^)PackageC9StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency( 9,oldSocketStates[i], socketState)); ((PerformanceCounter^)PackageC10StateResidencyHash_[s])->RawValue = __int64(1000. * getPackageCStateResidency(10,oldSocketStates[i], socketState)); } if (collectionInformation_->qpi) { ((PerformanceCounter^)qpiHash_[s])->RawValue = toBW(getSocketIncomingQPILinkBytes(i, systemState)); String^ t; // and qpi link counters per socket for ( unsigned int j=0; jRawValue = toBW(getIncomingQPILinkBytes(i, j, systemState)); } } } // Set core performance counters for ( unsigned int i = 0; i < numCores; ++i ) { s = UInt32(i).ToString(); const CoreCounterState & coreState = coreStates[i]; if (collectionInformation_->core) { __int64 ticks = getCycles(coreState); __int64 refTicks = m_->getNominalFrequency(); __int64 instr = getInstructionsRetired(coreState); ((PerformanceCounter^)instRetHash_[s])->RawValue = instr; ((PerformanceCounter^)ipcHash_[s])->RawValue = instr >> 17; ((PerformanceCounter^)l2CacheMissHash_[s])->IncrementBy(getL2CacheMisses(oldCoreStates[i], coreState)); ((PerformanceCounter^)l3CacheMissHash_[s])->IncrementBy(getL3CacheMisses(oldCoreStates[i], coreState)); ((PerformanceCounter^)ticksHash_[s])->RawValue = ticks; ((PerformanceCounter^)baseTicksForIpcHash_[s])->RawValue = ticks >> 17; ((PerformanceCounter^)relFreqHash_[s])->RawValue = ticks >> 17; ((PerformanceCounter^)baseTicksForRelFreqHash_[s])->IncrementBy(refTicks >> 17); ((PerformanceCounter^)thermalHeadroomHash_[s])->RawValue = coreState.getThermalHeadroom(); ((PerformanceCounter^)CoreC0StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(0,oldCoreStates[i], coreState)); ((PerformanceCounter^)CoreC1StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(1,oldCoreStates[i], coreState)); ((PerformanceCounter^)CoreC3StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(3,oldCoreStates[i], coreState)); ((PerformanceCounter^)CoreC6StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(6,oldCoreStates[i], coreState)); ((PerformanceCounter^)CoreC7StateResidencyHash_[s])->RawValue = __int64(1000. * getCoreCStateResidency(7,oldCoreStates[i], coreState)); } } std::swap(oldSystemState, systemState); std::swap(oldSocketStates, socketStates); std::swap(oldCoreStates, coreStates); std::swap(BeforeTime, AfterTime); } } catch( ThreadAbortException^ ) { // We get here when for instance the service gets stopped or something bad happens. // In order to do our cleanup, like unprogram the MSRs, close the driver and such, in case we get stopped we want to execute normally after this // so this resets the abort and allows a normal exit Thread::ResetAbort(); } // Here we now have the chance to do cleanup after catching the ThreadAbortException because of the ResetAbort m_->cleanup(); } private: // Core counter hashtables System::Collections::Hashtable ticksHash_; System::Collections::Hashtable instRetHash_; System::Collections::Hashtable ipcHash_; System::Collections::Hashtable baseTicksForIpcHash_; System::Collections::Hashtable relFreqHash_; System::Collections::Hashtable baseTicksForRelFreqHash_; System::Collections::Hashtable l3CacheMissHash_; System::Collections::Hashtable l2CacheMissHash_; // Socket counter hashtables System::Collections::Hashtable mrbHash_; System::Collections::Hashtable mwbHash_; // QPI counter hashtables System::Collections::Hashtable qpiHash_; // Energy counters System::Collections::Hashtable packageEnergyHash_; System::Collections::Hashtable DRAMEnergyHash_; // Thermal headroom System::Collections::Hashtable thermalHeadroomHash_; // C-state Residencies System::Collections::Hashtable CoreC0StateResidencyHash_; System::Collections::Hashtable CoreC1StateResidencyHash_; System::Collections::Hashtable CoreC3StateResidencyHash_; System::Collections::Hashtable CoreC6StateResidencyHash_; System::Collections::Hashtable CoreC7StateResidencyHash_; System::Collections::Hashtable PackageC0StateResidencyHash_; System::Collections::Hashtable PackageC2StateResidencyHash_; System::Collections::Hashtable PackageC3StateResidencyHash_; System::Collections::Hashtable PackageC6StateResidencyHash_; System::Collections::Hashtable PackageC7StateResidencyHash_; System::Collections::Hashtable PackageC8StateResidencyHash_; System::Collections::Hashtable PackageC9StateResidencyHash_; System::Collections::Hashtable PackageC10StateResidencyHash_; System::Collections::ArrayList baseArrayList_; System::Diagnostics::EventLog^ log_; PCM* m_; // Counter variable names initonly String^ CountersCore = gcnew String(L"PCM Core Counters"); initonly String^ CountersSocket = gcnew String(L"PCM Socket Counters"); initonly String^ CountersQpi; initonly String^ MetricCoreClocktick = gcnew String(L"Clockticks"); initonly String^ MetricCoreRetired = gcnew String(L"Instructions Retired"); initonly String^ MetricCoreMissL2 = gcnew String(L"L2 Cache Misses"); initonly String^ MetricCoreMissL3 = gcnew String(L"L3 Cache Misses"); initonly String^ MetricCoreIpc = gcnew String(L"Instructions Per Clocktick (IPC)"); initonly String^ MetricCoreBaseIpc = gcnew String(L"Base ticks IPC"); initonly String^ MetricCoreFreqRel = gcnew String(L"Relative Frequency (%)"); initonly String^ MetricCoreFreqNom = gcnew String(L"Nominal Frequency"); initonly String^ MetricCoreHeadroom = gcnew String(L"Thermal Headroom below TjMax"); initonly String^ MetricCoreResC0 = gcnew String(L"core C0-state residency (%)"); initonly String^ MetricCoreResC1 = gcnew String(L"core C1-state residency (%)"); initonly String^ MetricCoreResC3 = gcnew String(L"core C3-state residency (%)"); initonly String^ MetricCoreResC6 = gcnew String(L"core C6-state residency (%)"); initonly String^ MetricCoreResC7 = gcnew String(L"core C7-state residency (%)"); initonly String^ MetricCoreResC0Base = gcnew String(L"core C0-state base"); initonly String^ MetricCoreResC1Base = gcnew String(L"core C1-state base"); initonly String^ MetricCoreResC3Base = gcnew String(L"core C3-state base"); initonly String^ MetricCoreResC6Base = gcnew String(L"core C6-state base"); initonly String^ MetricCoreResC7Base = gcnew String(L"core C7-state base"); initonly String^ MetricSocketBandRead = gcnew String(L"Memory Read Bandwidth"); initonly String^ MetricSocketBandWrite = gcnew String(L"Memory Write Bandwidth"); initonly String^ MetricSocketEnergyPack = gcnew String(L"Package/Socket Consumed Energy"); initonly String^ MetricSocketEnergyDram = gcnew String(L"DRAM/Memory Consumed Energy"); initonly String^ MetricSocketResC0 = gcnew String(L"package C0-state residency (%)"); initonly String^ MetricSocketResC2 = gcnew String(L"package C2-state residency (%)"); initonly String^ MetricSocketResC3 = gcnew String(L"package C3-state residency (%)"); initonly String^ MetricSocketResC6 = gcnew String(L"package C6-state residency (%)"); initonly String^ MetricSocketResC7 = gcnew String(L"package C7-state residency (%)"); initonly String^ MetricSocketResC8 = gcnew String(L"package C8-state residency (%)"); initonly String^ MetricSocketResC9 = gcnew String(L"package C9-state residency (%)"); initonly String^ MetricSocketResC10 = gcnew String(L"package C10-state residency (%)"); initonly String^ MetricSocketResC0Base = gcnew String(L"package C0-state base"); initonly String^ MetricSocketResC2Base = gcnew String(L"package C2-state base"); initonly String^ MetricSocketResC3Base = gcnew String(L"package C3-state base"); initonly String^ MetricSocketResC6Base = gcnew String(L"package C6-state base"); initonly String^ MetricSocketResC7Base = gcnew String(L"package C7-state base"); initonly String^ MetricSocketResC8Base = gcnew String(L"package C8-state base"); initonly String^ MetricSocketResC9Base = gcnew String(L"package C9-state base"); initonly String^ MetricSocketResC10Base = gcnew String(L"package C10-state base"); initonly String^ MetricQpiBand; // Configuration values const int sampleRate_; const CollectionInformation^ collectionInformation_; }; /// /// Summary for PMCService /// /// /// WARNING: If you change the name of this class, you will need to change the /// 'Resource File Name' property for the managed resource compiler tool /// associated with all .resx files this class depends on. Otherwise, /// the designers will not be able to interact properly with localized /// resources associated with this form. public ref class PCMService : public System::ServiceProcess::ServiceBase { [DllImport ("advapi32.dll")] static bool SetServiceStatus (IntPtr hServiceStatus, LPSERVICE_STATUS lpServiceStatus); private: void SetServiceFail (int ErrorCode) { SERVICE_STATUS ServiceStatus_; ServiceStatus_.dwCurrentState = (int)SERVICE_STOPPED; ServiceStatus_.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus_.dwWaitHint = 0; ServiceStatus_.dwWin32ExitCode = ErrorCode; ServiceStatus_.dwServiceSpecificExitCode = 0; ServiceStatus_.dwCheckPoint = 0; ServiceStatus_.dwControlsAccepted = 0 | (this->CanStop ? (int) SERVICE_ACCEPT_STOP : 0) | (this->CanShutdown ? (int) SERVICE_ACCEPT_SHUTDOWN : 0) | (this->CanPauseAndContinue ? (int) SERVICE_ACCEPT_PAUSE_CONTINUE : 0) | (this->CanHandleSessionChangeEvent ? (int) SERVICE_ACCEPT_SESSIONCHANGE : 0) | (this->CanHandlePowerEvent ? (int) SERVICE_ACCEPT_POWEREVENT : 0); SetServiceStatus (this->ServiceHandle, &ServiceStatus_); } public: PCMService() { InitializeComponent(); // //TODO: Add the constructor code here // } protected: /// /// Clean up any resources being used. /// ~PCMService() { if (components) { delete components; } } /// /// Set things in motion so your service can do its work. /// virtual void OnStart(array^ args) override { PCM* m_ = PCM::getInstance(); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); // Default values for configuration int sampleRate = 1000; CollectionInformation^ collectionInformation = gcnew CollectionInformation(); // Read configuration values from registry HKEY hkey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\pcm\\service"), NULL, KEY_READ, &hkey)) { DWORD regDWORD = static_cast(REG_DWORD); DWORD lenDWORD = 32; DWORD sampleRateRead(0); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("SampleRate"), NULL, NULL, reinterpret_cast(&sampleRateRead), &lenDWORD)) { sampleRate = (int)sampleRateRead; } DWORD collectCoreRead(0); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("CollectCore"), NULL, NULL, reinterpret_cast(&collectCoreRead), &lenDWORD)) { collectionInformation->core = (int)collectCoreRead > 0; } DWORD collectSocketRead(0); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("CollectSocket"), NULL, NULL, reinterpret_cast(&collectSocketRead), &lenDWORD)) { collectionInformation->socket = (int)collectSocketRead > 0; } DWORD collectQpiRead(0); if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("CollectQpi"), NULL, NULL, reinterpret_cast(&collectQpiRead), &lenDWORD)) { collectionInformation->qpi = (int)collectQpiRead > 0; } RegCloseKey(hkey); } this->RequestAdditionalTime(4000); // We should open the driver here EventLog->WriteEntry(Globals::ServiceName, "Trying to start the driver...", EventLogEntryType::Information); drv_ = new Driver; if (!drv_->start()) { #ifdef UNICODE const auto& driverPath = drv_->driverPath(); #else std::wstring_convert> char_to_wide; std::wstring driverPath = char_to_wide.from_bytes(drv_->driverPath().c_str()); #endif String^ s = gcnew String((L"Cannot open the driver.\nYou must have a signed driver at " + driverPath + L" and have administrator rights to run this program.\n\n").c_str()); EventLog->WriteEntry(Globals::ServiceName, s, EventLogEntryType::Error); SetServiceFail(ERROR_FILE_NOT_FOUND); throw gcnew Exception(s); } // TODO: Add code here to start your service. MeasureThread^ mt; EventLog->WriteEntry(Globals::ServiceName, "Trying to create the measure thread...", EventLogEntryType::Information); try { mt = gcnew MeasureThread(EventLog, sampleRate, collectionInformation); } catch (Exception^ e) { EventLog->WriteEntry(Globals::ServiceName, "Could not create MeasureThread, aborting", EventLogEntryType::Error); EventLog->WriteEntry(Globals::ServiceName, e->Message, EventLogEntryType::Error); SetServiceFail(0x80886); throw e; } // Create thread, pretty obvious comment here workerThread_ = gcnew Thread( gcnew ThreadStart( mt, &MeasureThread::doMeasurements ) ); // Start timer/thread to read out registers and fill performance counter structures workerThread_->Start(); // EventLog->WriteEntry("PCMService", System::DateTime::Now.ToLongTimeString() + " Monitor could not initialize PMU, aborting.", EventLogEntryType::Error); } /// /// Stop this service. /// virtual void OnStop() override { // TODO: Add code here to perform any tear-down necessary to stop your service. this->RequestAdditionalTime(4000); // Stop timer/thread // doMeasurements will do cleanup itself, might have to do some sanity checks here workerThread_->Abort(); drv_->stop(); } private: /// /// Required designer variable. /// System::ComponentModel::Container ^components; System::Threading::Thread^ workerThread_; Driver* drv_; #pragma region Windows Form Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// void InitializeComponent(void) { // // PCMService // this->CanPauseAndContinue = true; this->ServiceName = Globals::ServiceName; } #pragma endregion }; } pcm-202307/src/windows/PCM_cpp_ReadMe.txt000066400000000000000000000023411445420033100201040ustar00rootroot00000000000000======================================================================== CONSOLE APPLICATION : pcm Project Overview ======================================================================== AppWizard has created this pcm application for you. This file contains a summary of what you will find in each of the files that make up your pcm application. pcm.vcproj This is the main project file for VC++ projects generated using an Application Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the Application Wizard. pcm.cpp This is the main application source file. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named pcm.pch and a precompiled types file named StdAfx.obj. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" comments to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// pcm-202307/src/windows/ReadMe_PCMService.txt000066400000000000000000000022301445420033100205600ustar00rootroot00000000000000======================================================================== APPLICATION : PMU Service Project Overview ======================================================================== Windows Service Wizard has created this PMU Service Application for you. This file contains a summary of what you will find in each of the files that make up your PMU Service application. PMU Service.vcproj This is the main project file for VC++ projects generated using a Windows Service Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations. PMU ServiceWinService.cpp This is the main application source file. AssemblyInfo.cpp Contains custom attributes for modifying assembly metadata. ///////////////////////////////////////////////////////////////////////////// Other notes: Windows Service Wizard uses "TODO:" to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// To run your service: 1. Build the project 2. From the command line, run: PMU Service.exe -Install pcm-202307/src/windows/dllmain.cpp000066400000000000000000000010061445420033100167660ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman */ // dllmain.cpp : Defines the entry point for the DLL application. #include BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } pcm-202307/src/windows/pcm-core-win.cpp000066400000000000000000000001771445420033100176560ustar00rootroot00000000000000// pcm-core-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-core.cpp" pcm-202307/src/windows/pcm-iio-win.cpp000066400000000000000000000001751445420033100175040ustar00rootroot00000000000000// pcm-iio-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-iio.cpp" pcm-202307/src/windows/pcm-latency-win.cpp000066400000000000000000000002051445420033100203550ustar00rootroot00000000000000// pcm-latency-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-latency.cpp" pcm-202307/src/windows/pcm-lib.cpp000066400000000000000000000003421445420033100166730ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman */ // pcm-lib.cpp : Defines the exported functions for the DLL application. // #include "pcm-lib.h" pcm-202307/src/windows/pcm-lib.h000066400000000000000000000013451445420033100163440ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* ** Written by Otto Bruggeman */ // The following ifdef block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the PCM_EXPORTS // symbol defined on the command line. this symbol should not be defined on any project // that uses this DLL. This way any other project whose source files include this file see // PCM_API functions as being imported from a DLL, whereas this DLL sees symbols // defined with this macro as being exported. #ifdef PCM_EXPORTS #define PCM_API __declspec(dllexport) #else #define PCM_API __declspec(dllimport) #endif #include "..\cpucounters.h" pcm-202307/src/windows/pcm-lspci-win.cpp000066400000000000000000000002011445420033100200240ustar00rootroot00000000000000// pcm-lspci-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-lspci.cpp" pcm-202307/src/windows/pcm-memory-win.cpp000066400000000000000000000002031445420033100202240ustar00rootroot00000000000000// pcm-memory-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-memory.cpp" pcm-202307/src/windows/pcm-mmio-win.cpp000066400000000000000000000001771445420033100176670ustar00rootroot00000000000000// pcm-mmio-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-mmio.cpp" pcm-202307/src/windows/pcm-msr-win.cpp000066400000000000000000000001751445420033100175250ustar00rootroot00000000000000// pcm-msr-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-msr.cpp" pcm-202307/src/windows/pcm-numa-win.cpp000066400000000000000000000001771445420033100176660ustar00rootroot00000000000000// pcm-numa-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-numa.cpp" pcm-202307/src/windows/pcm-pcicfg-win.cpp000066400000000000000000000002031445420033100201470ustar00rootroot00000000000000// pcm-pcicfg-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-pcicfg.cpp" pcm-202307/src/windows/pcm-pcie-win.cpp000066400000000000000000000001771445420033100176460ustar00rootroot00000000000000// pcm-pcie-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-pcie.cpp" pcm-202307/src/windows/pcm-power-win.cpp000066400000000000000000000002011445420033100200460ustar00rootroot00000000000000// pcm-power-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-power.cpp" pcm-202307/src/windows/pcm-raw-win.cpp000066400000000000000000000001751445420033100175150ustar00rootroot00000000000000// pcm-raw-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-raw.cpp" pcm-202307/src/windows/pcm-tsx-win.cpp000066400000000000000000000001751445420033100175420ustar00rootroot00000000000000// pcm-tsx-win.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm-tsx.cpp" pcm-202307/src/windows/pcm.cpp000066400000000000000000000003111445420033100161230ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // pcm.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "../pcm.cpp" pcm-202307/src/windows/pcm_lib_ReadMe.txt000066400000000000000000000023761445420033100202400ustar00rootroot00000000000000======================================================================== DYNAMIC LINK LIBRARY : PMU Library Project Overview ======================================================================== AppWizard has created this PMU Library DLL for you. This file contains a summary of what you will find in each of the files that make up your PMU Library application. PMU Library.vcproj This is the main project file for VC++ projects generated using an Application Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the Application Wizard. PMU Library.cpp This is the main DLL source file. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named PMU Library.pch and a precompiled types file named StdAfx.obj. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" comments to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// pcm-202307/src/windows/pcm_power_ReadMe.txt000066400000000000000000000023411445420033100206160ustar00rootroot00000000000000======================================================================== CONSOLE APPLICATION : pcm Project Overview ======================================================================== AppWizard has created this pcm application for you. This file contains a summary of what you will find in each of the files that make up your pcm application. pcm.vcproj This is the main project file for VC++ projects generated using an Application Wizard. It contains information about the version of Visual C++ that generated the file, and information about the platforms, configurations, and project features selected with the Application Wizard. pcm.cpp This is the main application source file. ///////////////////////////////////////////////////////////////////////////// Other standard files: StdAfx.h, StdAfx.cpp These files are used to build a precompiled header (PCH) file named pcm.pch and a precompiled types file named StdAfx.obj. ///////////////////////////////////////////////////////////////////////////// Other notes: AppWizard uses "TODO:" comments to indicate parts of the source code you should add to or customize. ///////////////////////////////////////////////////////////////////////////// pcm-202307/src/windows/restrictDriverAccess.cpp000066400000000000000000000015421445420033100215100ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2016-2022, Intel Corporation #include #include namespace pcm { #ifdef _MSC_VER #ifdef UNICODE static auto& tcerr = std::wcerr; #else static auto& tcerr = std::cerr; #endif #endif // _MSC_VER //! restrict usage of driver to system (SY) and builtin admins (BA) void restrictDriverAccess(LPCTSTR path) { try { System::Security::AccessControl::FileSecurity^ fSecurity = System::IO::File::GetAccessControl(gcnew System::String(path)); fSecurity->SetSecurityDescriptorSddlForm("O:BAG:SYD:(A;;FA;;;SY)(A;;FA;;;BA)"); System::IO::File::SetAccessControl(gcnew System::String(path), fSecurity); } catch (...) { tcerr << "Error in GetAccessControl/SetSecurityDescriptorSddlForm for " << path << " driver.\n"; } } } // namespace pcm pcm-202307/src/windows/stdafx.cpp000066400000000000000000000005621445420033100166450ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // stdafx.cpp : source file that includes just the standard includes // pcm.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file pcm-202307/src/windows/stdafx.h000066400000000000000000000010771445420033100163140ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. #define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. #endif #include #include // TODO: reference additional headers your program requires here pcm-202307/src/windows/windriver.h000066400000000000000000000171551445420033100170400ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation #ifndef WINDRIVER_HEADER #define WINDRIVER_HEADER // contact: Roman Dementiev // WARNING: This driver code is only for testing purposes, not for production use // #include #include #include #include "../cpucounters.h" namespace pcm { /*! \file windriver.h \brief Loading and unloading custom Windows MSR (Model Specific Register) Driver */ extern void restrictDriverAccess(LPCTSTR path); /*! \brief Manage custom Windows MSR (Model Specific Register) Driver The driver is required to access hardware Model Specific Registers (MSRs) under Windows. Currently only 64-bit Windows 7 has been tested. */ class Driver { SC_HANDLE hSCManager{}; SC_HANDLE hService{}; SERVICE_STATUS ss{}; public: static tstring msrLocalPath() { tstring driverPath; DWORD driverPathLen = 1; DWORD gcdReturn = 0; do { if (0 != gcdReturn) { driverPathLen = gcdReturn; } driverPath.resize(driverPathLen); gcdReturn = GetCurrentDirectory(driverPathLen, &driverPath[0]); } while (0 != gcdReturn && driverPathLen < gcdReturn); removeNullTerminator(driverPath); return driverPath + TEXT("\\msr.sys"); } Driver() : Driver(TEXT("c:\\windows\\system32\\msr.sys")) { } Driver(const tstring& driverPath) : Driver(driverPath, TEXT("PCM MSR"), TEXT("PCM MSR Driver")) { } Driver(const tstring& driverPath, const tstring& driverName, const tstring& driverDescription) : driverPath_(setConfigValue(TEXT("DriverPath"), driverPath)), driverName_(setConfigValue(TEXT("DriverName"), driverName)), driverDescription_(setConfigValue(TEXT("DriverDescription"), driverDescription)) { } const tstring& driverPath() const { return driverPath_; } /*! \brief Installs and loads the driver Installs the driver if not installed and then loads it. \param driverPath full path to the driver \return true iff driver start up was successful */ bool start() { hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (hSCManager) { hService = CreateService(hSCManager, &driverName_[0], &driverDescription_[0], SERVICE_START | DELETE | SERVICE_STOP, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, &driverPath_[0], NULL, NULL, NULL, NULL, NULL); if (!hService) { hService = OpenService(hSCManager, &driverName_[0], SERVICE_START | DELETE | SERVICE_STOP); } if (hService) { if (0 != StartService(hService, 0, NULL)) { tstring convDriverName(&driverName_[0]); tstring driverPath = TEXT("\\\\.\\") + convDriverName; restrictDriverAccess(driverPath.c_str()); return true; } DWORD err = GetLastError(); if (err == ERROR_SERVICE_ALREADY_RUNNING) return true; std::wcerr << "Starting MSR service failed with error " << err << " "; const TCHAR * errorStr = _com_error(err).ErrorMessage(); if (errorStr) std::wcerr << errorStr << "\n"; ControlService(hService, SERVICE_CONTROL_STOP, &ss); // DeleteService(hService); CloseServiceHandle(hService); } else { std::wcerr << "Opening service manager failed with error " << GetLastError() << " "; const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage(); if (errorStr) std::wcerr << errorStr << "\n"; } CloseServiceHandle(hSCManager); } else { std::wcerr << "Opening service manager failed with error " << GetLastError() << " "; const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage(); if (errorStr) std::wcerr << errorStr << "\n"; } #ifndef NO_WINRING // In cases where loading the WinRing0 driver is not desirable as a fallback to MSR.sys, add -DNO_WINRING to compile command to remove ability to load driver (will also remove initWinRing0Lib function) std::cerr << "Trying to load winring0.dll/winring0.sys driver...\n"; if(PCM::initWinRing0Lib()) { std::cerr << "Using winring0.dll/winring0.sys driver.\n\n"; return true; } else { std::cerr << "Failed to load winring0.dll/winring0.sys driver.\n\n"; } #endif // NO_WINRING return false; } //! \brief Stop and unload the driver void stop() { hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (hSCManager) { hService = OpenService(hSCManager, &driverName_[0], SERVICE_START | DELETE | SERVICE_STOP); if (hService) { ControlService(hService, SERVICE_CONTROL_STOP, &ss); CloseServiceHandle(hService); } CloseServiceHandle(hSCManager); } else { std::wcerr << "Opening service manager failed with error " << GetLastError() << " "; const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage(); if (errorStr) std::wcerr << errorStr; } } /*! \brief Uninstall the driver Uninstalls the driver. For successeful uninstallation you need to reboot the system after calling this method. */ void uninstall() { hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (hSCManager) { hService = OpenService(hSCManager, &driverName_[0], SERVICE_START | DELETE | SERVICE_STOP); if (hService) { ControlService(hService, SERVICE_CONTROL_STOP, &ss); DeleteService(hService); CloseServiceHandle(hService); } CloseServiceHandle(hSCManager); } else { std::wcerr << "Opening service manager failed with error " << GetLastError() << " "; const TCHAR * errorStr = _com_error(GetLastError()).ErrorMessage(); if (errorStr) std::wcerr << errorStr; } } private: static tstring setConfigValue(LPCTSTR key, const tstring& defaultValue) { tstring regRead; DWORD regLen = 1 * sizeof(TCHAR); DWORD regRes = ERROR_FILE_NOT_FOUND; // Safe error to start with in case key doesn't exist HKEY hKey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\pcm"), NULL, KEY_READ, &hKey)) { do { regRead.resize(regLen / sizeof(TCHAR)); regRes = RegQueryValueEx(hKey, key, NULL, NULL, (LPBYTE)®Read[0], ®Len); } while (ERROR_MORE_DATA == regRes); RegCloseKey(hKey); } removeNullTerminator(regRead); return ERROR_SUCCESS == regRes ? regRead : defaultValue; } static void removeNullTerminator(tstring& s) { if (!s.empty() && s.back() == '\0') { s.pop_back(); } } const tstring driverName_; const tstring driverPath_; const tstring driverDescription_; }; } // namespace pcm #endif pcm-202307/src/winpmem/000077500000000000000000000000001445420033100146275ustar00rootroot00000000000000pcm-202307/src/winpmem/LICENSE.txt000066400000000000000000000261441445420033100164610ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2012 Michael Cohen 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.pcm-202307/src/winpmem/winpmem.cpp000066400000000000000000000116301445420033100170100ustar00rootroot00000000000000// SPDX-License-Identifier: BSD-3-Clause // Copyright (c) 2009-2022, Intel Corporation /* Copyright 2012 Michael Cohen 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. */ /******************************************************************** This is a single binary memory imager for Windows. Supported systems: - Windows XPSP2 to Windows 8 inclusive, both 32 bit and 64 bit. *********************************************************************/ #include "winpmem.h" #ifdef PCM_EXPORTS #define PCM_API __declspec(dllexport) #else #define PCM_API #endif namespace pcm { extern PCM_API void restrictDriverAccess(LPCTSTR path); int WinPmem::set_acquisition_mode(__int32 mode) { DWORD size; // Set the acquisition mode. if(!DeviceIoControl(fd_, PMEM_CTRL_IOCTRL, &mode, 4, NULL, 0, &size, NULL)) { LogError(TEXT("Failed to set acquisition mode.\n")); return -1; }; return 1; }; int WinPmem::toggle_write_mode() { DWORD size; // Set the acquisition mode. if (!DeviceIoControl(fd_, PMEM_WRITE_ENABLE, NULL, 0, NULL, 0, &size, NULL)) { LogError(TEXT("INFO: winpmem driver does not support write mode.\n")); return -1; }; return 1; }; WinPmem::WinPmem(): suppress_output(FALSE), fd_(INVALID_HANDLE_VALUE), out_fd_(INVALID_HANDLE_VALUE), service_name(PMEM_SERVICE_NAME) { _tcscpy_s(last_error, TEXT("")); max_physical_memory_ = 0; } WinPmem::~WinPmem() { if (fd_ != INVALID_HANDLE_VALUE) { CloseHandle(fd_); } } void WinPmem::LogError(const TCHAR *message) { _tcsncpy_s(last_error, message, sizeof(last_error)); if (suppress_output) return; _tprintf(TEXT("%s"), message); }; void WinPmem::Log(const TCHAR *message, ...) { if (suppress_output) return; va_list ap; va_start(ap, message); _vtprintf(message, ap); va_end(ap); }; // Roman Dementiev (Intel): added delete_driver option (default is true) int WinPmem::install_driver(bool delete_driver) { SC_HANDLE scm, service; int status = -1; // Try to load the driver from the resource section. if (load_driver_() < 0) goto error; uninstall_driver(); scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (!scm) { LogError(TEXT("Can not open SCM. Are you administrator?")); goto error; } service = CreateService(scm, service_name, service_name, SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driver_filename, NULL, NULL, NULL, NULL, NULL); if (GetLastError() == ERROR_SERVICE_EXISTS) { CloseServiceHandle(service); service = OpenService(scm, service_name, SERVICE_ALL_ACCESS); } if (!service) { CloseServiceHandle(scm); goto error; }; if (!StartService(service, 0, NULL)) { if (GetLastError() != ERROR_SERVICE_ALREADY_RUNNING) { LogError(TEXT("Error: StartService(), Cannot start the driver.\n")); goto service_error; } } Log(TEXT("Loaded Driver %s.\n"), driver_filename); fd_ = CreateFile(TEXT("\\\\.\\") TEXT(PMEM_DEVICE_NAME), // Write is needed for IOCTL. GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(fd_ == INVALID_HANDLE_VALUE) { LogError(TEXT("Can not open raw device.")); status = -1; } else status = 1; service_error: CloseServiceHandle(service); CloseServiceHandle(scm); if(status == 1) restrictDriverAccess(TEXT("\\\\.\\") TEXT(PMEM_DEVICE_NAME)); if(delete_driver) DeleteFile(driver_filename); error: return status; } int WinPmem::uninstall_driver() { SC_HANDLE scm, service; SERVICE_STATUS ServiceStatus; scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (!scm) return 0; service = OpenService(scm, service_name, SERVICE_ALL_ACCESS); if (service) { ControlService(service, SERVICE_CONTROL_STOP, &ServiceStatus); }; DeleteService(service); CloseServiceHandle(service); Log(TEXT("Driver Unloaded.\n")); CloseServiceHandle(scm); return 1; } } // namespace pcm pcm-202307/src/winpmem/winpmem.h000066400000000000000000000050221445420033100164530ustar00rootroot00000000000000#include "windows.h" #include "stdio.h" #include "tchar.h" namespace pcm { // Executable version. static TCHAR version[] = TEXT("1.3. Built ") TEXT(__DATE__); #define PMEM_DEVICE_NAME "pmem" #define PMEM_SERVICE_NAME TEXT("winpmem") #define PAGE_SIZE 0x1000 class WinPmem { WinPmem(const WinPmem&) = delete; WinPmem& operator = (const WinPmem&) = delete; public: WinPmem(); virtual ~WinPmem(); virtual int install_driver(bool delete_driver = true); virtual int uninstall_driver(); virtual int set_acquisition_mode(__int32 mode); virtual int toggle_write_mode(); template void read(__int64 start, T & result) { LARGE_INTEGER large_start; DWORD bytes_read = 0; large_start.QuadPart = start; if (0xFFFFFFFF == SetFilePointer(fd_, (LONG)large_start.LowPart, &large_start.HighPart, FILE_BEGIN)) { LogError(TEXT("Failed to seek in the pmem device.\n")); return; } if (!ReadFile(fd_, &result, (DWORD)sizeof(T), &bytes_read, NULL)) { LogError(TEXT("Failed to read memory.")); } } template void write(__int64 start, T val) { LARGE_INTEGER large_start; DWORD bytes_written = 0; large_start.QuadPart = start; if (0xFFFFFFFF == SetFilePointer(fd_, (LONG)large_start.LowPart, &large_start.HighPart, FILE_BEGIN)) { LogError(TEXT("Failed to seek in the pmem device.\n")); return; } if (!WriteFile(fd_, &val, (DWORD)sizeof(T), &bytes_written, NULL)) { LogError(TEXT("Failed to write memory.")); } } // This is set if output should be suppressed (e.g. if we pipe the // image to the STDOUT). int suppress_output; TCHAR last_error[1024]; protected: virtual int load_driver_() = 0; virtual void LogError(const TCHAR *message); virtual void Log(const TCHAR *message, ...); // The file handle to the pmem device. HANDLE fd_; // The file handle to the image file. HANDLE out_fd_; TCHAR *service_name; TCHAR driver_filename[MAX_PATH]; // This is the maximum size of memory calculated. __int64 max_physical_memory_; }; // This is the filename of the driver we drop. static TCHAR driver_filename[MAX_PATH]; // ioctl to get memory ranges from our driver. #define PMEM_CTRL_IOCTRL CTL_CODE(0x22, 0x101, 3, 3) #define PMEM_WRITE_ENABLE CTL_CODE(0x22, 0x102, 3, 3) #define PMEM_INFO_IOCTRL CTL_CODE(0x22, 0x103, 3, 3) // Available modes #define PMEM_MODE_IOSPACE 0 #define PMEM_MODE_PHYSICAL 1 } // namespace pcmpcm-202307/src/winring0/000077500000000000000000000000001445420033100147105ustar00rootroot00000000000000pcm-202307/src/winring0/COPYRIGHT.txt000066400000000000000000000023451445420033100170250ustar00rootroot00000000000000Copyright (c) 2007-2009 OpenLibSys.org. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pcm-202307/src/winring0/OlsApi.h000066400000000000000000000461341445420033100162600ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Author : hiyohiyo // Mail : hiyohiyo@crystalmark.info // Web : http://openlibsys.org/ // License : The modified BSD license // // Copyright 2007-2009 OpenLibSys.org. All rights reserved. //----------------------------------------------------------------------------- // for WinRing0 1.3.x #pragma once /****************************************************************************** ** ** DLL Information ** ******************************************************************************/ //----------------------------------------------------------------------------- // GetDllStatus //----------------------------------------------------------------------------- DWORD // DLL Status, defined OLS_DLL_**** WINAPI GetDllStatus(); //----------------------------------------------------------------------------- // GetDllVersion //----------------------------------------------------------------------------- DWORD // DLL Version, defined OLS_VERSION WINAPI GetDllVersion( PBYTE major, // major version PBYTE minor, // minor version PBYTE revision, // revision PBYTE release // release/build ); //----------------------------------------------------------------------------- // GetDriverVersion //----------------------------------------------------------------------------- DWORD // Device Driver Version, defined OLS_DRIVER_VERSION WINAPI GetDriverVersion( PBYTE major, // major version PBYTE minor, // minor version PBYTE revision, // revision PBYTE release // release/build ); //----------------------------------------------------------------------------- // GetDriverType //----------------------------------------------------------------------------- DWORD // Device Driver Type, defined OLS_DRIVER_TYPE_**** WINAPI GetDriverType(); //----------------------------------------------------------------------------- // InitializeOls //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI InitializeOls(); //----------------------------------------------------------------------------- // DeinitializeOls //----------------------------------------------------------------------------- VOID WINAPI DeinitializeOls(); /****************************************************************************** ** ** CPU ** ******************************************************************************/ //----------------------------------------------------------------------------- // IsCpuid //----------------------------------------------------------------------------- BOOL // TRUE: support CPUID instruction, FALSE: not support CPUID instruction WINAPI IsCpuid(); //----------------------------------------------------------------------------- // IsMsr //----------------------------------------------------------------------------- BOOL // TRUE: support MSR(Model-Specific Register), FALSE: not support MSR WINAPI IsMsr(); //----------------------------------------------------------------------------- // IsTsc //----------------------------------------------------------------------------- BOOL // TRUE: support TSC(Time Stamp Counter), FALSE: not support TSC WINAPI IsTsc(); //----------------------------------------------------------------------------- // Rdmsr //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI Rdmsr( DWORD index, // MSR index PDWORD eax, // bit 0-31 PDWORD edx // bit 32-63 ); //----------------------------------------------------------------------------- // RdmsrTx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI RdmsrTx( DWORD index, // MSR index PDWORD eax, // bit 0-31 PDWORD edx, // bit 32-63 DWORD_PTR threadAffinityMask ); //----------------------------------------------------------------------------- // RdmsrPx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI RdmsrPx( DWORD index, // MSR index PDWORD eax, // bit 0-31 PDWORD edx, // bit 32-63 DWORD_PTR processAffinityMask ); //----------------------------------------------------------------------------- // Wrmsr //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI Wrmsr( DWORD index, // MSR index DWORD eax, // bit 0-31 DWORD edx // bit 32-63 ); //----------------------------------------------------------------------------- // WrmsrTx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WrmsrTx( DWORD index, // MSR index DWORD eax, // bit 0-31 DWORD edx, // bit 32-63 DWORD_PTR threadAffinityMask ); //----------------------------------------------------------------------------- // WrmsrPx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WrmsrPx( DWORD index, // MSR index DWORD eax, // bit 0-31 DWORD edx, // bit 32-63 DWORD_PTR processAffinityMask ); //----------------------------------------------------------------------------- // Rdpmc //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI Rdpmc( DWORD index, // PMC index PDWORD eax, // bit 0-31 PDWORD edx // bit 32-63 ); //----------------------------------------------------------------------------- // RdmsrTx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI RdpmcTx( DWORD index, // PMC index PDWORD eax, // bit 0-31 PDWORD edx, // bit 32-63 DWORD_PTR threadAffinityMask ); //----------------------------------------------------------------------------- // RdmsrPx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI RdpmcPx( DWORD index, // PMC index PDWORD eax, // bit 0-31 PDWORD edx, // bit 32-63 DWORD_PTR processAffinityMask ); //----------------------------------------------------------------------------- // Cpuid //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI Cpuid( DWORD index, // CPUID index PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx ); //----------------------------------------------------------------------------- // CpuidTx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI CpuidTx( DWORD index, // CPUID index PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx, DWORD_PTR threadAffinityMask ); //----------------------------------------------------------------------------- // CpuidPx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI CpuidPx( DWORD index, // CPUID index PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx, DWORD_PTR processAffinityMask ); //----------------------------------------------------------------------------- // Rdtsc //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI Rdtsc( PDWORD eax, // bit 0-31 PDWORD edx // bit 32-63 ); //----------------------------------------------------------------------------- // RdmsrTx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI RdtscTx( PDWORD eax, // bit 0-31 PDWORD edx, // bit 32-63 DWORD_PTR threadAffinityMask ); //----------------------------------------------------------------------------- // RdmsrPx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI RdtscPx( PDWORD eax, // bit 0-31 PDWORD edx, // bit 32-63 DWORD_PTR processAffinityMask ); //----------------------------------------------------------------------------- // Hlt //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI Hlt(); //----------------------------------------------------------------------------- // HltTx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI HltTx( DWORD_PTR threadAffinityMask ); //----------------------------------------------------------------------------- // HltPx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI HltPx( DWORD_PTR processAffinityMask ); /****************************************************************************** ** ** I/O ** ******************************************************************************/ //----------------------------------------------------------------------------- // ReadIoPortByte //----------------------------------------------------------------------------- BYTE // Read Value WINAPI ReadIoPortByte( WORD port // I/O port address ); //----------------------------------------------------------------------------- // ReadIoPortWord //----------------------------------------------------------------------------- WORD // Read Value WINAPI ReadIoPortWord( WORD port // I/O port address ); //----------------------------------------------------------------------------- // ReadIoPortDword //----------------------------------------------------------------------------- DWORD // Read Value WINAPI ReadIoPortDword( WORD port // I/O port address ); //----------------------------------------------------------------------------- // ReadIoPortByteEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI ReadIoPortByteEx( WORD port, // I/O port address PBYTE value // Read Value ); //----------------------------------------------------------------------------- // ReadIoPortWordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI ReadIoPortWordEx( WORD port, // I/O port address PWORD value // Read Value ); //----------------------------------------------------------------------------- // ReadIoPortDwordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI ReadIoPortDwordEx( WORD port, // I/O port address PDWORD value // Read Value ); //----------------------------------------------------------------------------- // WriteIoPortByte //----------------------------------------------------------------------------- VOID WINAPI WriteIoPortByte( WORD port, // I/O port address BYTE value // Write Value ); //----------------------------------------------------------------------------- // WriteIoPortDword //----------------------------------------------------------------------------- VOID WINAPI WriteIoPortDword( WORD port, // I/O port address DWORD value // Write Value ); //----------------------------------------------------------------------------- // WriteIoPortWord //----------------------------------------------------------------------------- VOID WINAPI WriteIoPortWord( WORD port, // I/O port address WORD value // Write Value ); //----------------------------------------------------------------------------- // WriteIoPortByteEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WriteIoPortByteEx( WORD port, // I/O port address BYTE value // Write Value ); //----------------------------------------------------------------------------- // WriteIoPortWordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WriteIoPortWordEx( WORD port, // I/O port address WORD value // Write Value ); //----------------------------------------------------------------------------- // WriteIoPortDwordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WriteIoPortDwordEx( WORD port, // I/O port address DWORD value // Write Value ); /****************************************************************************** ** ** PCI ** ******************************************************************************/ // pciAddress // 0- 2: Function Number // 3- 7: Device Number // 8-15: PCI Bus Number // 16-31: Reserved // 0xFFFFFFFF : Error //----------------------------------------------------------------------------- // SetPciMaxBusNo //----------------------------------------------------------------------------- VOID WINAPI SetPciMaxBusIndex( BYTE max // Max PCI Bus to Scan ); //----------------------------------------------------------------------------- // ReadPciConfigByte //----------------------------------------------------------------------------- BYTE // Read Value WINAPI ReadPciConfigByte( DWORD pciAddress, // PCI Device Address BYTE regAddress // Configuration Address 0-255 ); //----------------------------------------------------------------------------- // ReadPciConfigWord //----------------------------------------------------------------------------- WORD // Read Value WINAPI ReadPciConfigWord( DWORD pciAddress, // PCI Device Address BYTE regAddress // Configuration Address 0-255 ); //----------------------------------------------------------------------------- // ReadPciConfigDword //----------------------------------------------------------------------------- DWORD // Read Value WINAPI ReadPciConfigDword( DWORD pciAddress, // PCI Device Address BYTE regAddress // Configuration Address 0-255 ); //----------------------------------------------------------------------------- // ReadPciConfigByteEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI ReadPciConfigByteEx( DWORD pciAddress, // PCI Device Address DWORD regAddress, // Configuration Address 0-whatever PBYTE value // Read Value ); //----------------------------------------------------------------------------- // ReadPciConfigWordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI ReadPciConfigWordEx( DWORD pciAddress, // PCI Device Address DWORD regAddress, // Configuration Address 0-whatever PWORD value // Read Value ); //----------------------------------------------------------------------------- // ReadPciConfigDwordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI ReadPciConfigDwordEx( DWORD pciAddress, // PCI Device Address DWORD regAddress, // Configuration Address 0-whatever PDWORD value // Read Value ); //----------------------------------------------------------------------------- // WritePciConfigByte //----------------------------------------------------------------------------- VOID WINAPI WritePciConfigByte( DWORD pciAddress, // PCI Device Address BYTE regAddress, // Configuration Address 0-255 BYTE value // Write Value ); //----------------------------------------------------------------------------- // WritePciConfigWord //----------------------------------------------------------------------------- VOID WINAPI WritePciConfigWord( DWORD pciAddress, // PCI Device Address BYTE regAddress, // Configuration Address 0-255 WORD value // Write Value ); //----------------------------------------------------------------------------- // WritePciConfigDword //----------------------------------------------------------------------------- VOID WINAPI WritePciConfigDword( DWORD pciAddress, // PCI Device Address BYTE regAddress, // Configuration Address 0-255 DWORD value // Write Value ); //----------------------------------------------------------------------------- // WritePciConfigByteEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WritePciConfigByteEx( DWORD pciAddress, // PCI Device Address DWORD regAddress, // Configuration Address 0-whatever BYTE value // Write Value ); //----------------------------------------------------------------------------- // WritePciConfigWordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WritePciConfigWordEx( DWORD pciAddress, // PCI Device Address DWORD regAddress, // Configuration Address 0-whatever WORD value // Write Value ); //----------------------------------------------------------------------------- // WritePciConfigDwordEx //----------------------------------------------------------------------------- BOOL // TRUE: success, FALSE: failure WINAPI WritePciConfigDwordEx( DWORD pciAddress, // PCI Device Address DWORD regAddress, // Configuration Address 0-whatever DWORD value // Write Value ); //----------------------------------------------------------------------------- // FindPciDeviceById //----------------------------------------------------------------------------- DWORD // pciAddress, 0xFFFFFFFF: failure WINAPI FindPciDeviceById( WORD vendorId, // Vendor ID WORD deviceId, // Device ID BYTE index // Index ); //----------------------------------------------------------------------------- // FindPciDeviceByClass //----------------------------------------------------------------------------- DWORD // pciAddress, 0xFFFFFFFF: failure WINAPI FindPciDeviceByClass( BYTE baseClass, // Base Class BYTE subClass, // Sub Class BYTE programIf, // Program Interface BYTE index // Index ); /****************************************************************************** ** ** Memory (Special API) ** ******************************************************************************/ #ifdef _PHYSICAL_MEMORY_SUPPORT //----------------------------------------------------------------------------- // ReadDmiMemory //----------------------------------------------------------------------------- DWORD // Read size(byte), 0: failure WINAPI ReadDmiMemory( PBYTE buffer, // Buffer DWORD count, // Count DWORD unitSize // Unit Size (BYTE, WORD, DWORD) ); //----------------------------------------------------------------------------- // ReadPhysicalMemory //----------------------------------------------------------------------------- DWORD // Read size(byte), 0: failure WINAPI ReadPhysicalMemory( DWORD_PTR address, // Physical Memory Address PBYTE buffer, // Buffer DWORD count, // Count DWORD unitSize // Unit Size (BYTE, WORD, DWORD) ); //----------------------------------------------------------------------------- // WritePhysicalMemory //----------------------------------------------------------------------------- DWORD // Write size(byte), 0: failure WINAPI WritePhysicalMemory( DWORD_PTR address, // Physical Memory Address PBYTE buffer, // Buffer DWORD count, // Count DWORD unitSize // Unit Size (BYTE, WORD, DWORD) ); #endifpcm-202307/src/winring0/OlsApiInit.h000066400000000000000000000243041445420033100170770ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Author : hiyohiyo // Mail : hiyohiyo@crystalmark.info // Web : http://openlibsys.org/ // License : The modified BSD license // // Copyright 2007-2009 OpenLibSys.org. All rights reserved. //----------------------------------------------------------------------------- // for WinRing0 1.3.x #pragma once #include "OlsDef.h" #include "OlsApiInitDef.h" //----------------------------------------------------------------------------- // // Prototypes // //----------------------------------------------------------------------------- BOOL InitOpenLibSys(HMODULE *hModule); BOOL DeinitOpenLibSys(HMODULE *hModule); //----------------------------------------------------------------------------- // // Functions // //----------------------------------------------------------------------------- // DLL _GetDllStatus GetDllStatus = NULL; _GetDllVersion GetDllVersion = NULL; _GetDriverVersion GetDriverVersion = NULL; _GetDriverType GetDriverType = NULL; _InitializeOls InitializeOls = NULL; _DeinitializeOls DeinitializeOls = NULL; // CPU _IsCpuid IsCpuid = NULL; _IsMsr IsMsr = NULL; _IsTsc IsTsc = NULL; _Hlt Hlt = NULL; _Rdmsr Rdmsr = NULL; _Wrmsr Wrmsr = NULL; _Rdpmc Rdpmc = NULL; _Cpuid Cpuid = NULL; _Rdtsc Rdtsc = NULL; _HltTx HltTx = NULL; _RdmsrTx RdmsrTx = NULL; _WrmsrTx WrmsrTx = NULL; _RdpmcTx RdpmcTx = NULL; _CpuidTx CpuidTx = NULL; _RdtscTx RdtscTx = NULL; _HltPx HltPx = NULL; _RdmsrPx RdmsrPx = NULL; _WrmsrPx WrmsrPx = NULL; _RdpmcPx RdpmcPx = NULL; _CpuidPx CpuidPx = NULL; _RdtscPx RdtscPx = NULL; // I/O _ReadIoPortByte ReadIoPortByte = NULL; _ReadIoPortWord ReadIoPortWord = NULL; _ReadIoPortDword ReadIoPortDword = NULL; _ReadIoPortByteEx ReadIoPortByteEx = NULL; _ReadIoPortWordEx ReadIoPortWordEx = NULL; _ReadIoPortDwordEx ReadIoPortDwordEx = NULL; _WriteIoPortByte WriteIoPortByte = NULL; _WriteIoPortWord WriteIoPortWord = NULL; _WriteIoPortDword WriteIoPortDword = NULL; _WriteIoPortByteEx WriteIoPortByteEx = NULL; _WriteIoPortWordEx WriteIoPortWordEx = NULL; _WriteIoPortDwordEx WriteIoPortDwordEx = NULL; // PCI _SetPciMaxBusIndex SetPciMaxBusIndex = NULL; _ReadPciConfigByte ReadPciConfigByte = NULL; _ReadPciConfigWord ReadPciConfigWord = NULL; _ReadPciConfigDword ReadPciConfigDword = NULL; _ReadPciConfigByteEx ReadPciConfigByteEx = NULL; _ReadPciConfigWordEx ReadPciConfigWordEx = NULL; _ReadPciConfigDwordEx ReadPciConfigDwordEx = NULL; _WritePciConfigByte WritePciConfigByte = NULL; _WritePciConfigWord WritePciConfigWord = NULL; _WritePciConfigDword WritePciConfigDword = NULL; _WritePciConfigByteEx WritePciConfigByteEx = NULL; _WritePciConfigWordEx WritePciConfigWordEx = NULL; _WritePciConfigDwordEx WritePciConfigDwordEx = NULL; _FindPciDeviceById FindPciDeviceById = NULL; _FindPciDeviceByClass FindPciDeviceByClass = NULL; // Memory #ifdef _PHYSICAL_MEMORY_SUPPORT _ReadDmiMemory ReadDmiMemory = NULL; _ReadPhysicalMemory ReadPhysicalMemory = NULL; _WritePhysicalMemory WritePhysicalMemory = NULL; #endif #ifdef _OPEN_LIB_SYS #ifdef _UNICODE #define GetOlsString GetOlsStringW #else #define GetOlsString GetOlsStringA #endif _InstallOpenLibSys InstallOpenLibSys = NULL; _UninstallOpenLibSys UninstallOpenLibSys = NULL; _GetDriverStatus GetDriverStatus = NULL; _GetOlsStringA GetOlsStringA = NULL; _GetOlsStringW GetOlsStringW = NULL; _GetOlsValue GetOlsValue = NULL; _SetOlsValue SetOlsValue = NULL; #endif //----------------------------------------------------------------------------- // // Initialize // //----------------------------------------------------------------------------- BOOL InitOpenLibSys(HMODULE *hModule) { #ifdef _M_X64 *hModule = LoadLibrary(_T("WinRing0x64.dll")); #else *hModule = LoadLibrary(_T("WinRing0.dll")); #endif if(*hModule == NULL) { return FALSE; } //----------------------------------------------------------------------------- // GetProcAddress //----------------------------------------------------------------------------- // DLL GetDllStatus = (_GetDllStatus) GetProcAddress (*hModule, "GetDllStatus"); GetDllVersion = (_GetDllVersion) GetProcAddress (*hModule, "GetDllVersion"); GetDriverVersion = (_GetDriverVersion) GetProcAddress (*hModule, "GetDriverVersion"); GetDriverType = (_GetDriverType) GetProcAddress (*hModule, "GetDriverType"); InitializeOls = (_InitializeOls) GetProcAddress (*hModule, "InitializeOls"); DeinitializeOls = (_DeinitializeOls) GetProcAddress (*hModule, "DeinitializeOls"); // CPU IsCpuid = (_IsCpuid) GetProcAddress (*hModule, "IsCpuid"); IsMsr = (_IsMsr) GetProcAddress (*hModule, "IsMsr"); IsTsc = (_IsTsc) GetProcAddress (*hModule, "IsTsc"); Hlt = (_Hlt) GetProcAddress (*hModule, "Hlt"); Rdmsr = (_Rdmsr) GetProcAddress (*hModule, "Rdmsr"); Wrmsr = (_Wrmsr) GetProcAddress (*hModule, "Wrmsr"); Rdpmc = (_Rdpmc) GetProcAddress (*hModule, "Rdpmc"); Cpuid = (_Cpuid) GetProcAddress (*hModule, "Cpuid"); Rdtsc = (_Rdtsc) GetProcAddress (*hModule, "Rdtsc"); HltTx = (_HltTx) GetProcAddress (*hModule, "HltTx"); RdmsrTx = (_RdmsrTx) GetProcAddress (*hModule, "RdmsrTx"); WrmsrTx = (_WrmsrTx) GetProcAddress (*hModule, "WrmsrTx"); RdpmcTx = (_RdpmcTx) GetProcAddress (*hModule, "RdpmcTx"); CpuidTx = (_CpuidTx) GetProcAddress (*hModule, "CpuidTx"); RdtscTx = (_RdtscTx) GetProcAddress (*hModule, "RdtscTx"); HltPx = (_HltPx) GetProcAddress (*hModule, "HltPx"); RdmsrPx = (_RdmsrPx) GetProcAddress (*hModule, "RdmsrPx"); WrmsrPx = (_WrmsrPx) GetProcAddress (*hModule, "WrmsrPx"); RdpmcPx = (_RdpmcPx) GetProcAddress (*hModule, "RdpmcPx"); CpuidPx = (_CpuidPx) GetProcAddress (*hModule, "CpuidPx"); RdtscPx = (_RdtscPx) GetProcAddress (*hModule, "RdtscPx"); // I/O ReadIoPortByte = (_ReadIoPortByte) GetProcAddress (*hModule, "ReadIoPortByte"); ReadIoPortWord = (_ReadIoPortWord) GetProcAddress (*hModule, "ReadIoPortWord"); ReadIoPortDword = (_ReadIoPortDword) GetProcAddress (*hModule, "ReadIoPortDword"); ReadIoPortByteEx = (_ReadIoPortByteEx) GetProcAddress (*hModule, "ReadIoPortByteEx"); ReadIoPortWordEx = (_ReadIoPortWordEx) GetProcAddress (*hModule, "ReadIoPortWordEx"); ReadIoPortDwordEx = (_ReadIoPortDwordEx) GetProcAddress (*hModule, "ReadIoPortDwordEx"); WriteIoPortByte = (_WriteIoPortByte) GetProcAddress (*hModule, "WriteIoPortByte"); WriteIoPortWord = (_WriteIoPortWord) GetProcAddress (*hModule, "WriteIoPortWord"); WriteIoPortDword = (_WriteIoPortDword) GetProcAddress (*hModule, "WriteIoPortDword"); WriteIoPortByteEx = (_WriteIoPortByteEx) GetProcAddress (*hModule, "WriteIoPortByteEx"); WriteIoPortWordEx = (_WriteIoPortWordEx) GetProcAddress (*hModule, "WriteIoPortWordEx"); WriteIoPortDwordEx = (_WriteIoPortDwordEx) GetProcAddress (*hModule, "WriteIoPortDwordEx"); // PCI SetPciMaxBusIndex = (_SetPciMaxBusIndex) GetProcAddress (*hModule, "SetPciMaxBusIndex"); ReadPciConfigByte = (_ReadPciConfigByte) GetProcAddress (*hModule, "ReadPciConfigByte"); ReadPciConfigWord = (_ReadPciConfigWord) GetProcAddress (*hModule, "ReadPciConfigWord"); ReadPciConfigDword = (_ReadPciConfigDword) GetProcAddress (*hModule, "ReadPciConfigDword"); ReadPciConfigByteEx = (_ReadPciConfigByteEx) GetProcAddress (*hModule, "ReadPciConfigByteEx"); ReadPciConfigWordEx = (_ReadPciConfigWordEx) GetProcAddress (*hModule, "ReadPciConfigWordEx"); ReadPciConfigDwordEx = (_ReadPciConfigDwordEx) GetProcAddress (*hModule, "ReadPciConfigDwordEx"); WritePciConfigByte = (_WritePciConfigByte) GetProcAddress (*hModule, "WritePciConfigByte"); WritePciConfigWord = (_WritePciConfigWord) GetProcAddress (*hModule, "WritePciConfigWord"); WritePciConfigDword = (_WritePciConfigDword) GetProcAddress (*hModule, "WritePciConfigDword"); WritePciConfigByteEx = (_WritePciConfigByteEx) GetProcAddress (*hModule, "WritePciConfigByteEx"); WritePciConfigWordEx = (_WritePciConfigWordEx) GetProcAddress (*hModule, "WritePciConfigWordEx"); WritePciConfigDwordEx = (_WritePciConfigDwordEx)GetProcAddress (*hModule, "WritePciConfigDwordEx"); FindPciDeviceById = (_FindPciDeviceById) GetProcAddress (*hModule, "FindPciDeviceById"); FindPciDeviceByClass = (_FindPciDeviceByClass) GetProcAddress (*hModule, "FindPciDeviceByClass"); // Memory #ifdef _PHYSICAL_MEMORY_SUPPORT ReadDmiMemory = (_ReadDmiMemory) GetProcAddress (*hModule, "ReadDmiMemory"); ReadPhysicalMemory = (_ReadPhysicalMemory) GetProcAddress (*hModule, "ReadPhysicalMemory"); WritePhysicalMemory = (_WritePhysicalMemory) GetProcAddress (*hModule, "WritePhysicalMemory"); #endif //----------------------------------------------------------------------------- // Check Functions //----------------------------------------------------------------------------- if(!( GetDllStatus && GetDllVersion && GetDriverVersion && GetDriverType && InitializeOls && DeinitializeOls && IsCpuid && IsMsr && IsTsc && Hlt && HltTx && HltPx && Rdmsr && RdmsrTx && RdmsrPx && Wrmsr && WrmsrTx && WrmsrPx && Rdpmc && RdpmcTx && RdpmcPx && Cpuid && CpuidTx && CpuidPx && Rdtsc && RdtscTx && RdtscPx && ReadIoPortByte && ReadIoPortWord && ReadIoPortDword && ReadIoPortByteEx && ReadIoPortWordEx && ReadIoPortDwordEx && WriteIoPortByte && WriteIoPortWord && WriteIoPortDword && WriteIoPortByteEx && WriteIoPortWordEx && WriteIoPortDwordEx && SetPciMaxBusIndex && ReadPciConfigByte && ReadPciConfigWord && ReadPciConfigDword && ReadPciConfigByteEx && ReadPciConfigWordEx && ReadPciConfigDwordEx && WritePciConfigByte && WritePciConfigWord && WritePciConfigDword && WritePciConfigByteEx && WritePciConfigWordEx && WritePciConfigDwordEx && FindPciDeviceById && FindPciDeviceByClass #ifdef _PHYSICAL_MEMORY_SUPPORT && ReadDmiMemory && ReadPhysicalMemory && WritePhysicalMemory #endif )) { return FALSE; } return InitializeOls(); } //----------------------------------------------------------------------------- // // Deinitialize // //----------------------------------------------------------------------------- BOOL DeinitOpenLibSys(HMODULE *hModule) { BOOL result = FALSE; if(*hModule == NULL) { return TRUE; } else { DeinitializeOls(); result = FreeLibrary(*hModule); *hModule = NULL; return result; } } pcm-202307/src/winring0/OlsApiInitDef.h000066400000000000000000000120761445420033100175210ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Author : hiyohiyo // Mail : hiyohiyo@crystalmark.info // Web : http://openlibsys.org/ // License : The modified BSD license // // Copyright 2007-2009 OpenLibSys.org. All rights reserved. //----------------------------------------------------------------------------- // for WinRing0 1.3.x #pragma once //----------------------------------------------------------------------------- // // Type Defines // //----------------------------------------------------------------------------- // DLL typedef DWORD (WINAPI *_GetDllStatus) (); typedef DWORD (WINAPI *_GetDllVersion) (PBYTE major, PBYTE minor, PBYTE revision, PBYTE release); typedef DWORD (WINAPI *_GetDriverVersion) (PBYTE major, PBYTE minor, PBYTE revision, PBYTE release); typedef DWORD (WINAPI *_GetDriverType) (); typedef BOOL (WINAPI *_InitializeOls) (); typedef VOID (WINAPI *_DeinitializeOls) (); // CPU typedef BOOL (WINAPI *_IsCpuid) (); typedef BOOL (WINAPI *_IsMsr) (); typedef BOOL (WINAPI *_IsTsc) (); typedef BOOL (WINAPI *_Hlt) (); typedef DWORD (WINAPI *_Rdmsr) (DWORD index, PDWORD eax, PDWORD edx); typedef DWORD (WINAPI *_Wrmsr) (DWORD index, DWORD eax, DWORD edx); typedef DWORD (WINAPI *_Rdpmc) (DWORD index, PDWORD eax, PDWORD edx); typedef DWORD (WINAPI *_Cpuid) (DWORD index, PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx); typedef DWORD (WINAPI *_Rdtsc) (PDWORD eax, PDWORD edx); typedef BOOL (WINAPI *_HltTx) (DWORD_PTR threadAffinityMask); typedef DWORD (WINAPI *_RdmsrTx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR threadAffinityMask); typedef DWORD (WINAPI *_WrmsrTx) (DWORD index, DWORD eax, DWORD edx, DWORD_PTR threadAffinityMask); typedef DWORD (WINAPI *_RdpmcTx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR threadAffinityMask); typedef DWORD (WINAPI *_CpuidTx) (DWORD index, PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx, DWORD_PTR threadAffinityMask); typedef DWORD (WINAPI *_RdtscTx) (PDWORD eax, PDWORD edx, DWORD_PTR threadAffinityMask); typedef BOOL (WINAPI *_HltPx) (DWORD_PTR processAffinityMask); typedef DWORD (WINAPI *_RdmsrPx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR processAffinityMask); typedef DWORD (WINAPI *_WrmsrPx) (DWORD index, DWORD eax, DWORD edx, DWORD_PTR processAffinityMask); typedef DWORD (WINAPI *_RdpmcPx) (DWORD index, PDWORD eax, PDWORD edx, DWORD_PTR processAffinityMask); typedef DWORD (WINAPI *_CpuidPx) (DWORD index, PDWORD eax, PDWORD ebx, PDWORD ecx, PDWORD edx, DWORD_PTR processAffinityMask); typedef DWORD (WINAPI *_RdtscPx) (PDWORD eax, PDWORD edx, DWORD_PTR processAffinityMask); // I/O typedef BYTE (WINAPI *_ReadIoPortByte) (WORD address); typedef WORD (WINAPI *_ReadIoPortWord) (WORD address); typedef DWORD (WINAPI *_ReadIoPortDword) (WORD address); typedef BOOL (WINAPI *_ReadIoPortByteEx) (WORD address, PBYTE value); typedef BOOL (WINAPI *_ReadIoPortWordEx) (WORD address, PWORD value); typedef BOOL (WINAPI *_ReadIoPortDwordEx) (WORD address, PDWORD value); typedef VOID (WINAPI *_WriteIoPortByte) (WORD address, BYTE value); typedef VOID (WINAPI *_WriteIoPortWord) (WORD address, WORD value); typedef VOID (WINAPI *_WriteIoPortDword) (WORD address, DWORD value); typedef BOOL (WINAPI *_WriteIoPortByteEx) (WORD address, BYTE value); typedef BOOL (WINAPI *_WriteIoPortWordEx) (WORD address, WORD value); typedef BOOL (WINAPI *_WriteIoPortDwordEx) (WORD address, DWORD value); // PCI typedef VOID (WINAPI *_SetPciMaxBusIndex) (BYTE max); typedef BYTE (WINAPI *_ReadPciConfigByte) (DWORD pciAddress, BYTE regAddress); typedef WORD (WINAPI *_ReadPciConfigWord) (DWORD pciAddress, BYTE regAddress); typedef DWORD (WINAPI *_ReadPciConfigDword) (DWORD pciAddress, BYTE regAddress); typedef BOOL (WINAPI *_ReadPciConfigByteEx) (DWORD pciAddress, DWORD regAddress, PBYTE value); typedef BOOL (WINAPI *_ReadPciConfigWordEx) (DWORD pciAddress, DWORD regAddress, PWORD value); typedef BOOL (WINAPI *_ReadPciConfigDwordEx) (DWORD pciAddress, DWORD regAddress, PDWORD value); typedef VOID (WINAPI *_WritePciConfigByte) (DWORD pciAddress, BYTE regAddress, BYTE value); typedef VOID (WINAPI *_WritePciConfigWord) (DWORD pciAddress, BYTE regAddress, WORD value); typedef VOID (WINAPI *_WritePciConfigDword) (DWORD pciAddress, BYTE regAddress, DWORD value); typedef BOOL (WINAPI *_WritePciConfigByteEx) (DWORD pciAddress, DWORD regAddress, BYTE value); typedef BOOL (WINAPI *_WritePciConfigWordEx) (DWORD pciAddress, DWORD regAddress, WORD value); typedef BOOL (WINAPI *_WritePciConfigDwordEx) (DWORD pciAddress, DWORD regAddress, DWORD value); typedef DWORD (WINAPI *_FindPciDeviceById) (WORD vendorId, WORD deviceId, BYTE index); typedef DWORD (WINAPI *_FindPciDeviceByClass) (BYTE baseClass, BYTE subClass, BYTE programIf, BYTE index); // Memory #ifdef _PHYSICAL_MEMORY_SUPPORT typedef DWORD (WINAPI *_ReadDmiMemory) (PBYTE buffer, DWORD count, DWORD unitSize); typedef DWORD (WINAPI *_ReadPhysicalMemory) (DWORD_PTR address, PBYTE buffer, DWORD count, DWORD unitSize); typedef DWORD (WINAPI *_WritePhysicalMemory) (DWORD_PTR address, PBYTE buffer, DWORD count, DWORD unitSize); #endif pcm-202307/src/winring0/OlsApiInitExt.h000066400000000000000000000053671445420033100175700ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Author : hiyohiyo // Mail : hiyohiyo@crystalmark.info // Web : http://openlibsys.org/ // License : The modified BSD license // // Copyright 2007-2009 OpenLibSys.org. All rights reserved. //----------------------------------------------------------------------------- // for WinRing0 1.3.x #pragma once #include "OlsApiInitDef.h" //----------------------------------------------------------------------------- // // Externs // //----------------------------------------------------------------------------- // DLL extern _GetDllStatus GetDllStatus; extern _GetDllVersion GetDllVersion; extern _GetDriverVersion GetDriverVersion; extern _GetDriverType GetDriverType; extern _InitializeOls InitializeOls; extern _DeinitializeOls DeinitializeOls; // CPU extern _IsCpuid IsCpuid; extern _IsMsr IsMsr; extern _IsTsc IsTsc; extern _Hlt Hlt; extern _Rdmsr Rdmsr; extern _Wrmsr Wrmsr; extern _Rdpmc Rdpmc; extern _Cpuid Cpuid; extern _Rdtsc Rdtsc; extern _HltTx HltTx; extern _RdmsrTx RdmsrTx; extern _WrmsrTx WrmsrTx; extern _RdpmcTx RdpmcTx; extern _CpuidTx CpuidTx; extern _RdtscTx RdtscTx; extern _HltPx HltPx; extern _RdmsrPx RdmsrPx; extern _WrmsrPx WrmsrPx; extern _RdpmcPx RdpmcPx; extern _CpuidPx CpuidPx; extern _RdtscPx RdtscPx; // I/O extern _ReadIoPortByte ReadIoPortByte; extern _ReadIoPortWord ReadIoPortWord; extern _ReadIoPortDword ReadIoPortDword; extern _ReadIoPortByteEx ReadIoPortByteEx; extern _ReadIoPortWordEx ReadIoPortWordEx; extern _ReadIoPortDwordEx ReadIoPortDwordEx; extern _WriteIoPortByte WriteIoPortByte; extern _WriteIoPortWord WriteIoPortWord; extern _WriteIoPortDword WriteIoPortDword; extern _WriteIoPortByteEx WriteIoPortByteEx; extern _WriteIoPortWordEx WriteIoPortWordEx; extern _WriteIoPortDwordEx WriteIoPortDwordEx; // PCI extern _SetPciMaxBusIndex SetPciMaxBusIndex; extern _ReadPciConfigByte ReadPciConfigByte; extern _ReadPciConfigWord ReadPciConfigWord; extern _ReadPciConfigDword ReadPciConfigDword; extern _ReadPciConfigByteEx ReadPciConfigByteEx; extern _ReadPciConfigWordEx ReadPciConfigWordEx; extern _ReadPciConfigDwordEx ReadPciConfigDwordEx; extern _WritePciConfigByte WritePciConfigByte; extern _WritePciConfigWord WritePciConfigWord; extern _WritePciConfigDword WritePciConfigDword; extern _WritePciConfigByteEx WritePciConfigByteEx; extern _WritePciConfigWordEx WritePciConfigWordEx; extern _WritePciConfigDwordEx WritePciConfigDwordEx; extern _FindPciDeviceById FindPciDeviceById; extern _FindPciDeviceByClass FindPciDeviceByClass; // Memory #ifdef _PHYSICAL_MEMORY_SUPPORT extern _ReadDmiMemory ReadDmiMemory; extern _ReadPhysicalMemory ReadPhysicalMemory; extern _WritePhysicalMemory WritePhysicalMemory; #endif pcm-202307/src/winring0/OlsDef.h000066400000000000000000000043431445420033100162410ustar00rootroot00000000000000//----------------------------------------------------------------------------- // Author : hiyohiyo // Mail : hiyohiyo@crystalmark.info // Web : http://openlibsys.org/ // License : The modified BSD license // // Copyright 2007 OpenLibSys.org. All rights reserved. //----------------------------------------------------------------------------- #pragma once //----------------------------------------------------------------------------- // // DLL Status Code // //----------------------------------------------------------------------------- #define OLS_DLL_NO_ERROR 0 #define OLS_DLL_UNSUPPORTED_PLATFORM 1 #define OLS_DLL_DRIVER_NOT_LOADED 2 #define OLS_DLL_DRIVER_NOT_FOUND 3 #define OLS_DLL_DRIVER_UNLOADED 4 #define OLS_DLL_DRIVER_NOT_LOADED_ON_NETWORK 5 #define OLS_DLL_UNKNOWN_ERROR 9 //----------------------------------------------------------------------------- // // Driver Type // //----------------------------------------------------------------------------- #define OLS_DRIVER_TYPE_UNKNOWN 0 #define OLS_DRIVER_TYPE_WIN_9X 1 #define OLS_DRIVER_TYPE_WIN_NT 2 #define OLS_DRIVER_TYPE_WIN_NT4 3 // Obsolete #define OLS_DRIVER_TYPE_WIN_NT_X64 4 #define OLS_DRIVER_TYPE_WIN_NT_IA64 5 // Reserved //----------------------------------------------------------------------------- // // PCI Error Code // //----------------------------------------------------------------------------- #define OLS_ERROR_PCI_BUS_NOT_EXIST (0xE0000001L) #define OLS_ERROR_PCI_NO_DEVICE (0xE0000002L) #define OLS_ERROR_PCI_WRITE_CONFIG (0xE0000003L) #define OLS_ERROR_PCI_READ_CONFIG (0xE0000004L) //----------------------------------------------------------------------------- // // Support Macros // //----------------------------------------------------------------------------- // Bus Number, Device Number and Function Number to PCI Device Address #define PciBusDevFunc(Bus, Dev, Func) ((Bus&0xFF)<<8) | ((Dev&0x1F)<<3) | (Func&7) // PCI Device Address to Bus Number #define PciGetBus(address) ((address>>8) & 0xFF) // PCI Device Address to Device Number #define PciGetDev(address) ((address>>3) & 0x1F) // PCI Device Address to Function Number #define PciGetFunc(address) (address&7) pcm-202307/tests/000077500000000000000000000000001445420033100135265ustar00rootroot00000000000000pcm-202307/tests/CMakeLists.txt000066400000000000000000000010531445420033100162650ustar00rootroot00000000000000# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2022, Intel Corporation set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/tests) if(UNIX) # daemon_alignment_test on Linux and Unix file(GLOB TEST_FILE daemon_alignment_test.cpp) add_executable(daemon_alignment_test ${TEST_FILE}) target_link_libraries(daemon_alignment_test) # PCM_STATIC + pcm_sensor = urltest if(LINUX) add_executable(urltest urltest.cpp) target_link_libraries(urltest Threads::Threads PCM_STATIC) endif(LINUX) endif(UNIX) pcm-202307/tests/daemon_alignment_test.cpp000066400000000000000000000043231445420033100205740ustar00rootroot00000000000000#include #include #include #include #include "../src/daemon/common.h" #define ALIGNMENT 64 void checkAlignment(char const * debugMessage, void* ptr) { printf("Checking: %-20s\t\t", debugMessage); uint64_t currentAlignment = (uint64_t)ptr % ALIGNMENT; if(currentAlignment != 0) { printf("Failed\n"); printf("Current alignment: %llu\n\n", (unsigned long long)currentAlignment); exit(EXIT_FAILURE); } else { printf("Passed\n"); } } int main() { printf("Testing alignment\n\n"); PCMDaemon::SharedPCMState* pcmState = (PCMDaemon::SharedPCMState*)aligned_alloc(ALIGNMENT, sizeof(PCMDaemon::SharedPCMState)); if (pcmState == nullptr) { printf("Memory allocation failed\n\n"); exit(EXIT_FAILURE); } std::fill((char*)pcmState, ((char*)pcmState) + sizeof(PCMDaemon::SharedPCMState), 0); checkAlignment("pcmState", pcmState); checkAlignment("pcm", &pcmState->pcm); checkAlignment("pcm core", &pcmState->pcm.core); checkAlignment("pcm memory", &pcmState->pcm.memory); checkAlignment("pcm qpi", &pcmState->pcm.qpi); for(uint32_t i(0); i < MAX_CPU_CORES; ++i) { checkAlignment("pcm core cores", &pcmState->pcm.core.cores[i]); } checkAlignment("pcm core energyUsed", &pcmState->pcm.core.energyUsedBySockets); for(uint32_t i(0); i < MAX_SOCKETS; ++i) { checkAlignment("pcm memory sockets", &pcmState->pcm.memory.sockets[i]); } for(uint32_t i(0); i < MAX_SOCKETS; ++i) { checkAlignment("pcm qpi incoming", &pcmState->pcm.qpi.incoming[i]); } for(uint32_t i(0); i < MAX_SOCKETS; ++i) { for(uint32_t j(0); j < QPI_MAX_LINKS; ++j) { checkAlignment("pcm qpi incoming links", &pcmState->pcm.qpi.incoming[i].links[j]); } } for(uint32_t i(0); i < MAX_SOCKETS; ++i) { checkAlignment("pcm qpi outgoing", &pcmState->pcm.qpi.outgoing[i]); } for(uint32_t i(0); i < MAX_SOCKETS; ++i) { for(uint32_t j(0); j < QPI_MAX_LINKS; ++j) { checkAlignment("pcm qpi outgoing links", &pcmState->pcm.qpi.outgoing[i].links[j]); } } free(pcmState); printf("\n------ All passed ------\n\n"); return EXIT_SUCCESS; }pcm-202307/tests/test.sh000077500000000000000000000215601445420033100150500ustar00rootroot00000000000000modprobe msr export BIN_DIR="build/bin" pushd $BIN_DIR echo Enable NMI watchdog echo 1 > /proc/sys/kernel/nmi_watchdog echo Testing pcm with PCM_NO_PERF=1 PCM_NO_PERF=1 ./pcm -r -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm" exit 1 fi echo Testing pcm with PCM_USE_UNCORE_PERF=1 PCM_USE_UNCORE_PERF=1 ./pcm -r -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm" exit 1 fi echo Testing pcm w/o env vars ./pcm -r -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm" exit 1 fi echo Testing pcm with -pid perl -e ' do {} until (0)' & test_pid="$!" ./pcm -pid $test_pid -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm" kill $perl_pid exit 1 fi kill $test_pid echo Testing pcm with PCM_KEEP_NMI_WATCHDOG=1 PCM_KEEP_NMI_WATCHDOG=1 ./pcm -r -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm" exit 1 fi echo Testing pcm with -csv ./pcm -r 0.1 -csv=pcm.csv -- sleep 5 if [ "$?" -ne "0" ]; then echo "Error in pcm" exit 1 fi echo Testing pcm-memory ./pcm-memory -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-memory" exit 1 fi echo Testing pcm-memory with csv output ./pcm-memory -csv=pcm-memory.csv -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-memory" exit 1 fi echo Testing pcm-memory with -rank ./pcm-memory -rank=1 -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-memory" exit 1 fi echo Testing pcm-memory with -rank and -csv ./pcm-memory -rank=1 -csv -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-memory" exit 1 fi echo Testing pcm-iio --list ./pcm-iio --list if [ "$?" -ne "0" ]; then echo "Error in pcm-iio" exit 1 fi echo Testing pcm-iio ./pcm-iio -i=1 if [ "$?" -ne "0" ]; then echo "Error in pcm-iio" exit 1 fi echo Testing pcm-raw ./pcm-raw -e core/config=0x30203,name=LD_BLOCKS.STORE_FORWARD/ -e cha/config=0,name=UNC_CHA_CLOCKTICKS/ -e imc/fixed,name=DRAM_CLOCKS -e thread_msr/config=0x10,config1=1 -e thread_msr/config=0x19c,config1=0 -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-raw" exit 1 fi echo Testing pcm-mmio ./pcm-mmio 0x0 if [ "$?" -ne "0" ]; then echo "Error in pcm-mmio" exit 1 fi echo Testing pcm-pcicfg ./pcm-pcicfg 0 0 0 0 0 if [ "$?" -ne "0" ]; then echo "Error in pcm-pcicfg" exit 1 fi echo Testing pcm-numa ./pcm-numa -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-numa" exit 1 fi echo Testing pcm-core ./pcm-core -e cpu/umask=0x01,event=0x0e,name=UOPS_ISSUED.STALL_CYCLES/ -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-core" exit 1 fi echo Testing c_example ./examples/c_example if [ "$?" -ne "0" ]; then echo "Error in c_example" exit 1 fi echo Testing c_example_shlib ./examples/c_example_shlib if [ "$?" -ne "0" ]; then echo "Error in c_example_shlib" exit 1 fi echo Testing pcm-msr \(read only\) ./pcm-msr -a 0x30A if [ "$?" -ne "0" ]; then echo "Error in pcm-msr" exit 1 fi echo Testing pcm-power ./pcm-power -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-power" exit 1 fi echo Testing pcm-pcie ./pcm-pcie -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-pcie" exit 1 fi echo Testing pcm-latency ./pcm-latency -i=1 if [ "$?" -ne "0" ]; then echo "Error in pcm-latency" exit 1 fi echo Testing pcm-tsx ./pcm-tsx -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-tsx" exit 1 fi # TODO add more tests # e.g for ./pcm-sensor-server, ./pcm-sensor, ... echo Testing urltest ./tests/urltest # We have 12 expected errors, anything else is a bug if [ "$?" != 12 ]; then echo "Error in urltest, 12 expected errors but found $?!" exit 1 fi echo Testing pcm-raw with event files echo Download necessary files if [ ! -f "mapfile.csv" ]; then echo "Downloading https://raw.githubusercontent.com/intel/perfmon/main/mapfile.csv" wget -q --timeout=10 https://raw.githubusercontent.com/intel/perfmon/main/mapfile.csv if [ "$?" -ne "0" ]; then echo "Could not download mapfile.csv" exit 1 fi fi VENDOR=$(lscpu | grep "Vendor ID:" | awk '{print $3}') FAMILY=$(lscpu | grep "CPU family:" | awk '{print $3}') MODEL=$(lscpu | grep "Model:" | awk '{printf("%x", $2)}') STRING="${VENDOR}-${FAMILY}-${MODEL}-" FILES=$(grep $STRING "mapfile.csv" | awk -F "\"*,\"*" '{print $3}') DIRS= for FILE in $FILES do DIR="$(dirname $FILE)" DIR="${DIR#?}" if [[ ! " ${DIRS[*]} " =~ " ${DIR} " ]]; then DIRS+="${DIR} " fi done for DIR in $DIRS do if [ ! -d $DIR ]; then mkdir -p $DIR cd $DIR DIRPATH="https://github.com/intel/perfmon.git" echo "Downloading all files from ${DIRPATH} using git" git clone $DIRPATH if [ "$?" -ne "0" ]; then cd .. echo "Could not download ${DIRPATH}" exit 1 fi mv perfmon/${DIR}/* . rm -rf perfmon cd ../.. fi done echo Now check pcm-raw with JSON files from mapFile.csv ./pcm-raw -r -e LD_BLOCKS.STORE_FORWARD -e CPU_CLK_UNHALTED.THREAD_ANY -e INST_RETIRED.ANY -e UNC_CHA_CLOCKTICKS -- sleep 1 if [ "$?" -ne "0" ]; then echo "Error in pcm-raw" exit 1 fi echo Now get corresponding TSV files and replace JSON files in mapFile.csv with them cp "mapfile.csv" "mapfile.csv_orig" for FILE in $FILES do DIR="$(dirname $FILE)" DIR="${DIR#?}" cd $DIR BASE="$(basename $FILE)" TYPE="$(echo $BASE | sed 's/_v[0-9].*json//g')" # TYPE can be for example: skylakex_core or skylakex_uncore. CMD="find . -type f -regex '\.\/${TYPE}_v[0-9]*\.[0-9]*.tsv'" TSVFILE=$(eval $CMD) TSVFILE="${TSVFILE:2}" cd ../.. CMD="sed -i 's/${BASE}/${TSVFILE}/g' mapfile.csv" eval $CMD done # echo Test pcm-raw with TSV files #./pcm-raw -r -e LD_BLOCKS.STORE_FORWARD -e CPU_CLK_UNHALTED.THREAD_ANY -e INST_RETIRED.ANY -e UNC_CHA_CLOCKTICKS -- sleep 1 #if [ "$?" -ne "0" ]; then # echo "Error in pcm-raw" # rm -rf mapfile.csv # cp "mapfile.csv_orig" "mapfile.csv" # exit 1 #fi rm -rf mapfile.csv cp "mapfile.csv_orig" "mapfile.csv" if [ ! -f "event_file_test.txt" ]; then cat < event_file_test.txt # group 1 INST_RETIRED.ANY CPU_CLK_UNHALTED.REF_TSC MEM_TRANS_RETIRED.LOAD_LATENCY_GT_4 UNC_CHA_DIR_LOOKUP.SNP UNC_CHA_DIR_LOOKUP.NO_SNP UNC_M_CAS_COUNT.RD UNC_M_CAS_COUNT.WR UNC_UPI_CLOCKTICKS UNC_UPI_TxL_FLITS.ALL_DATA UNC_UPI_TxL_FLITS.NON_DATA UNC_UPI_L1_POWER_CYCLES UNC_CHA_TOR_INSERTS.IA_MISS MSR_EVENT:msr=0x19C:type=STATIC:scope=THREAD MSR_EVENT:msr=0x1A2:type=STATIC:scope=THREAD MSR_EVENT:msr=0x34:type=FREERUN:scope=PACKAGE MSR_EVENT:msr=0x34:type=static:scope=PACKAGE package_msr/config=0x34,config1=0 thread_msr/config=0x10,config1=1,name=TSC_DELTA thread_msr/config=0x10,config1=0,name=TSC pcicfg/config=0x208d,config1=0,config2=0,width=64,name=first_8_bytes_of_208d_device pcicfg/config=0x2021,config1=0,config2=0,width=32 pcicfg/config=0x2021,config1=0,config2=0,width=64 pcicfg/config=0x2058,config1=0x318,config2=1,width=64,name=UPI_reg ; # group 2 OFFCORE_REQUESTS_BUFFER.SQ_FULL UNC_CHA_DIR_UPDATE.HA UNC_CHA_DIR_UPDATE.TOR UNC_M2M_DIRECTORY_UPDATE.ANY UNC_M_CAS_COUNT.RD UNC_M_CAS_COUNT.WR imc/fixed,name=DRAM_CLOCKS UNC_CHA_TOR_INSERTS.IA_MISS:tid=0x20 UNC_M_PRE_COUNT.PAGE_MISS UNC_UPI_TxL0P_POWER_CYCLES UNC_UPI_RxL0P_POWER_CYCLES UNC_UPI_RxL_FLITS.ALL_DATA UNC_UPI_RxL_FLITS.NON_DATA MSR_EVENT:msr=0x10:type=FREERUN:scope=thread MSR_EVENT:msr=0x10:type=static:scope=thread pcicfg/config=0x2021,config1=4,config2=0,width=32 pcicfg/config=0x208d,config1=0,config2=1,width=64,name=first_8_bytes_of_208d_device_diff ; EOF fi echo Testing pcm-raw with -el event_file_test.txt -tr -csv ./pcm-raw -el event_file_test.txt -tr -csv=raw_tr_wo_ext.csv -i=4 0.25 if [ "$?" -ne "0" ]; then echo "Error in pcm-raw" exit 1 fi echo Testing pcm-raw with -el event_file_test.txt -tr -ext -csv ./pcm-raw -el event_file_test.txt -tr -ext -csv=raw_tr_wi_ext.csv -i=4 0.25 if [ "$?" -ne "0" ]; then echo "Error in pcm-raw" exit 1 fi echo Testing pcm-raw with -el event_file_test.txt -tr -ext -single-header -csv ./pcm-raw -el event_file_test.txt -tr -ext -single-header -csv=raw_tr_wi_ext_single_header.csv -i=4 0.25 if [ "$?" -ne "0" ]; then echo "Error in pcm-raw" exit 1 fi echo Testing pcm-raw with -json ./pcm-raw -el event_file_test.txt -json=raw_json.json -i=4 0.25 if [ "$?" -ne "0" ]; then echo "Error in pcm-raw" exit 1 fi echo Testing pcm-raw with -edp ./pcm-raw -edp -out raw_edp.txt 0.25 -tr -i=4 -el event_file_test.txt if [ "$?" -ne "0" ]; then echo "Error in pcm-raw" exit 1 fi echo Testing pcm-raw with -edp and offlined cores online_offline_cores() { for i in {5..10}; do echo $1 > /sys/devices/system/cpu/cpu$i/online done } online_offline_cores 0 ./pcm-raw -edp -out raw_edp_offlined_cores.txt 0.25 -tr -i=4 -el event_file_test.txt if [ "$?" -ne "0" ]; then online_offline_cores 1 echo "Error in pcm-raw with offlined cores" exit 1 fi online_offline_cores 1 popd pcm-202307/tests/urltest.cpp000066400000000000000000000023201445420033100157310ustar00rootroot00000000000000#define UNIT_TEST 1 #include "../src/pcm-sensor-server.cpp" #undef UNIT_TEST std::vector urls{ "http://otto:test@www.intel.com/~otto/file1.txt", "file://localhost/c/mnt/cd/file2.txt", "ftp://otto%40yahoo.com:abcd%3B1234@www.intel.com:30/xyz.php?a=1&t=3", "gopher://otto@hostname1.intel.com:8080/file3.zyx", "www.intel.com", "http://www.blah.org/file.html#firstmark", "http://www.blah.org/file.html#firstmark%21%23", "localhost", "https://www.intel.com", "://google.com/", "https://intc.com/request?", "htt:ps//www.intel.com", "http://www.intel.com:66666/", "http:///", "http://[1234::1234::1234/", "http://@www.intel.com", "http://otto@:www.intel.com", "https://:@www.intel.com", "https://user:@www.intel.com", "http:www.intel.com/", "http://ww\x00\x00\x00rstmark\x0a" }; int main( int, char** ) { int errors = 0; for ( auto & s : urls ) { try { std::cout << s << "\n"; URL x = URL::parse( s ); x.printURL(std::cout); } catch (const std::runtime_error & x ) { std::cout << "\"" << s << "\": " << x.what() << "\n"; ++errors; } } return errors; }