├── .github
└── workflows
│ ├── build-extension.yml
│ ├── cache.yml
│ └── ci.yml
├── .gitignore
├── .gitmodules
├── AMBuildScript
├── README.md
├── buildbot
├── BreakpadSymbols
├── PackageScript
├── Versioning
└── pushbuild.txt
├── configure.py
├── dockerbuild
├── .gitkeep
├── _accelerator_docker_build_internal.sh
├── _github_actions_setup.sh
└── accelerator_docker_build.sh
├── extension
├── AMBuilder
├── MemoryDownloader.cpp
├── MemoryDownloader.h
├── accelerator.autoload
├── extension.cpp
├── extension.h
├── smsdk_config.h
├── version.h
└── version.rc
├── gamedata
└── accelerator.games.txt
├── patches
├── 0001-Ignore-invalid-modules-rather-than-bailing-on-the-en.patch
├── 0002-Write-FUNC-records-instead-of-PUBLIC-for-ELF-symbols.patch
├── 0003-Support-compilation-on-VS-2015.patch
├── 0004-Fix-Linux-a-out-include.patch
└── 0005-Add-LSS.patch
├── product.version
├── third_party
├── AMBuilder
├── Configure
├── Patch
└── cwd_cmd.py
└── upload.py
/.github/workflows/build-extension.yml:
--------------------------------------------------------------------------------
1 | name: Build extension
2 | on:
3 | workflow_call:
4 | inputs:
5 | os:
6 | type: string
7 | required: true
8 | cc:
9 | type: string
10 | required: true
11 | cxx:
12 | type: string
13 | upload:
14 | type: boolean
15 | required: false
16 | default: false
17 | upload-artifact-name:
18 | type: string
19 | required: false
20 | default: package
21 | debug:
22 | type: boolean
23 | required: false
24 | default: false
25 | cache-key:
26 | type: string
27 | required: true
28 | cache-dir:
29 | type: string
30 | required: true
31 |
32 | jobs:
33 | build:
34 | name: Accelerator Extension ${{ inputs.os }}-${{ inputs.cc }}
35 | runs-on: ${{ inputs.os }}
36 |
37 | env:
38 | SOURCEMOD: ${{ github.workspace }}/${{ inputs.cache-dir }}/sourcemod
39 | steps:
40 | - uses: actions/checkout@v4
41 | with:
42 | fetch-depth: 0
43 | submodules: true
44 |
45 | - name: Retrieve the cache
46 | id: cache
47 | uses: actions/cache@v4
48 | with:
49 | path: ${{ inputs.cache-dir }}
50 | key: ${{ inputs.cache-key }}
51 | fail-on-cache-miss: true
52 | enableCrossOsArchive: true
53 |
54 | - name: Linux dependencies
55 | if: startsWith(runner.os, 'Linux')
56 | run: |
57 | sudo dpkg --add-architecture i386
58 | sudo apt-get update
59 | sudo apt-get install -y --no-install-recommends \
60 | gcc-multilib g++-multilib libstdc++6 lib32stdc++6 \
61 | libc6-dev libc6-dev-i386 linux-libc-dev \
62 | linux-libc-dev:i386 lib32z1-dev \
63 | zlib1g-dev:i386 zlib1g-dev ${{ inputs.cc }}
64 |
65 | - name: Select clang compiler
66 | if: startsWith(runner.os, 'Linux')
67 | run: |
68 | echo "CC=${{ inputs.cc }}" >> $GITHUB_ENV
69 | echo "CXX=${{ inputs.cxx }}" >> $GITHUB_ENV
70 | ${{ inputs.cc }} --version
71 | ${{ inputs.cxx }} --version
72 |
73 | - uses: actions/setup-python@v5
74 | name: Setup Python 3.10
75 | with:
76 | python-version: '3.10'
77 |
78 | - name: Install AMBuild
79 | run: |
80 | python -m pip install --upgrade pip setuptools wheel
81 | pip install ./${{ inputs.cache-dir }}/ambuild
82 |
83 | - name: Build (Debug)
84 | if: ${{ inputs.debug }}
85 | run: |
86 | mkdir -p build && cd build
87 | python ../configure.py --enable-debug
88 | ambuild
89 |
90 | - name: Build (Release)
91 | if: not ${{ inputs.debug }}
92 | run: |
93 | mkdir -p build && cd build
94 | python ../configure.py --enable-optimize
95 | ambuild
96 |
97 | - name: Upload package
98 | if: ${{ inputs.upload }}
99 | uses: actions/upload-artifact@v4
100 | with:
101 | name: ${{ inputs.upload-artifact-name }}
102 | path: build/package
--------------------------------------------------------------------------------
/.github/workflows/cache.yml:
--------------------------------------------------------------------------------
1 | name: Cache
2 | on:
3 | workflow_call:
4 | outputs:
5 | key:
6 | value: ${{ jobs.cache.outputs.key }}
7 | dir:
8 | value: .cache
9 |
10 | jobs:
11 | cache:
12 | name: Create cache
13 | runs-on: 'ubuntu-latest'
14 | outputs:
15 | key: ${{ steps.cache-key.outputs.key }}
16 |
17 | env:
18 | SOURCEMOD_REF: 'e0e018c5ebcd11df9bab91b43d40bf148805c684'
19 | AMBUILD_REF: '2d4620da4cdaf89cf25afc3577f920e6138ae7cd'
20 | steps:
21 | - uses: actions/checkout@v4
22 |
23 | - name: Create cache key
24 | run: |
25 | echo "${{ env.SOURCEMOD_REF }}" >> cache-key
26 | echo "${{ env.AMBUILD_REF }}" >> cache-key
27 |
28 | - name: Output cache key
29 | id: cache-key
30 | run: |
31 | ls -a
32 | echo "key=cache-${{ hashFiles('cache-key') }}" >> $GITHUB_OUTPUT
33 |
34 | - name: Retrieve the cache
35 | id: cache
36 | uses: actions/cache@v4
37 | with:
38 | path: .cache
39 | key: ${{ steps.cache-key.outputs.key }}
40 | enableCrossOsArchive: true
41 |
42 | - uses: actions/checkout@v4
43 | if: ${{ !steps.cache.outputs.cache-hit }}
44 | name: SourceMod checkout
45 | with:
46 | repository: alliedmodders/sourcemod
47 | ref: ${{ env.SOURCEMOD_REF }}
48 | submodules: true
49 | path: .cache/sourcemod
50 |
51 | - uses: actions/checkout@v4
52 | if: ${{ !steps.cache.outputs.cache-hit }}
53 | name: AMBuild checkout
54 | with:
55 | repository: alliedmodders/ambuild
56 | ref: ${{ env.AMBUILD_REF }}
57 | path: .cache/ambuild
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | branches:
6 | - master
7 | - overhaul
8 |
9 | push:
10 | branches:
11 | - master
12 | - overhaul
13 |
14 | jobs:
15 | cache:
16 | uses: ./.github/workflows/cache.yml
17 |
18 | build:
19 | name: Build
20 | needs: cache
21 |
22 | strategy:
23 | fail-fast: false
24 | matrix:
25 | os: [ 'ubuntu-22.04', 'ubuntu-latest', 'windows-2019', 'windows-latest' ]
26 | include:
27 | # we need to ship ubuntu 22.04 because of glibc reasons
28 | - os: ubuntu-22.04
29 | #cc: clang-8
30 | #cxx: clang++-8
31 | cc: clang
32 | cxx: clang++
33 | upload: true
34 | upload-artifact-name: accelerator_linux
35 | - os: ubuntu-latest
36 | cc: clang
37 | cxx: clang++
38 | upload: false
39 | upload-artifact-name: none
40 | - os: windows-2019
41 | cc: msvc
42 | cxx: msvc
43 | upload: false
44 | upload-artifact-name: none
45 | - os: windows-latest
46 | cc: msvc
47 | cxx: msvc
48 | upload: true
49 | upload-artifact-name: accelerator_windows
50 |
51 | uses: ./.github/workflows/build-extension.yml
52 | with:
53 | os: ${{ matrix.os }}
54 | cc: ${{ matrix.cc }}
55 | cxx: ${{ matrix.cxx }}
56 | upload: ${{ matrix.upload }}
57 | upload-artifact-name: ${{ matrix.upload-artifact-name }}
58 | cache-key: ${{ needs.cache.outputs.key }}
59 | cache-dir: ${{ needs.cache.outputs.dir }}
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /extension/version_auto.h
3 | .vscode
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "breakpad"]
2 | path = third_party/breakpad
3 | url = https://github.com/google/breakpad.git
4 | [submodule "zlib"]
5 | path = third_party/zlib
6 | url = https://github.com/madler/zlib.git
7 |
--------------------------------------------------------------------------------
/AMBuildScript:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import os, sys, shutil
3 |
4 | def ResolveEnvPath(env, folder=None):
5 | if env in os.environ:
6 | path = os.environ[env]
7 | if os.path.isdir(path):
8 | return path
9 | return None
10 |
11 | if folder:
12 | head = os.getcwd()
13 | oldhead = None
14 | while head != None and head != oldhead:
15 | path = os.path.join(head, folder)
16 | if os.path.isdir(path):
17 | return path
18 | oldhead = head
19 | head, tail = os.path.split(head)
20 |
21 | return None
22 |
23 | def Normalize(path):
24 | return os.path.abspath(os.path.normpath(path))
25 |
26 | class AcceleratorConfig(object):
27 | def __init__(self):
28 | self.mms_root = None
29 | self.sm_root = None
30 | self.extension = None
31 | self.libz = None
32 | self.libbreakpad_client = None
33 | self.libbreakpad = None
34 | self.libdisasm = None
35 | self.breakpad_patch = None
36 | self.targets = []
37 | self.target_archs = set()
38 | self.breakpad_config = dict()
39 | self.breakpad_patch = None
40 |
41 | if builder.options.targets:
42 | target_archs = builder.options.targets.split(',')
43 | else:
44 | target_archs = ['x86', 'x86_64']
45 |
46 | for arch in target_archs:
47 | try:
48 | cxx = builder.DetectCxx(target_arch = arch)
49 | self.target_archs.add(cxx.target.arch)
50 | except Exception as e:
51 | if builder.options.targets:
52 | raise
53 | print('Skipping target {}: {}'.format(arch, e))
54 | continue
55 | self.targets.append(cxx)
56 |
57 | if not self.targets:
58 | raise Exception('No suitable C/C++ compiler was found.')
59 |
60 | @property
61 | def tag(self):
62 | if builder.options.debug == '1':
63 | return 'Debug'
64 | return 'Release'
65 |
66 | def retrieve_sm(self):
67 | if builder.options.sm_path:
68 | self.sm_root = builder.options.sm_path
69 | else:
70 | self.sm_root = ResolveEnvPath('SOURCEMOD', 'sourcemod')
71 |
72 | if not self.sm_root or not os.path.isdir(self.sm_root):
73 | raise Exception('Could not find a source copy of SourceMod')
74 | self.sm_root = Normalize(self.sm_root)
75 |
76 | def use_auto_versioning(self):
77 | return not builder.options.disable_auto_versioning
78 |
79 | def configure_cxx(self, cxx):
80 | if cxx.like('gcc'):
81 | self.configure_gcc(cxx)
82 | elif cxx.family == 'msvc':
83 | self.configure_msvc(cxx)
84 |
85 | # Optimization
86 | if builder.options.opt == '1':
87 | cxx.defines += ['NDEBUG']
88 |
89 | # Debugging
90 | if builder.options.debug == '1':
91 | cxx.defines += ['DEBUG', '_DEBUG']
92 |
93 | # Platform-specifics
94 | if cxx.target.platform == 'linux':
95 | self.configure_linux(cxx)
96 | elif cxx.target.platform == 'windows':
97 | self.configure_windows(cxx)
98 |
99 | if self.use_auto_versioning():
100 | cxx.defines += ['GIT_ACTION_BUILD']
101 |
102 | def configure_gcc(self, cxx):
103 | cxx.cflags += [
104 | '-fPIC',
105 | '-pipe',
106 | '-fno-strict-aliasing',
107 | '-fvisibility=hidden',
108 | '-fvisibility-inlines-hidden',
109 | '-Wall',
110 | '-Werror',
111 | '-msse'
112 | ]
113 |
114 | cxx.cxxflags += [
115 | '-std=c++17',
116 | '-fno-threadsafe-statics',
117 | '-Wno-non-virtual-dtor',
118 | '-Wno-overloaded-virtual',
119 | '-Wno-implicit-exception-spec-mismatch'
120 | ]
121 |
122 | cxx.postlink += ['-pthread', '-static-libstdc++', '-static-libgcc']
123 |
124 | if builder.options.opt == '1':
125 | cxx.cflags += ['-O3']
126 | return
127 |
128 | def configure_msvc(self, cxx):
129 |
130 | cxx.cxxflags += [
131 | '/EHsc',
132 | '/std:c++17'
133 | ]
134 |
135 | return
136 |
137 | def configure_linux(self, cxx):
138 | cxx.defines += ['_LINUX', 'POSIX', '_GLIBCXX_USE_CXX11_ABI=0']
139 | return
140 |
141 | def configure_windows(self, cxx):
142 | cxx.defines += ['_WINDOWS']
143 | if cxx.target.arch == 'x86':
144 | cxx.defines += ['WIN32']
145 | elif cxx.target.arch == 'x86_64':
146 | cxx.defines += ['WIN64']
147 | return
148 |
149 | def configure(self):
150 | self.retrieve_sm()
151 |
152 | for cxx in self.targets:
153 | self.configure_cxx(cxx)
154 |
155 | def configure_extension(self, compiler, context):
156 | compiler.cxxincludes += [
157 | os.path.join(context.currentSourcePath),
158 | os.path.join(self.sm_root, 'public'),
159 | os.path.join(self.sm_root, 'public', 'extensions'),
160 | os.path.join(self.sm_root, 'public', 'amtl', 'amtl'),
161 | os.path.join(self.sm_root, 'public', 'amtl'),
162 | os.path.join(self.sm_root, 'sourcepawn', 'include')
163 | ]
164 |
165 | def link_libz(self, compiler, context):
166 | for task in self.libz:
167 | if task.target.arch == compiler.target.arch:
168 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)]
169 | compiler.linkdeps += [task.binary]
170 | return
171 | raise Exception('No suitable build of libz was found.')
172 |
173 | def link_libbreakpad_client(self, compiler, context):
174 | for task in self.libbreakpad_client:
175 | if task.target.arch == compiler.target.arch:
176 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)]
177 | compiler.linkdeps += [task.binary]
178 | return
179 | raise Exception('No suitable build of libbreakpad_client was found.')
180 |
181 | def link_libbreakpad(self, compiler, context):
182 | for task in self.libbreakpad:
183 | if task.target.arch == compiler.target.arch:
184 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)]
185 | compiler.linkdeps += [task.binary]
186 | return
187 | raise Exception('No suitable build of libbreakpad was found.')
188 |
189 | def link_libdisasm(self, compiler, context):
190 | for task in self.libdisasm:
191 | if task.target.arch == compiler.target.arch:
192 | compiler.postlink += [os.path.join(context.buildPath, task.binary.path)]
193 | compiler.linkdeps += [task.binary]
194 | return
195 | raise Exception('No suitable build of libdisasm was found.')
196 |
197 | def ConfigureLibrary(self, project, compiler, context):
198 | binary = project.Configure(compiler, project.name, '{0} - {1}'.format(self.tag, compiler.target.arch))
199 | binary.compiler.cxxincludes += [
200 | os.path.join(context.currentSourcePath)
201 | ]
202 | return binary
203 |
204 | def ConfigureExtension(self, project, compiler, context):
205 | binary = self.ConfigureLibrary(project, compiler, context)
206 | self.configure_extension(binary.compiler, context)
207 | return binary
208 |
209 | Accelerator = AcceleratorConfig()
210 | Accelerator.configure()
211 |
212 | builder.Build(['third_party/Patch', 'third_party/Configure', 'third_party/AMBuilder'], { 'Accelerator': Accelerator })
213 |
214 | BuildScripts = ['extension/AMBuilder', 'buildbot/PackageScript']
215 | builder.Build(BuildScripts, { 'Accelerator': Accelerator })
216 |
217 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Accelerator
2 |
--------------------------------------------------------------------------------
/buildbot/BreakpadSymbols:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import os
3 | from ambuild.command import Command
4 | from ambuild.command import ShellCommand
5 |
6 | try:
7 | import urllib.request as urllib
8 | except ImportError:
9 | import urllib2 as urllib
10 |
11 | class IterateDebugInfoCommand(Command):
12 | def run(self, master, job):
13 | pdblog = open(os.path.join(AMBuild.outputFolder, 'pdblog.txt'), 'rt')
14 | for debug_info in pdblog:
15 | debug_info = os.path.join(AMBuild.outputFolder, debug_info.strip())
16 | job.AddCommand(SymbolCommand(debug_info, symbolServer))
17 | pdblog.close()
18 |
19 | class SymbolCommand(ShellCommand):
20 | def __init__(self, debugFile, symbolServer):
21 | self.serverResponse = None
22 | self.symbolServer = symbolServer
23 | if AMBuild.target['platform'] == 'linux':
24 | cmdstring = "dump_syms {0} {1}".format(debugFile, os.path.dirname(debugFile))
25 | elif AMBuild.target['platform'] == 'darwin':
26 | cmdstring = "dump_syms {0}".format(debugFile)
27 | elif AMBuild.target['platform'] == 'windows':
28 | cmdstring = "dump_syms.exe {0}".format(debugFile)
29 | ShellCommand.__init__(self, cmdstring)
30 | def run(self, master, job):
31 | ShellCommand.run(self, master, job)
32 | if self.stdout != None and len(self.stdout) > 0:
33 | request = urllib.Request(symbolServer, self.stdout.encode('utf-8'))
34 | request.add_header("Content-Type", "text/plain")
35 | self.serverResponse = urllib.urlopen(request).read().decode('utf-8')
36 | def spew(self, runner):
37 | if self.stderr != None and len(self.stderr) > 0:
38 | runner.PrintOut(self.stderr)
39 | if self.serverResponse != None and len(self.serverResponse) > 0:
40 | runner.PrintOut(self.serverResponse)
41 |
42 | if 'BREAKPAD_SYMBOL_SERVER' in os.environ:
43 | symbolServer = os.environ['BREAKPAD_SYMBOL_SERVER']
44 | job = AMBuild.AddJob('breakpad-symbols')
45 | job.AddCommand(IterateDebugInfoCommand())
46 |
--------------------------------------------------------------------------------
/buildbot/PackageScript:
--------------------------------------------------------------------------------
1 | # vim: set ts=8 sts=2 sw=2 tw=99 et ft=python:
2 | import os
3 |
4 | # This is where the files will be output to
5 | # package is the default
6 | builder.SetBuildFolder('package')
7 |
8 | # Add any folders you need to this list
9 | folder_list = [
10 | 'addons/sourcemod/extensions',
11 | 'addons/sourcemod/gamedata',
12 | 'addons/sourcemod/configs'
13 | ]
14 |
15 | # Are we build a x86_64 extension ?
16 | for task in Accelerator.extension:
17 | if task.target.arch == 'x86_64':
18 | folder_list += ['addons/sourcemod/extensions/x64']
19 | break
20 |
21 | # Create the distribution folder hierarchy.
22 | folder_map = {}
23 | for folder in folder_list:
24 | norm_folder = os.path.normpath(folder)
25 | folder_map[folder] = builder.AddFolder(norm_folder)
26 |
27 | # Do all straight-up file copies from the source tree.
28 | def CopyFiles(src, dest, files):
29 | if not dest:
30 | dest = src
31 | dest_entry = folder_map[dest]
32 | for source_file in files:
33 | source_path = os.path.join(builder.sourcePath, src, source_file)
34 | builder.AddCopy(source_path, dest_entry)
35 | def CopyFile(src, dest):
36 | dest_entry = folder_map[dest]
37 | source_path = os.path.join(builder.sourcePath, src)
38 | builder.AddCopy(source_path, dest_entry)
39 | def CopyDirContent(src, dest):
40 | dest_entry = folder_map[dest]
41 | for item in os.scandir(os.path.join(builder.sourcePath, src)):
42 | if item.is_file():
43 | builder.AddCopy(item.path, dest_entry)
44 |
45 | # Copy binaries.
46 | for task in Accelerator.extension:
47 | if task.target.arch == 'x86_64':
48 | builder.AddCopy(task.binary, folder_map['addons/sourcemod/extensions/x64'])
49 | else:
50 | builder.AddCopy(task.binary, folder_map['addons/sourcemod/extensions'])
51 |
52 | CopyDirContent('gamedata', 'addons/sourcemod/gamedata')
53 | CopyFile('extension/accelerator.autoload', 'addons/sourcemod/extensions')
--------------------------------------------------------------------------------
/buildbot/Versioning:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import os
3 | import re
4 | import subprocess
5 | from ambuild.cache import Cache
6 | import ambuild.command as command
7 |
8 | #Quickly try to ascertain the current repository revision
9 | def GetVersion():
10 | rev = command.RunDirectCommand(AMBuild, ['git', 'rev-list', '--count', 'HEAD']).stdoutText.strip()
11 | cset = command.RunDirectCommand(AMBuild, ['git', 'log', '--pretty=format:%h', '-n', '1']).stdoutText.strip()
12 |
13 | if not rev or not cset:
14 | raise Exception('Could not determine repository version')
15 |
16 | return (rev, cset)
17 |
18 | def PerformReversioning():
19 | rev, cset = GetVersion()
20 | cacheFile = os.path.join(AMBuild.outputFolder, '.ambuild', 'hgcache')
21 | cache = Cache(cacheFile)
22 | if os.path.isfile(cacheFile):
23 | cache.LoadCache()
24 | if cache.HasVariable('cset') and cache['cset'] == cset:
25 | return False
26 | cache.CacheVariable('cset', cset)
27 |
28 | productFile = open(os.path.join(AMBuild.sourceFolder, 'product.version'), 'r')
29 | productContents = productFile.read()
30 | productFile.close()
31 | m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents)
32 | if m == None:
33 | raise Exception('Could not detremine product version')
34 | major, minor, release, tag = m.groups()
35 |
36 | incFolder = os.path.join(AMBuild.sourceFolder, 'extension')
37 | incFile = open(os.path.join(incFolder, 'version_auto.h'), 'w')
38 | incFile.write("""
39 | #ifndef _AUTO_VERSION_INFORMATION_H_
40 | #define _AUTO_VERSION_INFORMATION_H_
41 |
42 | #define SM_BUILD_TAG \"{0}\"
43 | #define SM_BUILD_UNIQUEID \"{1}:{2}\" SM_BUILD_TAG
44 | #define SM_VERSION \"{3}.{4}.{5}\"
45 | #define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
46 | #define SM_FILE_VERSION {6},{7},{8},0
47 |
48 | #endif /* _AUTO_VERSION_INFORMATION_H_ */
49 |
50 | """.format(tag, rev, cset, major, minor, release, major, minor, release))
51 | incFile.close()
52 | cache.WriteCache()
53 |
54 | PerformReversioning()
55 |
56 |
57 |
--------------------------------------------------------------------------------
/buildbot/pushbuild.txt:
--------------------------------------------------------------------------------
1 | Hi!
2 | Once upon a time there was a bee.
3 |
--------------------------------------------------------------------------------
/configure.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from ambuild2 import run
3 |
4 | parser = run.BuildParser(sourcePath = sys.path[0], api='2.2')
5 | parser.options.add_argument('--sm-path', type=str, dest='sm_path', default=None,
6 | help='Path to SourceMod')
7 | parser.options.add_argument('--enable-debug', action='store_const', const='1', dest='debug',
8 | help='Enable debugging symbols')
9 | parser.options.add_argument('--enable-optimize', action='store_const', const='1', dest='opt',
10 | help='Enable optimization')
11 | parser.options.add_argument('--disable-auto-versioning', action='store_false', dest='disable_auto_versioning',
12 | default=True, help='Disables the auto versioning script')
13 | parser.options.add_argument('--targets', type=str, dest='targets', default=None,
14 | help="Override the target architecture (use commas to separate multiple targets).")
15 | parser.Configure()
--------------------------------------------------------------------------------
/dockerbuild/.gitkeep:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/dockerbuild/_accelerator_docker_build_internal.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -euxo pipefail
4 |
5 |
6 | bootstrapEnv()
7 | {
8 | # for squashing git whining when people run with docker instead of rootless podman
9 | git config --global --add safe.directory /
10 | git config --global --add safe.directory "*"
11 |
12 | # for preventing any submodule tomfuckery
13 | git submodule update --init --recursive -f
14 |
15 | # whack build dir it's literally faster and easier to do this than to cache it
16 | rm -rfv ./build
17 |
18 | # clean up patch cruft in breakpad dir so we start from a clean slate no matter what
19 | pushd third_party/breakpad
20 | git reset --hard
21 | git clean -x -f -d
22 | git clean -X -f -d
23 | popd
24 | }
25 |
26 |
27 | bootstrapPkgs()
28 | {
29 | # we really need to slim this shit down lol
30 | dpkg --add-architecture i386 && \
31 | apt-get update -y && \
32 | apt-get install -y \
33 | --no-install-recommends \
34 | git \
35 | clang \
36 | make \
37 | python3-httplib2 python3-pip \
38 | lib32stdc++-10-dev lib32z1-dev libc6-dev-i386 linux-libc-dev:i386 \
39 | libzstd-dev libzstd-dev:i386 zlib1g-dev zlib1g-dev:i386
40 |
41 | # force clang to be our compiler no matter what, hopefully
42 | update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100
43 | update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100
44 |
45 | # this is just for logging
46 | cc --version || true
47 | c++ --version || true
48 | }
49 |
50 |
51 | smBranch="master"
52 | amTempLocation="_am_temp"
53 | succCloneLocation="/accelerator/${amTempLocation}/successful_clone"
54 | bootstrapAM()
55 | {
56 | # need to install ambuild if we already cloned, otherwise checkout-deps will do it 4 us
57 | if test -f "${succCloneLocation}"; then
58 | pip install /accelerator/"${amTempLocation}"/ambuild
59 | return 255;
60 | fi
61 |
62 | rm -rf /accelerator/"${amTempLocation}"/ || true
63 | mkdir -p ${amTempLocation} || exit 1
64 | pushd ${amTempLocation} || exit 1
65 | git clone -b ${smBranch} --recursive --depth 1 https://github.com/alliedmodders/sourcemod sourcemod || exit 1
66 | git clone --recursive --depth 1 https://github.com/alliedmodders/ambuild ambuild || exit 1
67 | pip install ./ambuild
68 |
69 | # make a blank file so that we don't reclone everything if we don't need to
70 | true > "${succCloneLocation}" || exit 1
71 | popd
72 | }
73 |
74 |
75 | buildIt()
76 | {
77 | if test ! -d build; then
78 | mkdir -p build
79 | fi
80 |
81 | pushd build
82 | CC=clang CXX=clang++ python3 ../configure.py --sm-path=/accelerator/${amTempLocation}/sourcemod/
83 | ambuild
84 | popd
85 | }
86 |
87 | ###############################
88 |
89 | cd /accelerator
90 |
91 | bootstrapPkgs
92 | bootstrapEnv
93 | bootstrapAM || true
94 |
95 | buildIt
96 |
--------------------------------------------------------------------------------
/dockerbuild/_github_actions_setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -euxo pipefail
4 |
5 | pwd
6 | ls -la
7 |
8 | mkdir /accelerator
9 | cp -av "$(pwd)"/. /accelerator
10 | cd /accelerator
11 |
12 | pwd
13 | ls -la
14 |
15 |
16 | bash ./dockerbuild/_accelerator_docker_build_internal.sh
17 |
--------------------------------------------------------------------------------
/dockerbuild/accelerator_docker_build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euxo pipefail
3 |
4 | dockerimage="debian:11-slim"
5 |
6 |
7 | # we do this so that we can be agnostic about where we're invoked from
8 | # meaning you can exec this script anywhere and it should work the same
9 | thisiswhereiam=${BASH_SOURCE[0]}
10 | # this should be /whatever/directory/structure/Open-Fortress-Source
11 | script_folder=$( cd -- "$( dirname -- "${thisiswhereiam}" )" &> /dev/null && pwd )
12 |
13 |
14 | # this should be /whatever/directory/structure/[accelerator_root]/cicd
15 | build_dir="dockerbuild"
16 |
17 | pushd "${script_folder}" &> /dev/null || exit 99
18 |
19 | # this is relative to our source dir/build
20 | internalscript="_accelerator_docker_build_internal.sh"
21 |
22 | # this should always be our accelerator root dir
23 | pushd ../ &> /dev/null
24 | dev_srcdir=$(pwd)
25 | container_rootdir="accelerator"
26 |
27 | # add -it flags automatically if in null tty
28 | itflag=""
29 | if [ -t 0 ] ; then
30 | itflag="-it"
31 | else
32 | itflag=""
33 | fi
34 |
35 | podman run ${itflag} \
36 | -v "${dev_srcdir}":/"${container_rootdir}" \
37 | -w /${container_rootdir} \
38 | ${dockerimage} \
39 | bash ./${build_dir}/${internalscript} "$@"
40 |
41 | ecodereal=$?
42 | echo "real exit code ${ecodereal}"
43 |
44 | popd &> /dev/null || exit
45 |
46 | popd &> /dev/null || exit
47 |
48 | exit ${ecodereal}
49 |
50 |
--------------------------------------------------------------------------------
/extension/AMBuilder:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import os, sys
3 |
4 | builder.SetBuildFolder('/')
5 |
6 | project = builder.LibraryProject('accelerator.ext')
7 | project.sources = [
8 | 'extension.cpp',
9 | 'MemoryDownloader.cpp',
10 | os.path.join(Accelerator.sm_root, 'public', 'smsdk_ext.cpp')
11 | ]
12 |
13 | def AddSourceFilesFromDir(path, files):
14 | list = []
15 | for file in files:
16 | list.append(os.path.join(path, file))
17 | return list
18 |
19 | for cxx in Accelerator.targets:
20 | binary = Accelerator.ConfigureExtension(project, cxx, builder)
21 | compiler = binary.compiler
22 | # Wait for breakpad to be patched
23 | compiler.sourcedeps += Accelerator.breakpad_patch
24 | # We depend on breakpad on config
25 | compiler.sourcedeps += Accelerator.breakpad_config[compiler.target.arch]
26 |
27 | compiler.defines += ['HAVE_CONFIG_H']
28 | compiler.cxxincludes += [
29 | os.path.join(builder.sourcePath, 'third_party', 'breakpad', 'src'),
30 | os.path.join(builder.buildPath, 'third_party', 'config', compiler.target.arch),
31 | ]
32 |
33 | if compiler.target.platform in ['linux']:
34 | binary.sources += AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, '..', 'third_party', 'breakpad', 'src', 'common'), [
35 | 'dwarf_cfi_to_module.cc',
36 | 'dwarf_cu_to_module.cc',
37 | 'dwarf_line_to_module.cc',
38 | 'dwarf_range_list_handler.cc',
39 | 'language.cc',
40 | 'module.cc',
41 | 'path_helper.cc',
42 | 'stabs_reader.cc',
43 | 'stabs_to_module.cc',
44 | 'dwarf/bytereader.cc',
45 | 'dwarf/dwarf2diehandler.cc',
46 | 'dwarf/dwarf2reader.cc',
47 | 'dwarf/elf_reader.cc',
48 | 'linux/crc32.cc',
49 | 'linux/dump_symbols.cc',
50 | 'linux/elf_symbols_to_module.cc',
51 | 'linux/breakpad_getcontext.S'
52 | ])
53 |
54 | Accelerator.link_libbreakpad_client(compiler, builder)
55 | Accelerator.link_libbreakpad(compiler, builder)
56 | Accelerator.link_libdisasm(compiler, builder)
57 |
58 | Accelerator.extension = builder.Add(project)
--------------------------------------------------------------------------------
/extension/MemoryDownloader.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 :
3 | * =============================================================================
4 | * SourceMod Updater Extension
5 | * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
6 | * =============================================================================
7 | *
8 | * This program is free software; you can redistribute it and/or modify it under
9 | * the terms of the GNU General Public License, version 3.0, as published by the
10 | * Free Software Foundation.
11 | *
12 | * This program is distributed in the hope that it will be useful, but WITHOUT
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 | * details.
16 | *
17 | * You should have received a copy of the GNU General Public License along with
18 | * this program. If not, see .
19 | *
20 | * As a special exception, AlliedModders LLC gives you permission to link the
21 | * code of this program (as well as its derivative works) to "Half-Life 2," the
22 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 | * by the Valve Corporation. You must obey the GNU General Public License in
24 | * all respects for all other code used. Additionally, AlliedModders LLC grants
25 | * this exception to all derivative works. AlliedModders LLC defines further
26 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 | * or .
28 | *
29 | * Version: $Id$
30 | */
31 |
32 | #include
33 | #include
34 | #include
35 | #include "MemoryDownloader.h"
36 |
37 | using namespace SourceMod;
38 |
39 | MemoryDownloader::MemoryDownloader() : buffer(NULL), bufsize(0), bufpos(0)
40 | {
41 | }
42 |
43 | MemoryDownloader::~MemoryDownloader()
44 | {
45 | free(buffer);
46 | }
47 |
48 | DownloadWriteStatus MemoryDownloader::OnDownloadWrite(IWebTransfer *session,
49 | void *userdata,
50 | void *ptr,
51 | size_t size,
52 | size_t nmemb)
53 | {
54 | size_t total = size * nmemb;
55 |
56 | if (bufpos + total > bufsize)
57 | {
58 | size_t rem = (bufpos + total) - bufsize;
59 | bufsize += rem + (rem / 2);
60 | buffer = (char *)realloc(buffer, bufsize);
61 | }
62 |
63 | assert(bufpos + total <= bufsize);
64 |
65 | memcpy(&buffer[bufpos], ptr, total);
66 | bufpos += total;
67 |
68 | return DownloadWrite_Okay;
69 | }
70 |
71 | void MemoryDownloader::Reset()
72 | {
73 | bufpos = 0;
74 | }
75 |
76 | char *MemoryDownloader::GetBuffer()
77 | {
78 | return buffer;
79 | }
80 |
81 | size_t MemoryDownloader::GetSize()
82 | {
83 | return bufpos;
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/extension/MemoryDownloader.h:
--------------------------------------------------------------------------------
1 | /**
2 | * vim: set ts=4 :
3 | * =============================================================================
4 | * SourceMod Updater Extension
5 | * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
6 | * =============================================================================
7 | *
8 | * This program is free software; you can redistribute it and/or modify it under
9 | * the terms of the GNU General Public License, version 3.0, as published by the
10 | * Free Software Foundation.
11 | *
12 | * This program is distributed in the hope that it will be useful, but WITHOUT
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 | * details.
16 | *
17 | * You should have received a copy of the GNU General Public License along with
18 | * this program. If not, see .
19 | *
20 | * As a special exception, AlliedModders LLC gives you permission to link the
21 | * code of this program (as well as its derivative works) to "Half-Life 2," the
22 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 | * by the Valve Corporation. You must obey the GNU General Public License in
24 | * all respects for all other code used. Additionally, AlliedModders LLC grants
25 | * this exception to all derivative works. AlliedModders LLC defines further
26 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 | * or .
28 | *
29 | * Version: $Id$
30 | */
31 |
32 | #ifndef _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
33 | #define _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_
34 |
35 | #include
36 |
37 | namespace SourceMod
38 | {
39 | class MemoryDownloader : public ITransferHandler
40 | {
41 | public:
42 | MemoryDownloader();
43 | ~MemoryDownloader();
44 | public:
45 | DownloadWriteStatus OnDownloadWrite(IWebTransfer *session,
46 | void *userdata,
47 | void *ptr,
48 | size_t size,
49 | size_t nmemb);
50 | public:
51 | void Reset();
52 | char *GetBuffer();
53 | size_t GetSize();
54 | private:
55 | char *buffer;
56 | size_t bufsize;
57 | size_t bufpos;
58 | };
59 | }
60 |
61 | #endif /* _INCLUDE_SOURCEMOD_UPDATER_MEMORY_DOWNLOADER_H_ */
62 |
63 |
--------------------------------------------------------------------------------
/extension/accelerator.autoload:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asherkin/accelerator/5e319ecdc33f3cf09dad5a9e234b7dc530997ffd/extension/accelerator.autoload
--------------------------------------------------------------------------------
/extension/extension.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * =============================================================================
3 | * Accelerator Extension
4 | * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved.
5 | * =============================================================================
6 | *
7 | * This program is free software; you can redistribute it and/or modify it under
8 | * the terms of the GNU General Public License, version 3.0, as published by the
9 | * Free Software Foundation.
10 | *
11 | * This program is distributed in the hope that it will be useful, but WITHOUT
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 | * details.
15 | *
16 | * You should have received a copy of the GNU General Public License along with
17 | * this program. If not, see .
18 | */
19 |
20 | #include "extension.h"
21 |
22 | #ifndef PLATFORM_ARCH_FOLDER
23 | #define PLATFORM_ARCH_FOLDER ""
24 | #endif
25 |
26 | #include
27 |
28 | #include
29 | #include "MemoryDownloader.h"
30 |
31 | #if defined _LINUX
32 | #include "client/linux/handler/exception_handler.h"
33 | #include "common/linux/linux_libc_support.h"
34 | #include "third_party/lss/linux_syscall_support.h"
35 | #include "common/linux/dump_symbols.h"
36 | #include "common/path_helper.h"
37 |
38 | #include
39 | #include
40 | #include
41 | #include
42 |
43 | class StderrInhibitor
44 | {
45 | FILE *saved_stderr = nullptr;
46 |
47 | public:
48 | StderrInhibitor() {
49 | saved_stderr = fdopen(dup(fileno(stderr)), "w");
50 | if (freopen(_PATH_DEVNULL, "w", stderr)) {
51 | // If it fails, not a lot we can (or should) do.
52 | // Add this brace section to silence gcc warnings.
53 | }
54 | }
55 |
56 | ~StderrInhibitor() {
57 | fflush(stderr);
58 | dup2(fileno(saved_stderr), fileno(stderr));
59 | fclose(saved_stderr);
60 | }
61 | };
62 |
63 | // Taken from https://hg.mozilla.org/mozilla-central/file/3eb7623b5e63b37823d5e9c562d56e586604c823/build/unix/stdc%2B%2Bcompat/stdc%2B%2Bcompat.cpp
64 | extern "C" void __attribute__((weak)) __cxa_throw_bad_array_new_length() {
65 | abort();
66 | }
67 |
68 | namespace std {
69 | /* We shouldn't be throwing exceptions at all, but it sadly turns out
70 | we call STL (inline) functions that do. */
71 | void __attribute__((weak)) __throw_out_of_range_fmt(char const* fmt, ...) {
72 | va_list ap;
73 | char buf[1024]; // That should be big enough.
74 |
75 | va_start(ap, fmt);
76 | vsnprintf(buf, sizeof(buf), fmt, ap);
77 | buf[sizeof(buf) - 1] = 0;
78 | va_end(ap);
79 |
80 | __throw_range_error(buf);
81 | }
82 | } // namespace std
83 |
84 | // Updated versions of the SM ones for C++14
85 | void operator delete(void *ptr, size_t sz) {
86 | free(ptr);
87 | }
88 |
89 | void operator delete[](void *ptr, size_t sz) {
90 | free(ptr);
91 | }
92 |
93 | #elif defined _WINDOWS
94 | #define _STDINT // ~.~
95 | #include "client/windows/handler/exception_handler.h"
96 |
97 | #else
98 | #error Bad platform.
99 | #endif
100 |
101 | #include
102 | #include
103 | #include
104 | #include
105 | #include
106 | #include
107 |
108 | #include
109 | #include
110 | #include
111 |
112 | Accelerator g_accelerator;
113 | SMEXT_LINK(&g_accelerator);
114 |
115 | IWebternet *webternet;
116 | IGameConfig *gameconfig;
117 |
118 | typedef void (*GetSpew_t)(char *buffer, size_t length);
119 | GetSpew_t GetSpew;
120 | #if defined _WINDOWS
121 | typedef void(__fastcall *GetSpewFastcall_t)(char *buffer, size_t length);
122 | GetSpewFastcall_t GetSpewFastcall;
123 | #endif
124 |
125 | char spewBuffer[65536]; // Hi.
126 |
127 | char crashMap[256];
128 | char crashGamePath[512];
129 | char crashCommandLine[1024];
130 | char crashSourceModPath[512];
131 | char crashGameDirectory[256];
132 | char crashSourceModVersion[32];
133 | char steamInf[1024];
134 |
135 | char dumpStoragePath[512];
136 | char logPath[512];
137 |
138 | google_breakpad::ExceptionHandler *handler = NULL;
139 |
140 | #if defined _LINUX
141 | void terminateHandler()
142 | {
143 | const char *msg = "missing exception";
144 | std::exception_ptr pEx = std::current_exception();
145 | if (pEx) {
146 | try {
147 | std::rethrow_exception(pEx);
148 | } catch(const std::exception &e) {
149 | msg = strdup(e.what());
150 | } catch(...) {
151 | msg = "unknown exception";
152 | }
153 | }
154 |
155 | size_t msgLength = strlen(msg) + 2;
156 | volatile char * volatile msgForCrashDumps = (char *)alloca(msgLength);
157 | strcpy((char *)msgForCrashDumps + 1, msg);
158 |
159 | abort();
160 | }
161 |
162 | void (*SignalHandler)(int, siginfo_t *, void *);
163 |
164 | const int kExceptionSignals[] = {
165 | SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS
166 | };
167 |
168 | const int kNumHandledSignals = sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
169 |
170 | static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
171 | {
172 | //printf("Wrote minidump to: %s\n", descriptor.path());
173 |
174 | if (succeeded) {
175 | sys_write(STDOUT_FILENO, "Wrote minidump to: ", 19);
176 | } else {
177 | sys_write(STDOUT_FILENO, "Failed to write minidump to: ", 29);
178 | }
179 |
180 | sys_write(STDOUT_FILENO, descriptor.path(), my_strlen(descriptor.path()));
181 | sys_write(STDOUT_FILENO, "\n", 1);
182 |
183 | if (!succeeded) {
184 | return succeeded;
185 | }
186 |
187 | my_strlcpy(dumpStoragePath, descriptor.path(), sizeof(dumpStoragePath));
188 | my_strlcat(dumpStoragePath, ".txt", sizeof(dumpStoragePath));
189 |
190 | int extra = sys_open(dumpStoragePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
191 | if (extra == -1) {
192 | sys_write(STDOUT_FILENO, "Failed to open metadata file!\n", 30);
193 | return succeeded;
194 | }
195 |
196 | sys_write(extra, "-------- CONFIG BEGIN --------", 30);
197 | sys_write(extra, "\nMap=", 5);
198 | sys_write(extra, crashMap, my_strlen(crashMap));
199 | sys_write(extra, "\nGamePath=", 10);
200 | sys_write(extra, crashGamePath, my_strlen(crashGamePath));
201 | sys_write(extra, "\nCommandLine=", 13);
202 | sys_write(extra, crashCommandLine, my_strlen(crashCommandLine));
203 | sys_write(extra, "\nSourceModPath=", 15);
204 | sys_write(extra, crashSourceModPath, my_strlen(crashSourceModPath));
205 | sys_write(extra, "\nGameDirectory=", 15);
206 | sys_write(extra, crashGameDirectory, my_strlen(crashGameDirectory));
207 | if (crashSourceModVersion[0]) {
208 | sys_write(extra, "\nSourceModVersion=", 18);
209 | sys_write(extra, crashSourceModVersion, my_strlen(crashSourceModVersion));
210 | }
211 | sys_write(extra, "\nExtensionVersion=", 18);
212 | sys_write(extra, SM_VERSION, my_strlen(SM_VERSION));
213 | sys_write(extra, "\nExtensionBuild=", 16);
214 | sys_write(extra, SM_BUILD_UNIQUEID, my_strlen(SM_BUILD_UNIQUEID));
215 | sys_write(extra, steamInf, my_strlen(steamInf));
216 | sys_write(extra, "\n-------- CONFIG END --------\n", 30);
217 |
218 | if (GetSpew) {
219 | GetSpew(spewBuffer, sizeof(spewBuffer));
220 |
221 | if (my_strlen(spewBuffer) > 0) {
222 | sys_write(extra, "-------- CONSOLE HISTORY BEGIN --------\n", 40);
223 | sys_write(extra, spewBuffer, my_strlen(spewBuffer));
224 | sys_write(extra, "-------- CONSOLE HISTORY END --------\n", 38);
225 | }
226 | }
227 |
228 | sys_close(extra);
229 |
230 | return succeeded;
231 | }
232 |
233 | void OnGameFrame(bool simulating)
234 | {
235 | std::set_terminate(terminateHandler);
236 |
237 | bool weHaveBeenFuckedOver = false;
238 | struct sigaction oact;
239 |
240 | for (int i = 0; i < kNumHandledSignals; ++i) {
241 | sigaction(kExceptionSignals[i], NULL, &oact);
242 |
243 | if (oact.sa_sigaction != SignalHandler) {
244 | weHaveBeenFuckedOver = true;
245 | break;
246 | }
247 | }
248 |
249 | if (!weHaveBeenFuckedOver) {
250 | return;
251 | }
252 |
253 | struct sigaction act;
254 | memset(&act, 0, sizeof(act));
255 | sigemptyset(&act.sa_mask);
256 |
257 | for (int i = 0; i < kNumHandledSignals; ++i) {
258 | sigaddset(&act.sa_mask, kExceptionSignals[i]);
259 | }
260 |
261 | act.sa_sigaction = SignalHandler;
262 | act.sa_flags = SA_ONSTACK | SA_SIGINFO;
263 |
264 | for (int i = 0; i < kNumHandledSignals; ++i) {
265 | sigaction(kExceptionSignals[i], &act, NULL);
266 | }
267 | }
268 |
269 | #elif defined _WINDOWS
270 | void *vectoredHandler = NULL;
271 |
272 | LONG CALLBACK BreakpadVectoredHandler(_In_ PEXCEPTION_POINTERS ExceptionInfo)
273 | {
274 | switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
275 | {
276 | case EXCEPTION_ACCESS_VIOLATION:
277 | case EXCEPTION_INVALID_HANDLE:
278 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
279 | case EXCEPTION_DATATYPE_MISALIGNMENT:
280 | case EXCEPTION_ILLEGAL_INSTRUCTION:
281 | case EXCEPTION_INT_DIVIDE_BY_ZERO:
282 | case EXCEPTION_STACK_OVERFLOW:
283 | case 0xC0000409: // STATUS_STACK_BUFFER_OVERRUN
284 | case 0xC0000374: // STATUS_HEAP_CORRUPTION
285 | break;
286 | case 0: // Valve use this for Sys_Error.
287 | if ((ExceptionInfo->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE) == 0)
288 | return EXCEPTION_CONTINUE_SEARCH;
289 | break;
290 | default:
291 | return EXCEPTION_CONTINUE_SEARCH;
292 | }
293 |
294 | if (handler->WriteMinidumpForException(ExceptionInfo))
295 | {
296 | // Stop the handler thread from deadlocking us.
297 | delete handler;
298 |
299 | // Stop Valve's handler being called.
300 | ExceptionInfo->ExceptionRecord->ExceptionCode = EXCEPTION_BREAKPOINT;
301 |
302 | return EXCEPTION_EXECUTE_HANDLER;
303 | } else {
304 | return EXCEPTION_CONTINUE_SEARCH;
305 | }
306 | }
307 |
308 | static bool dumpCallback(const wchar_t* dump_path,
309 | const wchar_t* minidump_id,
310 | void* context,
311 | EXCEPTION_POINTERS* exinfo,
312 | MDRawAssertionInfo* assertion,
313 | bool succeeded)
314 | {
315 | if (!succeeded) {
316 | printf("Failed to write minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id);
317 | return succeeded;
318 | }
319 |
320 | printf("Wrote minidump to: %ls\\%ls.dmp\n", dump_path, minidump_id);
321 |
322 | sprintf(dumpStoragePath, "%ls\\%ls.dmp.txt", dump_path, minidump_id);
323 |
324 | FILE *extra = fopen(dumpStoragePath, "wb");
325 | if (!extra) {
326 | printf("Failed to open metadata file!\n");
327 | return succeeded;
328 | }
329 |
330 | fprintf(extra, "-------- CONFIG BEGIN --------");
331 | fprintf(extra, "\nMap=%s", crashMap);
332 | fprintf(extra, "\nGamePath=%s", crashGamePath);
333 | fprintf(extra, "\nCommandLine=%s", crashCommandLine);
334 | fprintf(extra, "\nSourceModPath=%s", crashSourceModPath);
335 | fprintf(extra, "\nGameDirectory=%s", crashGameDirectory);
336 | if (crashSourceModVersion[0]) {
337 | fprintf(extra, "\nSourceModVersion=%s", crashSourceModVersion);
338 | }
339 | fprintf(extra, "\nExtensionVersion=%s", SM_VERSION);
340 | fprintf(extra, "\nExtensionBuild=%s", SM_BUILD_UNIQUEID);
341 | fprintf(extra, "%s", steamInf);
342 | fprintf(extra, "\n-------- CONFIG END --------\n");
343 |
344 | if (GetSpew || GetSpewFastcall) {
345 | if (GetSpew) {
346 | GetSpew(spewBuffer, sizeof(spewBuffer));
347 | } else if (GetSpewFastcall) {
348 | GetSpewFastcall(spewBuffer, sizeof(spewBuffer));
349 | }
350 |
351 | if (spewBuffer[0]) {
352 | fprintf(extra, "-------- CONSOLE HISTORY BEGIN --------\n%s-------- CONSOLE HISTORY END --------\n", spewBuffer);
353 | }
354 | }
355 |
356 | fclose(extra);
357 |
358 | return succeeded;
359 | }
360 |
361 | #else
362 | #error Bad platform.
363 | #endif
364 |
365 | class ClogInhibitor
366 | {
367 | std::streambuf *saved_clog = nullptr;
368 |
369 | public:
370 | ClogInhibitor() {
371 | saved_clog = std::clog.rdbuf();
372 | std::clog.rdbuf(nullptr);
373 | }
374 |
375 | ~ClogInhibitor() {
376 | std::clog.rdbuf(saved_clog);
377 | }
378 | };
379 |
380 | class UploadThread: public IThread
381 | {
382 | FILE *log = nullptr;
383 | char serverId[38] = "";
384 |
385 | void RunThread(IThreadHandle *pHandle) {
386 | rootconsole->ConsolePrint("Accelerator upload thread started.");
387 |
388 | log = fopen(logPath, "a");
389 | if (!log) {
390 | g_pSM->LogError(myself, "Failed to open Accelerator log file: %s", logPath);
391 | }
392 |
393 | char path[512];
394 | g_pSM->Format(path, sizeof(path), "%s/server-id.txt", dumpStoragePath);
395 | FILE *serverIdFile = fopen(path, "r");
396 | if (serverIdFile) {
397 | fread(serverId, 1, sizeof(serverId) - 1, serverIdFile);
398 | if (!feof(serverIdFile) || strlen(serverId) != 36) {
399 | serverId[0] = '\0';
400 | }
401 | fclose(serverIdFile);
402 | }
403 | if (!serverId[0]) {
404 | serverIdFile = fopen(path, "w");
405 | if (serverIdFile) {
406 | g_pSM->Format(serverId, sizeof(serverId), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
407 | rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, 0x40 | ((rand() % 255) & 0x0F), rand() % 255,
408 | 0x80 | ((rand() % 255) & 0x3F), rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255, rand() % 255);
409 | fputs(serverId, serverIdFile);
410 | fclose(serverIdFile);
411 | }
412 | }
413 |
414 | IDirectory *dumps = libsys->OpenDirectory(dumpStoragePath);
415 |
416 | int skip = 0;
417 | int count = 0;
418 | int failed = 0;
419 | char metapath[512];
420 | char presubmitToken[512];
421 | char response[512];
422 |
423 | while (dumps->MoreFiles()) {
424 | if (!dumps->IsEntryFile()) {
425 | dumps->NextEntry();
426 | continue;
427 | }
428 |
429 | const char *name = dumps->GetEntryName();
430 |
431 | int namelen = strlen(name);
432 | if (namelen < 4 || strcmp(&name[namelen-4], ".dmp") != 0) {
433 | dumps->NextEntry();
434 | continue;
435 | }
436 |
437 | g_pSM->Format(path, sizeof(path), "%s/%s", dumpStoragePath, name);
438 | g_pSM->Format(metapath, sizeof(metapath), "%s.txt", path);
439 |
440 | if (!libsys->PathExists(metapath)) {
441 | metapath[0] = '\0';
442 | }
443 |
444 | presubmitToken[0] = '\0';
445 | PresubmitResponse presubmitResponse = kPRUploadCrashDumpAndMetadata;
446 |
447 | const char *presubmitOption = g_pSM->GetCoreConfigValue("MinidumpPresubmit");
448 | bool canPresubmit = !presubmitOption || (tolower(presubmitOption[0]) == 'y' || presubmitOption[0] == '1');
449 |
450 | if (canPresubmit) {
451 | presubmitResponse = PresubmitCrashDump(path, presubmitToken, sizeof(presubmitToken));
452 | }
453 |
454 | switch (presubmitResponse) {
455 | case kPRLocalError:
456 | failed++;
457 | g_pSM->LogError(myself, "Accelerator failed to locally process crash dump");
458 | if (log) fprintf(log, "Failed to locally process crash dump");
459 | break;
460 | case kPRRemoteError:
461 | case kPRUploadCrashDumpAndMetadata:
462 | case kPRUploadMetadataOnly:
463 | if (UploadCrashDump((presubmitResponse == kPRUploadMetadataOnly) ? nullptr : path, metapath, presubmitToken, response, sizeof(response))) {
464 | count++;
465 | g_pSM->LogError(myself, "Accelerator uploaded crash dump: %s", response);
466 | if (log) fprintf(log, "Uploaded crash dump: %s\n", response);
467 | } else {
468 | failed++;
469 | g_pSM->LogError(myself, "Accelerator failed to upload crash dump: %s", response);
470 | if (log) fprintf(log, "Failed to upload crash dump: %s\n", response);
471 | }
472 | break;
473 | case kPRDontUpload:
474 | skip++;
475 | g_pSM->LogError(myself, "Accelerator crash dump upload skipped by server");
476 | if (log) fprintf(log, "Skipped due to server request\n");
477 | break;
478 | }
479 |
480 | if (metapath[0]) {
481 | unlink(metapath);
482 | }
483 |
484 | unlink(path);
485 |
486 | if (log) fflush(log);
487 |
488 | dumps->NextEntry();
489 | }
490 |
491 | libsys->CloseDirectory(dumps);
492 |
493 | if (log) {
494 | fclose(log);
495 | log = nullptr;
496 | }
497 |
498 | rootconsole->ConsolePrint("Accelerator upload thread finished. (%d skipped, %d uploaded, %d failed)", skip, count, failed);
499 | }
500 |
501 | void OnTerminate(IThreadHandle *pHandle, bool cancel) {
502 | rootconsole->ConsolePrint("Accelerator upload thread terminated. (canceled = %s)", (cancel ? "true" : "false"));
503 | }
504 |
505 | #if defined _LINUX
506 | bool UploadSymbolFile(const google_breakpad::CodeModule *module, const char *presubmitToken) {
507 | if (log) fprintf(log, "UploadSymbolFile\n");
508 | if (log) fflush(log);
509 |
510 | auto debugFile = module->debug_file();
511 | std::string vdsoOutputPath = "";
512 |
513 | if (false && debugFile == "linux-gate.so") {
514 | FILE *auxvFile = fopen("/proc/self/auxv", "rb");
515 | if (auxvFile) {
516 | char vdsoOutputPathBuffer[512];
517 | g_pSM->BuildPath(Path_SM, vdsoOutputPathBuffer, sizeof(vdsoOutputPathBuffer), "data/dumps/linux-gate.so");
518 | vdsoOutputPath = vdsoOutputPathBuffer;
519 |
520 | while (!feof(auxvFile)) {
521 | int auxvEntryId = 0;
522 | fread(&auxvEntryId, sizeof(auxvEntryId), 1, auxvFile);
523 | long auxvEntryValue = 0;
524 | fread(&auxvEntryValue, sizeof(auxvEntryValue), 1, auxvFile);
525 |
526 | if (auxvEntryId == 0) break;
527 | if (auxvEntryId != 33) continue; // AT_SYSINFO_EHDR
528 |
529 | #ifdef PLATFORM_X64
530 | Elf64_Ehdr *vdsoHdr = (Elf64_Ehdr *)auxvEntryValue;
531 | #else
532 | Elf32_Ehdr *vdsoHdr = (Elf32_Ehdr *)auxvEntryValue;
533 | #endif
534 | auto vdsoSize = vdsoHdr->e_shoff + (vdsoHdr->e_shentsize * vdsoHdr->e_shnum);
535 | void *vdsoBuffer = malloc(vdsoSize);
536 | memcpy(vdsoBuffer, vdsoHdr, vdsoSize);
537 |
538 | FILE *vdsoFile = fopen(vdsoOutputPath.c_str(), "wb");
539 | if (vdsoFile) {
540 | fwrite(vdsoBuffer, 1, vdsoSize, vdsoFile);
541 | fclose(vdsoFile);
542 | debugFile = vdsoOutputPath;
543 | }
544 |
545 | free(vdsoBuffer);
546 | break;
547 | }
548 |
549 | fclose(auxvFile);
550 | }
551 | }
552 |
553 | if (debugFile[0] != '/') {
554 | return false;
555 | }
556 |
557 | if (log) fprintf(log, "Submitting symbols for %s\n", debugFile.c_str());
558 | if (log) fflush(log);
559 |
560 | auto debugFileDir = google_breakpad::DirName(debugFile);
561 | std::vector debug_dirs{
562 | debugFileDir,
563 | debugFileDir + "/.debug",
564 | "/usr/lib/debug" + debugFileDir,
565 | };
566 |
567 | std::ostringstream outputStream;
568 | google_breakpad::DumpOptions options(ALL_SYMBOL_DATA, true, true);
569 |
570 | {
571 | StderrInhibitor stdrrInhibitor;
572 |
573 | if (!WriteSymbolFile(debugFile, debugFile, "Linux", debug_dirs, options, outputStream)) {
574 | outputStream.str("");
575 | outputStream.clear();
576 |
577 | // Try again without debug dirs.
578 | if (!WriteSymbolFile(debugFile, debugFile, "Linux", {}, options, outputStream)) {
579 | if (log) fprintf(log, "Failed to process symbol file\n");
580 | if (log) fflush(log);
581 | return false;
582 | }
583 | }
584 | }
585 |
586 | auto output = outputStream.str();
587 | // output = output.substr(0, output.find("\n"));
588 | // printf(">>> %s\n", output.c_str());
589 |
590 | if (debugFile == vdsoOutputPath) {
591 | unlink(vdsoOutputPath.c_str());
592 | }
593 |
594 | IWebForm *form = webternet->CreateForm();
595 |
596 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
597 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount);
598 |
599 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION);
600 | form->AddString("ServerID", serverId);
601 |
602 | if (presubmitToken && presubmitToken[0]) {
603 | form->AddString("PresubmitToken", presubmitToken);
604 | }
605 |
606 | form->AddString("symbol_file", output.c_str());
607 |
608 | MemoryDownloader data;
609 | IWebTransfer *xfer = webternet->CreateSession();
610 | xfer->SetFailOnHTTPError(true);
611 |
612 | const char *symbolUrl = g_pSM->GetCoreConfigValue("MinidumpSymbolUrl");
613 | if (!symbolUrl) symbolUrl = "http://crash.limetech.org/symbols/submit";
614 |
615 | bool symbolUploaded = xfer->PostAndDownload(symbolUrl, form, &data, NULL);
616 |
617 | if (!symbolUploaded) {
618 | if (log) fprintf(log, "Symbol upload failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode());
619 | if (log) fflush(log);
620 | return false;
621 | }
622 |
623 | int responseSize = data.GetSize();
624 | char *response = new char[responseSize + 1];
625 | strncpy(response, data.GetBuffer(), responseSize + 1);
626 | response[responseSize] = '\0';
627 | while (responseSize > 0 && response[responseSize - 1] == '\n') {
628 | response[--responseSize] = '\0';
629 | }
630 | if (log) fprintf(log, "Symbol upload complete: %s\n", response);
631 | delete[] response;
632 | if (log) fflush(log);
633 | return true;
634 | }
635 | #endif
636 |
637 | bool UploadModuleFile(const google_breakpad::CodeModule *module, const char *presubmitToken) {
638 | const auto &codeFile = module->code_file();
639 |
640 | #ifndef WIN32
641 | if (codeFile[0] != '/') {
642 | #else
643 | if (codeFile[1] != ':') {
644 | #endif
645 | return false;
646 | }
647 |
648 | if (log) fprintf(log, "Submitting binary for %s\n", codeFile.c_str());
649 | if (log) fflush(log);
650 |
651 | IWebForm *form = webternet->CreateForm();
652 |
653 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
654 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount);
655 |
656 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION);
657 | form->AddString("ServerID", serverId);
658 |
659 | if (presubmitToken && presubmitToken[0]) {
660 | form->AddString("PresubmitToken", presubmitToken);
661 | }
662 |
663 | form->AddString("debug_identifier", module->debug_identifier().c_str());
664 | form->AddString("code_identifier", module->code_identifier().c_str());
665 |
666 | form->AddFile("code_file", codeFile.c_str());
667 |
668 | MemoryDownloader data;
669 | IWebTransfer *xfer = webternet->CreateSession();
670 | xfer->SetFailOnHTTPError(true);
671 |
672 | const char *binaryUrl = g_pSM->GetCoreConfigValue("MinidumpBinaryUrl");
673 | if (!binaryUrl) binaryUrl = "http://crash.limetech.org/binary/submit";
674 |
675 | bool binaryUploaded = xfer->PostAndDownload(binaryUrl, form, &data, NULL);
676 |
677 | if (!binaryUploaded) {
678 | if (log) fprintf(log, "Binary upload failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode());
679 | if (log) fflush(log);
680 | return false;
681 | }
682 |
683 | int responseSize = data.GetSize();
684 | char *response = new char[responseSize + 1];
685 | strncpy(response, data.GetBuffer(), responseSize + 1);
686 | response[responseSize] = '\0';
687 | while (responseSize > 0 && response[responseSize - 1] == '\n') {
688 | response[--responseSize] = '\0';
689 | }
690 | if (log) fprintf(log, "Binary upload complete: %s\n", response);
691 | if (log) fflush(log);
692 | delete[] response;
693 |
694 | return true;
695 | }
696 |
697 | enum ModuleType {
698 | kMTUnknown,
699 | kMTSystem,
700 | kMTGame,
701 | kMTAddon,
702 | kMTExtension,
703 | };
704 |
705 | const char *ModuleTypeCode[5] = {
706 | "Unknown",
707 | "System",
708 | "Game",
709 | "Addon",
710 | "Extension",
711 | };
712 |
713 | #ifndef WIN32
714 | #define PATH_SEP "/"
715 | #else
716 | #define PATH_SEP "\\"
717 | #endif
718 |
719 | bool PathPrefixMatches(const std::string &prefix, const std::string &path) {
720 | #ifndef WIN32
721 | return strncmp(prefix.c_str(), path.c_str(), prefix.length()) == 0;
722 | #else
723 | return _strnicmp(prefix.c_str(), path.c_str(), prefix.length()) == 0;
724 | #endif
725 | }
726 |
727 | struct PathComparator {
728 | struct compare {
729 | bool operator() (const unsigned char &a, const unsigned char &b) const {
730 | #ifndef WIN32
731 | return a < b;
732 | #else
733 | return tolower(a) < tolower(b);
734 | #endif
735 | }
736 | };
737 |
738 | bool operator() (const std::string &a, const std::string &b) const {
739 | return !std::lexicographical_compare(
740 | a.begin(), a.end(),
741 | b.begin(), b.end(),
742 | compare());
743 | };
744 | };
745 |
746 | std::map modulePathMap;
747 | bool InitModuleClassificationMap(const std::string &base) {
748 | if (!modulePathMap.empty()) {
749 | modulePathMap.clear();
750 | }
751 |
752 | modulePathMap[base] = kMTGame;
753 | modulePathMap[std::string(crashGamePath) + PATH_SEP "addons" PATH_SEP] = kMTAddon;
754 | modulePathMap[std::string(crashSourceModPath) + PATH_SEP "extensions" PATH_SEP] = kMTExtension;
755 |
756 | return true;
757 | }
758 |
759 | ModuleType ClassifyModule(const google_breakpad::CodeModule *module) {
760 | if (modulePathMap.empty()) {
761 | return kMTUnknown;
762 | }
763 |
764 | const auto &codeFile = module->code_file();
765 |
766 | #ifndef WIN32
767 | if (codeFile == "linux-gate.so") {
768 | return kMTSystem;
769 | }
770 |
771 | if (codeFile[0] != '/') {
772 | #else
773 | if (codeFile[1] != ':') {
774 | #endif
775 | return kMTUnknown;
776 | }
777 |
778 | for (decltype(modulePathMap)::const_iterator i = modulePathMap.begin(); i != modulePathMap.end(); ++i) {
779 | if (PathPrefixMatches(i->first, codeFile)) {
780 | return i->second;
781 | }
782 | }
783 |
784 | return kMTSystem;
785 | }
786 |
787 | std::string PathnameStripper_Directory(const std::string &path) {
788 | std::string::size_type slash = path.rfind('/');
789 | std::string::size_type backslash = path.rfind('\\');
790 |
791 | std::string::size_type file_start = 0;
792 | if (slash != std::string::npos && (backslash == std::string::npos || slash > backslash)) {
793 | file_start = slash + 1;
794 | } else if (backslash != string::npos) {
795 | file_start = backslash + 1;
796 | }
797 |
798 | return path.substr(0, file_start);
799 | }
800 |
801 | enum PresubmitResponse {
802 | kPRLocalError,
803 | kPRRemoteError,
804 | kPRDontUpload,
805 | kPRUploadCrashDumpAndMetadata,
806 | kPRUploadMetadataOnly,
807 | };
808 |
809 | PresubmitResponse PresubmitCrashDump(const char *path, char *tokenBuffer, size_t tokenBufferLength) {
810 | google_breakpad::ProcessState processState;
811 | google_breakpad::ProcessResult processResult;
812 | google_breakpad::MinidumpProcessor minidumpProcessor(nullptr, nullptr);
813 |
814 | {
815 | ClogInhibitor clogInhibitor;
816 | processResult = minidumpProcessor.Process(path, &processState);
817 | }
818 |
819 | if (processResult != google_breakpad::PROCESS_OK) {
820 | return kPRLocalError;
821 | }
822 |
823 | std::string os_short = "";
824 | std::string cpu_arch = "";
825 | if (processState.system_info()) {
826 | os_short = processState.system_info()->os_short;
827 | if (os_short.empty()) {
828 | os_short = processState.system_info()->os;
829 | }
830 | cpu_arch = processState.system_info()->cpu;
831 | }
832 |
833 | int requestingThread = processState.requesting_thread();
834 | if (requestingThread == -1) {
835 | requestingThread = 0;
836 | }
837 |
838 | const google_breakpad::CallStack *stack = processState.threads()->at(requestingThread);
839 | if (!stack) {
840 | return kPRLocalError;
841 | }
842 |
843 | int frameCount = stack->frames()->size();
844 | if (frameCount > 1024) {
845 | frameCount = 1024;
846 | }
847 |
848 | std::ostringstream summaryStream;
849 | summaryStream << 2 << "|" << processState.time_date_stamp() << "|" << os_short << "|" << cpu_arch << "|" << processState.crashed() << "|" << processState.crash_reason() << "|" << std::hex << processState.crash_address() << std::dec << "|" << requestingThread;
850 |
851 | std::map moduleMap;
852 |
853 | unsigned int moduleCount = processState.modules() ? processState.modules()->module_count() : 0;
854 | for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
855 | auto module = processState.modules()->GetModuleAtIndex(moduleIndex);
856 | moduleMap[module] = moduleIndex;
857 |
858 | auto debugFile = google_breakpad::PathnameStripper::File(module->debug_file());
859 | auto debugIdentifier = module->debug_identifier();
860 |
861 | summaryStream << "|M|" << debugFile << "|" << debugIdentifier;
862 | }
863 |
864 | for (int frameIndex = 0; frameIndex < frameCount; ++frameIndex) {
865 | auto frame = stack->frames()->at(frameIndex);
866 |
867 | int moduleIndex = -1;
868 | auto moduleOffset = frame->ReturnAddress();
869 | if (frame->module) {
870 | moduleIndex = moduleMap[frame->module];
871 | moduleOffset -= frame->module->base_address();
872 | }
873 |
874 | summaryStream << "|F|" << moduleIndex << "|" << std::hex << moduleOffset << std::dec;
875 | }
876 |
877 | auto summaryLine = summaryStream.str();
878 | // printf("%s\n", summaryLine.c_str());
879 |
880 | IWebForm *form = webternet->CreateForm();
881 |
882 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
883 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount);
884 |
885 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION);
886 | form->AddString("ServerID", serverId);
887 |
888 | form->AddString("CrashSignature", summaryLine.c_str());
889 |
890 | MemoryDownloader data;
891 | IWebTransfer *xfer = webternet->CreateSession();
892 | xfer->SetFailOnHTTPError(true);
893 |
894 | const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl");
895 | if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit";
896 |
897 | bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL);
898 |
899 | if (!uploaded) {
900 | if (log) fprintf(log, "Presubmit failed: %s (%d)\n", xfer->LastErrorMessage(), xfer->LastErrorCode());
901 | return kPRRemoteError;
902 | }
903 |
904 | int responseSize = data.GetSize();
905 | char *response = new char[responseSize + 1];
906 | strncpy(response, data.GetBuffer(), responseSize + 1);
907 | response[responseSize] = '\0';
908 | while (responseSize > 0 && response[responseSize - 1] == '\n') {
909 | response[--responseSize] = '\0';
910 | }
911 | //if (log) fprintf(log, "Presubmit complete: %s\n", response);
912 |
913 | if (responseSize < 2) {
914 | if (log) fprintf(log, "Presubmit response too short\n");
915 | delete[] response;
916 | return kPRRemoteError;
917 | }
918 |
919 | if (response[0] == 'E') {
920 | if (log) fprintf(log, "Presubmit error: %s\n", &response[2]);
921 | delete[] response;
922 | return kPRRemoteError;
923 | }
924 |
925 | PresubmitResponse presubmitResponse = kPRRemoteError;
926 | if (response[0] == 'Y') presubmitResponse = kPRUploadCrashDumpAndMetadata;
927 | else if (response[0] == 'N') presubmitResponse = kPRDontUpload;
928 | else if (response[0] == 'M') presubmitResponse = kPRUploadMetadataOnly;
929 | else return kPRRemoteError;
930 |
931 | if (response[1] != '|') {
932 | if (log) fprintf(log, "Response delimiter missing\n");
933 | delete[] response;
934 | return kPRRemoteError;
935 | }
936 |
937 | unsigned int responseCount = responseSize - 2;
938 | if (responseCount < moduleCount) {
939 | if (log) fprintf(log, "Response module list doesn't match sent list (%d < %d)\n", responseCount, moduleCount);
940 | delete[] response;
941 | return presubmitResponse;
942 | }
943 |
944 | // There was a presubmit token included.
945 | if (tokenBuffer && responseCount > moduleCount && response[2 + moduleCount] == '|') {
946 | int tokenStart = 2 + moduleCount + 1;
947 | int tokenEnd = tokenStart;
948 | while (tokenEnd < responseSize && response[tokenEnd] != '|') {
949 | tokenEnd++;
950 | }
951 |
952 | size_t tokenLength = tokenEnd - tokenStart;
953 | if (tokenLength < tokenBufferLength) {
954 | strncpy(tokenBuffer, &response[tokenStart], tokenLength);
955 | tokenBuffer[tokenLength] = '\0';
956 | }
957 |
958 | if (log) fprintf(log, "Got a presubmit token from server: %s\n", tokenBuffer);
959 | }
960 |
961 | if (moduleCount > 0) {
962 | auto mainModule = processState.modules()->GetMainModule();
963 | auto executableBaseDir = PathnameStripper_Directory(mainModule->code_file());
964 | InitModuleClassificationMap(executableBaseDir);
965 |
966 | // 0 = Disabled
967 | // 1 = System Only
968 | // 2 = System + Game
969 | // 3 = System + Game + Addons
970 | const char *symbolSubmitOptionStr = g_pSM->GetCoreConfigValue("MinidumpSymbolUpload");
971 | int symbolSubmitOption = symbolSubmitOptionStr ? atoi(symbolSubmitOptionStr) : 3;
972 |
973 | const char *binarySubmitOption = g_pSM->GetCoreConfigValue("MinidumpBinaryUpload");
974 | bool canBinarySubmit = !binarySubmitOption || (tolower(binarySubmitOption[0]) == 'y' || binarySubmitOption[0] == '1');
975 |
976 | for (unsigned int moduleIndex = 0; moduleIndex < moduleCount; ++moduleIndex) {
977 | bool submitSymbols = false;
978 | bool submitBinary = (response[2 + moduleIndex] == 'U');
979 |
980 | #if defined _LINUX
981 | submitSymbols = (response[2 + moduleIndex] == 'Y');
982 | #endif
983 |
984 | if (!submitSymbols && !submitBinary) {
985 | continue;
986 | }
987 | if (log) fprintf(log, "Getting module at index %d\n", moduleIndex);
988 | if (log) fflush(log);
989 |
990 | auto module = processState.modules()->GetModuleAtIndex(moduleIndex);
991 |
992 | auto moduleType = ClassifyModule(module);
993 | if (log) fprintf(log, "Classified module %s as %s\n", module->code_file().c_str(), ModuleTypeCode[moduleType]);
994 | if (log) fflush(log);
995 | switch (moduleType) {
996 | case kMTUnknown:
997 | continue;
998 | case kMTSystem:
999 | if (symbolSubmitOption < 1) {
1000 | continue;
1001 | }
1002 | break;
1003 | case kMTGame:
1004 | if (symbolSubmitOption < 2) {
1005 | continue;
1006 | }
1007 | break;
1008 | case kMTAddon:
1009 | case kMTExtension:
1010 | if (symbolSubmitOption < 3) {
1011 | continue;
1012 | }
1013 | break;
1014 | }
1015 |
1016 | if (canBinarySubmit && submitBinary) {
1017 | UploadModuleFile(module, tokenBuffer);
1018 | }
1019 |
1020 | #if defined _LINUX
1021 | if (submitSymbols) {
1022 | UploadSymbolFile(module, tokenBuffer);
1023 | }
1024 | #endif
1025 | }
1026 | }
1027 | if (log) fprintf(log, "PresubmitCrashDump complete\n");
1028 | if (log) fflush(log);
1029 |
1030 | delete[] response;
1031 | return presubmitResponse;
1032 | }
1033 |
1034 | bool UploadCrashDump(const char *path, const char *metapath, const char *presubmitToken, char *response, int maxlen) {
1035 | IWebForm *form = webternet->CreateForm();
1036 |
1037 | const char *minidumpAccount = g_pSM->GetCoreConfigValue("MinidumpAccount");
1038 | if (minidumpAccount && minidumpAccount[0]) form->AddString("UserID", minidumpAccount);
1039 |
1040 | form->AddString("GameDirectory", crashGameDirectory);
1041 | form->AddString("ExtensionVersion", SMEXT_CONF_VERSION);
1042 | form->AddString("ServerID", serverId);
1043 |
1044 | if (presubmitToken && presubmitToken[0]) {
1045 | form->AddString("PresubmitToken", presubmitToken);
1046 | }
1047 |
1048 | if (path && path[0]) {
1049 | form->AddFile("upload_file_minidump", path);
1050 | }
1051 |
1052 | if (metapath && metapath[0]) {
1053 | form->AddFile("upload_file_metadata", metapath);
1054 | }
1055 |
1056 | MemoryDownloader data;
1057 | IWebTransfer *xfer = webternet->CreateSession();
1058 | xfer->SetFailOnHTTPError(true);
1059 |
1060 | const char *minidumpUrl = g_pSM->GetCoreConfigValue("MinidumpUrl");
1061 | if (!minidumpUrl) minidumpUrl = "http://crash.limetech.org/submit";
1062 |
1063 | bool uploaded = xfer->PostAndDownload(minidumpUrl, form, &data, NULL);
1064 |
1065 | if (response) {
1066 | if (uploaded) {
1067 | int responseSize = data.GetSize();
1068 | if (responseSize >= maxlen) responseSize = maxlen - 1;
1069 | strncpy(response, data.GetBuffer(), responseSize);
1070 | response[responseSize] = '\0';
1071 | while (responseSize > 0 && response[responseSize - 1] == '\n') {
1072 | response[--responseSize] = '\0';
1073 | }
1074 | } else {
1075 | g_pSM->Format(response, maxlen, "%s (%d)", xfer->LastErrorMessage(), xfer->LastErrorCode());
1076 | }
1077 | }
1078 |
1079 | return uploaded;
1080 | }
1081 | } uploadThread;
1082 |
1083 | class VFuncEmptyClass {};
1084 |
1085 | const char *GetCmdLine()
1086 | {
1087 | static int getCmdLineOffset = 0;
1088 | if (getCmdLineOffset == 0) {
1089 | if (!gameconfig || !gameconfig->GetOffset("GetCmdLine", &getCmdLineOffset)) {
1090 | return "";
1091 | }
1092 | if (getCmdLineOffset == 0) {
1093 | return "";
1094 | }
1095 | }
1096 |
1097 | void *cmdline = gamehelpers->GetValveCommandLine();
1098 | void **vtable = *(void ***)cmdline;
1099 | void *vfunc = vtable[getCmdLineOffset];
1100 |
1101 | union {
1102 | const char *(VFuncEmptyClass::*mfpnew)();
1103 | #ifndef WIN32
1104 | struct {
1105 | void *addr;
1106 | intptr_t adjustor;
1107 | } s;
1108 | } u;
1109 | u.s.addr = vfunc;
1110 | u.s.adjustor = 0;
1111 | #else
1112 | void *addr;
1113 | } u;
1114 | u.addr = vfunc;
1115 | #endif
1116 |
1117 | return (const char *)(reinterpret_cast(cmdline)->*u.mfpnew)();
1118 | }
1119 |
1120 | bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late)
1121 | {
1122 | sharesys->AddDependency(myself, "webternet.ext", true, true);
1123 | SM_GET_IFACE(WEBTERNET, webternet);
1124 |
1125 | g_pSM->BuildPath(Path_SM, dumpStoragePath, sizeof(dumpStoragePath), "data/dumps");
1126 |
1127 | if (!libsys->IsPathDirectory(dumpStoragePath))
1128 | {
1129 | if (!libsys->CreateFolder(dumpStoragePath))
1130 | {
1131 | if (error)
1132 | g_pSM->Format(error, maxlength, "%s didn't exist and we couldn't create it :(", dumpStoragePath);
1133 | return false;
1134 | }
1135 | }
1136 |
1137 | g_pSM->BuildPath(Path_SM, logPath, sizeof(logPath), "logs/accelerator.log");
1138 |
1139 | // Get these early so the upload thread can use them.
1140 | strncpy(crashGamePath, g_pSM->GetGamePath(), sizeof(crashGamePath) - 1);
1141 | strncpy(crashSourceModPath, g_pSM->GetSourceModPath(), sizeof(crashSourceModPath) - 1);
1142 | strncpy(crashGameDirectory, g_pSM->GetGameFolderName(), sizeof(crashGameDirectory) - 1);
1143 |
1144 | threader->MakeThread(&uploadThread);
1145 |
1146 | do {
1147 | char gameconfigError[256];
1148 | if (!gameconfs->LoadGameConfigFile("accelerator.games", &gameconfig, gameconfigError, sizeof(gameconfigError))) {
1149 | smutils->LogMessage(myself, "WARNING: Failed to load gamedata file, console output and command line will not be included in crash reports: %s", gameconfigError);
1150 | break;
1151 | }
1152 |
1153 | bool useFastcall = false;
1154 |
1155 |
1156 | #if defined _WINDOWS
1157 | const char *fastcall = gameconfig->GetKeyValue("UseFastcall");
1158 | if (fastcall && strcmp(fastcall, "yes") == 0) {
1159 | useFastcall = true;
1160 | }
1161 |
1162 | if (useFastcall && !gameconfig->GetMemSig("GetSpewFastcall", (void **)&GetSpewFastcall)) {
1163 | smutils->LogMessage(myself, "WARNING: GetSpewFastcall not found in gamedata, console output will not be included in crash reports.");
1164 | break;
1165 | }
1166 | #endif
1167 |
1168 | if (!useFastcall && !gameconfig->GetMemSig("GetSpew", (void **)&GetSpew)) {
1169 | smutils->LogMessage(myself, "WARNING: GetSpew not found in gamedata, console output will not be included in crash reports.");
1170 | break;
1171 | }
1172 |
1173 | if (!GetSpew
1174 | #if defined _WINDOWS
1175 | && !GetSpewFastcall
1176 | #endif
1177 | ) {
1178 | smutils->LogMessage(myself, "WARNING: Sigscan for GetSpew failed, console output will not be included in crash reports.");
1179 | break;
1180 | }
1181 | } while(false);
1182 |
1183 | #if defined _LINUX
1184 | google_breakpad::MinidumpDescriptor descriptor(dumpStoragePath);
1185 | handler = new google_breakpad::ExceptionHandler(descriptor, NULL, dumpCallback, NULL, true, -1);
1186 |
1187 | struct sigaction oact;
1188 | sigaction(SIGSEGV, NULL, &oact);
1189 | SignalHandler = oact.sa_sigaction;
1190 |
1191 | g_pSM->AddGameFrameHook(OnGameFrame);
1192 | #elif defined _WINDOWS
1193 | wchar_t *buf = new wchar_t[sizeof(dumpStoragePath)];
1194 | size_t num_chars = mbstowcs(buf, dumpStoragePath, sizeof(dumpStoragePath));
1195 |
1196 | handler = new google_breakpad::ExceptionHandler(
1197 | std::wstring(buf, num_chars), NULL, dumpCallback, NULL, google_breakpad::ExceptionHandler::HANDLER_ALL,
1198 | static_cast(MiniDumpWithUnloadedModules | MiniDumpWithFullMemoryInfo), static_cast(NULL), NULL);
1199 |
1200 | vectoredHandler = AddVectoredExceptionHandler(0, BreakpadVectoredHandler);
1201 |
1202 | delete buf;
1203 | #else
1204 | #error Bad platform.
1205 | #endif
1206 |
1207 | do {
1208 | char spJitPath[512];
1209 | g_pSM->BuildPath(Path_SM, spJitPath, sizeof(spJitPath), "bin/" PLATFORM_ARCH_FOLDER "sourcepawn.jit.x86." PLATFORM_LIB_EXT);
1210 |
1211 | char spJitError[255];
1212 | std::unique_ptr spJit(libsys->OpenLibrary(spJitPath, spJitError, sizeof(spJitError)));
1213 | if (!spJit) {
1214 | smutils->LogMessage(myself, "WARNING: Failed to load SourcePawn library %s: %s", spJitPath, spJitError);
1215 | break;
1216 | }
1217 |
1218 | GetSourcePawnFactoryFn factoryFn = (GetSourcePawnFactoryFn)spJit->GetSymbolAddress("GetSourcePawnFactory");
1219 | if (!factoryFn) {
1220 | smutils->LogMessage(myself, "WARNING: SourcePawn library is out of date: No factory function.");
1221 | break;
1222 | }
1223 |
1224 | ISourcePawnFactory *spFactory = factoryFn(0x0207);
1225 | if (!spFactory) {
1226 | smutils->LogMessage(myself, "WARNING: SourcePawn library is out of date: Failed to get version 2.7", 0x0207);
1227 | break;
1228 | }
1229 |
1230 | ISourcePawnEnvironment *spEnvironment = spFactory->CurrentEnvironment();
1231 | if (!spEnvironment) {
1232 | smutils->LogMessage(myself, "WARNING: Could not get SourcePawn environment.");
1233 | break;
1234 | }
1235 |
1236 | ISourcePawnEngine2 *spEngine2 = spEnvironment->APIv2();
1237 | if (!spEngine2) {
1238 | smutils->LogMessage(myself, "WARNING: Could not get SourcePawn engine2.");
1239 | break;
1240 | }
1241 |
1242 | strncpy(crashSourceModVersion, spEngine2->GetVersionString(), sizeof(crashSourceModVersion));
1243 | } while(false);
1244 |
1245 | plsys->AddPluginsListener(this);
1246 |
1247 | IPluginIterator *iterator = plsys->GetPluginIterator();
1248 | while (iterator->MorePlugins()) {
1249 | IPlugin *plugin = iterator->GetPlugin();
1250 | if (plugin->GetStatus() == Plugin_Running) {
1251 | this->OnPluginLoaded(plugin);
1252 | }
1253 | iterator->NextPlugin();
1254 | }
1255 | delete iterator;
1256 |
1257 | strncpy(crashCommandLine, GetCmdLine(), sizeof(crashCommandLine) - 1);
1258 |
1259 | char steamInfPath[512];
1260 | g_pSM->BuildPath(Path_Game, steamInfPath, sizeof(steamInfPath), "steam.inf");
1261 |
1262 | FILE *steamInfFile = fopen(steamInfPath, "rb");
1263 | if (steamInfFile) {
1264 | char steamInfTemp[1024] = {0};
1265 | fread(steamInfTemp, sizeof(char), sizeof(steamInfTemp) - 1, steamInfFile);
1266 |
1267 | fclose(steamInfFile);
1268 |
1269 | unsigned commentChars = 0;
1270 | unsigned valueChars = 0;
1271 | unsigned source = 0;
1272 | strcpy(steamInf, "\nSteam_");
1273 | unsigned target = 7; // strlen("\nSteam_");
1274 | while (true) {
1275 | if (steamInfTemp[source] == '\0') {
1276 | source++;
1277 | break;
1278 | }
1279 | if (steamInfTemp[source] == '/') {
1280 | source++;
1281 | commentChars++;
1282 | continue;
1283 | }
1284 | if (commentChars == 1) {
1285 | commentChars = 0;
1286 | steamInf[target++] = '/';
1287 | valueChars++;
1288 | }
1289 | if (steamInfTemp[source] == '\r') {
1290 | source++;
1291 | continue;
1292 | }
1293 | if (steamInfTemp[source] == '\n') {
1294 | commentChars = 0;
1295 | source++;
1296 | if (steamInfTemp[source] == '\0') {
1297 | break;
1298 | }
1299 | if (valueChars > 0) {
1300 | valueChars = 0;
1301 | strcpy(&steamInf[target], "\nSteam_");
1302 | target += 7;
1303 | }
1304 | continue;
1305 | }
1306 | if (commentChars >= 2) {
1307 | source++;
1308 | continue;
1309 | }
1310 | steamInf[target++] = steamInfTemp[source++];
1311 | valueChars++;
1312 | }
1313 | }
1314 |
1315 | if (late) {
1316 | this->OnCoreMapStart(NULL, 0, 0);
1317 | }
1318 |
1319 | return true;
1320 | }
1321 |
1322 | void Accelerator::SDK_OnUnload()
1323 | {
1324 | plsys->RemovePluginsListener(this);
1325 |
1326 | #if defined _LINUX
1327 | g_pSM->RemoveGameFrameHook(OnGameFrame);
1328 | #elif defined _WINDOWS
1329 | if (vectoredHandler) {
1330 | RemoveVectoredExceptionHandler(vectoredHandler);
1331 | }
1332 | #else
1333 | #error Bad platform.
1334 | #endif
1335 |
1336 | delete handler;
1337 | }
1338 |
1339 | void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
1340 | {
1341 | strncpy(crashMap, gamehelpers->GetCurrentMap(), sizeof(crashMap) - 1);
1342 | }
1343 |
1344 | /* 010 Editor Template
1345 | uint64 headerMagic;
1346 | uint32 version;
1347 | uint32 size;
1348 | uint32 count;
1349 | struct {
1350 | uint32 size;
1351 | uint32 context ;
1352 | char file[];
1353 | uint32 count;
1354 | struct {
1355 | uint32 pcode ;
1356 | char name[];
1357 | } functions[count] ;
1358 | } plugins[count] ;
1359 | uint64 tailMagic;
1360 | */
1361 |
1362 | unsigned char *serializedPluginContexts = nullptr;
1363 | std::map pluginContextMap;
1364 |
1365 | void SerializePluginContexts()
1366 | {
1367 | if (serializedPluginContexts) {
1368 | handler->UnregisterAppMemory(serializedPluginContexts);
1369 | free(serializedPluginContexts);
1370 | serializedPluginContexts = nullptr;
1371 | }
1372 |
1373 | uint32_t count = pluginContextMap.size();
1374 | if (count == 0) {
1375 | return;
1376 | }
1377 |
1378 | uint32_t size = 0;
1379 | size += sizeof(uint64_t); // header magic
1380 | size += sizeof(uint32_t); // version
1381 | size += sizeof(uint32_t); // size
1382 | size += sizeof(uint32_t); // count
1383 |
1384 | for (auto &it : pluginContextMap) {
1385 | unsigned char *buffer = it.second;
1386 |
1387 | uint32_t bufferSize;
1388 | memcpy(&bufferSize, buffer, sizeof(uint32_t));
1389 |
1390 | size += bufferSize;
1391 | }
1392 |
1393 | size += sizeof(uint64_t); // tail magic
1394 |
1395 | serializedPluginContexts = (unsigned char *)malloc(size);
1396 | handler->RegisterAppMemory(serializedPluginContexts, size);
1397 | unsigned char *cursor = serializedPluginContexts;
1398 |
1399 | uint64_t headerMagic = 103582791429521979ULL;
1400 | memcpy(cursor, &headerMagic, sizeof(uint64_t));
1401 | cursor += sizeof(uint64_t);
1402 |
1403 | uint32_t version = 1;
1404 | memcpy(cursor, &version, sizeof(uint32_t));
1405 | cursor += sizeof(uint32_t);
1406 |
1407 | memcpy(cursor, &size, sizeof(uint32_t));
1408 | cursor += sizeof(uint32_t);
1409 |
1410 | memcpy(cursor, &count, sizeof(uint32_t));
1411 | cursor += sizeof(uint32_t);
1412 |
1413 | for (auto &it : pluginContextMap) {
1414 | unsigned char *buffer = it.second;
1415 |
1416 | uint32_t bufferSize;
1417 | memcpy(&bufferSize, buffer, sizeof(uint32_t));
1418 |
1419 | memcpy(cursor, buffer, bufferSize);
1420 | cursor += bufferSize;
1421 | }
1422 |
1423 | uint64_t tailMagic = 76561197987819599ULL;
1424 | memcpy(cursor, &tailMagic, sizeof(uint64_t));
1425 | cursor += sizeof(uint64_t);
1426 | }
1427 |
1428 | void Accelerator::OnPluginLoaded(IPlugin *plugin)
1429 | {
1430 | IPluginRuntime *runtime = plugin->GetRuntime();
1431 | IPluginContext *context = plugin->GetBaseContext();
1432 | if (!runtime || !context) {
1433 | return;
1434 | }
1435 |
1436 | const char *filename = plugin->GetFilename();
1437 | size_t filenameSize = strlen(filename) + 1;
1438 |
1439 | uint32_t size = 0;
1440 | size += sizeof(uint32_t); // size
1441 | size += sizeof(void *); // GetBaseContext
1442 | size += filenameSize;
1443 |
1444 | uint32_t count = runtime->GetPublicsNum();
1445 | size += sizeof(uint32_t); // count
1446 | size += count * sizeof(uint32_t); // pubinfo->code_offs
1447 |
1448 | for (uint32_t i = 0; i < count; ++i) {
1449 | sp_public_t *pubinfo;
1450 | runtime->GetPublicByIndex(i, &pubinfo);
1451 |
1452 | size += strlen(pubinfo->name) + 1;
1453 | }
1454 |
1455 | unsigned char *buffer = (unsigned char *)malloc(size);
1456 | unsigned char *cursor = buffer;
1457 |
1458 | memcpy(cursor, &size, sizeof(uint32_t));
1459 | cursor += sizeof(uint32_t);
1460 |
1461 | memcpy(cursor, &context, sizeof(void *));
1462 | cursor += sizeof(void *);
1463 |
1464 | memcpy(cursor, filename, filenameSize);
1465 | cursor += filenameSize;
1466 |
1467 | memcpy(cursor, &count, sizeof(uint32_t));
1468 | cursor += sizeof(uint32_t);
1469 |
1470 | for (uint32_t i = 0; i < count; ++i) {
1471 | sp_public_t *pubinfo;
1472 | runtime->GetPublicByIndex(i, &pubinfo);
1473 |
1474 | memcpy(cursor, &pubinfo->code_offs, sizeof(uint32_t));
1475 | cursor += sizeof(uint32_t);
1476 |
1477 | size_t nameSize = strlen(pubinfo->name) + 1;
1478 | memcpy(cursor, pubinfo->name, nameSize);
1479 | cursor += nameSize;
1480 | }
1481 |
1482 | pluginContextMap[context] = buffer;
1483 |
1484 | SerializePluginContexts();
1485 | }
1486 |
1487 | void Accelerator::OnPluginUnloaded(IPlugin *plugin)
1488 | {
1489 | IPluginContext *context = plugin->GetBaseContext();
1490 | if (!context) {
1491 | return;
1492 | }
1493 |
1494 | auto it = pluginContextMap.find(context);
1495 | if (it != pluginContextMap.end()) {
1496 | free(it->second);
1497 | pluginContextMap.erase(it);
1498 | }
1499 |
1500 | SerializePluginContexts();
1501 | }
1502 |
--------------------------------------------------------------------------------
/extension/extension.h:
--------------------------------------------------------------------------------
1 | /**
2 | * =============================================================================
3 | * Accelerator Extension
4 | * Copyright (C) 2009-2010 Asher Baker (asherkin). All rights reserved.
5 | * =============================================================================
6 | *
7 | * This program is free software; you can redistribute it and/or modify it under
8 | * the terms of the GNU General Public License, version 3.0, as published by the
9 | * Free Software Foundation.
10 | *
11 | * This program is distributed in the hope that it will be useful, but WITHOUT
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 | * details.
15 | *
16 | * You should have received a copy of the GNU General Public License along with
17 | * this program. If not, see .
18 | *
19 | */
20 |
21 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
22 | #define _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
23 |
24 | /**
25 | * @file extension.hpp
26 | * @brief Accelerator extension code header.
27 | */
28 |
29 | #include "smsdk_ext.h"
30 |
31 | /**
32 | * @brief Sample implementation of the SDK Extension.
33 | */
34 | class Accelerator : public SDKExtension, IPluginsListener
35 | {
36 | public: // SDKExtension
37 | /**
38 | * @brief This is called after the initial loading sequence has been processed.
39 | *
40 | * @param error Error message buffer.
41 | * @param maxlength Size of error message buffer.
42 | * @param late Whether or not the module was loaded after map load.
43 | * @return True to succeed loading, false to fail.
44 | */
45 | virtual bool SDK_OnLoad(char *error, size_t maxlen, bool late);
46 |
47 | /**
48 | * @brief This is called right before the extension is unloaded.
49 | */
50 | virtual void SDK_OnUnload();
51 |
52 | /**
53 | * @brief Called on server activation before plugins receive the OnServerLoad forward.
54 | *
55 | * @param pEdictList Edicts list.
56 | * @param edictCount Number of edicts in the list.
57 | * @param clientMax Maximum number of clients allowed in the server.
58 | */
59 | virtual void OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax);
60 |
61 | public: // IPluginsListener
62 | /**
63 | * @brief Called when a plugin is fully loaded successfully.
64 | */
65 | virtual void OnPluginLoaded(IPlugin *plugin);
66 |
67 | /**
68 | * @brief Called when a plugin is unloaded (only if fully loaded).
69 | */
70 | virtual void OnPluginUnloaded(IPlugin *plugin);
71 | };
72 |
73 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_
74 |
--------------------------------------------------------------------------------
/extension/smsdk_config.h:
--------------------------------------------------------------------------------
1 | /**
2 | * =============================================================================
3 | * Accelerator Extension
4 | * Copyright (C) 2011 Asher Baker (asherkin). All rights reserved.
5 | * =============================================================================
6 | *
7 | * This program is free software; you can redistribute it and/or modify it under
8 | * the terms of the GNU General Public License, version 3.0, as published by the
9 | * Free Software Foundation.
10 | *
11 | * This program is distributed in the hope that it will be useful, but WITHOUT
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 | * details.
15 | *
16 | * You should have received a copy of the GNU General Public License along with
17 | * this program. If not, see .
18 | *
19 | * As a special exception, AlliedModders LLC gives you permission to link the
20 | * code of this program (as well as its derivative works) to "Half-Life 2," the
21 | * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
22 | * by the Valve Corporation. You must obey the GNU General Public License in
23 | * all respects for all other code used. Additionally, AlliedModders LLC grants
24 | * this exception to all derivative works. AlliedModders LLC defines further
25 | * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
26 | * or .
27 | */
28 |
29 | #ifndef _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
30 | #define _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
31 |
32 | /**
33 | * @file smsdk_config.hpp
34 | * @brief Contains macros for configuring basic extension information.
35 | */
36 |
37 | #include "version.h" // SM_FULL_VERSION
38 |
39 | /* Basic information exposed publicly */
40 | #define SMEXT_CONF_NAME "Accelerator"
41 | #define SMEXT_CONF_DESCRIPTION "SRCDS Crash Handler"
42 | #define SMEXT_CONF_VERSION SM_FULL_VERSION
43 | #define SMEXT_CONF_AUTHOR "asherkin, kenzzer"
44 | #define SMEXT_CONF_URL "https://crash.limetech.org/"
45 | #define SMEXT_CONF_LOGTAG "CRASH"
46 | #define SMEXT_CONF_LICENSE "GPL"
47 | #define SMEXT_CONF_DATESTRING __DATE__
48 |
49 | /**
50 | * @brief Exposes plugin's main interface.
51 | */
52 | #define SMEXT_LINK(name) SDKExtension *g_pExtensionIface = name;
53 |
54 | /**
55 | * @brief Sets whether or not this plugin required Metamod.
56 | * NOTE: Uncomment to enable, comment to disable.
57 | */
58 | //#define SMEXT_CONF_METAMOD
59 |
60 | /** Enable interfaces you want to use here by uncommenting lines */
61 | //#define SMEXT_ENABLE_FORWARDSYS
62 | //#define SMEXT_ENABLE_HANDLESYS
63 | //#define SMEXT_ENABLE_PLAYERHELPERS
64 | //#define SMEXT_ENABLE_DBMANAGER
65 | #define SMEXT_ENABLE_GAMECONF
66 | //#define SMEXT_ENABLE_MEMUTILS
67 | #define SMEXT_ENABLE_GAMEHELPERS
68 | //#define SMEXT_ENABLE_TIMERSYS
69 | #define SMEXT_ENABLE_THREADER
70 | #define SMEXT_ENABLE_LIBSYS
71 | //#define SMEXT_ENABLE_MENUS
72 | //#define SMEXT_ENABLE_ADTFACTORY
73 | #define SMEXT_ENABLE_PLUGINSYS
74 | //#define SMEXT_ENABLE_ADMINSYS
75 | //#define SMEXT_ENABLE_TEXTPARSERS
76 | //#define SMEXT_ENABLE_USERMSGS
77 | //#define SMEXT_ENABLE_TRANSLATOR
78 | //#define SMEXT_ENABLE_NINVOKE
79 | #define SMEXT_ENABLE_ROOTCONSOLEMENU
80 |
81 | #endif // _INCLUDE_SOURCEMOD_EXTENSION_CONFIG_H_
82 |
--------------------------------------------------------------------------------
/extension/version.h:
--------------------------------------------------------------------------------
1 | #ifndef _INCLUDE_VERSION_INFORMATION_H_
2 | #define _INCLUDE_VERSION_INFORMATION_H_
3 |
4 | /**
5 | * @file Contains version information.
6 | * @brief This file will redirect to an autogenerated version if being compiled via
7 | * the build scripts.
8 | */
9 |
10 | #if defined SM_GENERATED_BUILD
11 | #include "version_auto.h"
12 | #else
13 |
14 | #ifndef SM_GENERATED_BUILD
15 | #undef BINARY_NAME
16 | #define BINARY_NAME "accelerator.ext.dll\0"
17 | #endif
18 |
19 | #define SM_BUILD_TAG "-manual"
20 | #define SM_BUILD_UNIQUEID "[MANUAL BUILD]"
21 | #define SM_VERSION "2.6.0"
22 | #define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
23 | #define SM_FILE_VERSION 2,6,0,0
24 |
25 | #endif
26 |
27 | #endif /* _INCLUDE_VERSION_INFORMATION_H_ */
28 |
--------------------------------------------------------------------------------
/extension/version.rc:
--------------------------------------------------------------------------------
1 | #include "winres.h"
2 |
3 | #include
4 |
5 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
6 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
7 | #pragma code_page(1252)
8 | #endif
9 |
10 | #ifndef SM_GENERATED_BUILD
11 | #define BINARY_NAME "accelerator.ext.dll\0"
12 | #endif
13 |
14 | VS_VERSION_INFO VERSIONINFO
15 | FILEVERSION SM_FILE_VERSION
16 | PRODUCTVERSION SM_FILE_VERSION
17 | FILEFLAGSMASK 0x17L
18 | #ifdef _DEBUG
19 | FILEFLAGS 0x1L
20 | #else
21 | FILEFLAGS 0x0L
22 | #endif
23 | FILEOS 0x4L
24 | FILETYPE 0x2L
25 | FILESUBTYPE 0x0L
26 | BEGIN
27 | BLOCK "StringFileInfo"
28 | BEGIN
29 | BLOCK "040904b0"
30 | BEGIN
31 | VALUE "Comments", "Accelerator Extension"
32 | VALUE "FileDescription", "SourceMod Accelerator Extension"
33 | VALUE "FileVersion", SM_BUILD_UNIQUEID
34 | VALUE "InternalName", "Accelerator"
35 | VALUE "LegalCopyright", "Copyright (c) 2012, Asher Baker"
36 | VALUE "OriginalFilename", BINARY_NAME
37 | VALUE "ProductName", "Accelerator Extension"
38 | VALUE "ProductVersion", SM_FULL_VERSION
39 | END
40 | END
41 | BLOCK "VarFileInfo"
42 | BEGIN
43 | VALUE "Translation", 0x0409, 0x04B0
44 | END
45 | END
46 |
--------------------------------------------------------------------------------
/gamedata/accelerator.games.txt:
--------------------------------------------------------------------------------
1 | "Games"
2 | {
3 | "#default"
4 | {
5 | "Offsets"
6 | {
7 | /*
8 | * This has never changed, let us hope it never does.
9 | */
10 | "GetCmdLine"
11 | {
12 | "windows" "2"
13 | "linux" "2"
14 | "linux64" "2"
15 | }
16 | }
17 |
18 | "Signatures"
19 | {
20 | /*
21 | * String: "\nConsole History (reversed)\n\n"
22 | * It's the function in the following branch.
23 | */
24 | "GetSpew"
25 | {
26 | "library" "engine"
27 | "linux" "@_Z7GetSpewPcj"
28 | "linux64" "@_Z7GetSpewPcm"
29 | }
30 | }
31 | }
32 |
33 | "tf"
34 | {
35 | "Signatures"
36 | {
37 | "GetSpew"
38 | {
39 | "library" "engine"
40 | "windows" "\x55\x8B\xEC\x83\xEC\x20\x8D\x45\xE0\x53\x57\x6A\x01\x68\x49\x03\x00\x00"
41 | }
42 | }
43 | }
44 |
45 | "cstrike"
46 | {
47 | "Signatures"
48 | {
49 | "GetSpew"
50 | {
51 | "library" "engine"
52 | "windows" "\x55\x8B\xEC\x53\xFF\x15\x2A\x2A\x2A\x2A\x8B\xD0\xBB\x2A\x2A\x2A\x2A\x3B\x15\x2A\x2A\x2A\x2A\x74\x2A\x8B\xCA\x33\xC0\xF0\x0F\xB1\x0B\x85\xC0\x74\x2A\xF3\x90\x6A\x00\x52\x8B\xCB\xFF\x15\x2A\x2A\x2A\x2A\xEB\x2A\xFF\x05\x2A\x2A\x2A\x2A\x0F\xB7\x05\x2A\x2A\x2A\x2A\x57"
53 | }
54 | }
55 | }
56 |
57 | "hl2mp"
58 | {
59 | "Signatures"
60 | {
61 | "GetSpew"
62 | {
63 | "library" "engine"
64 | "windows" "\x55\x8B\xEC\x53\xFF\x15\x2A\x2A\x2A\x2A\x8B\xD0\xBB\x2A\x2A\x2A\x2A\x3B\x15\x2A\x2A\x2A\x2A\x74\x2A\x8B\xCA\x33\xC0\xF0\x0F\xB1\x0B\x85\xC0\x74\x2A\xF3\x90\x6A\x00\x52\x8B\xCB\xFF\x15\x2A\x2A\x2A\x2A\xEB\x2A\xFF\x05\x2A\x2A\x2A\x2A\x0F\xB7\x05\x2A\x2A\x2A\x2A\x57"
65 | }
66 | }
67 | }
68 |
69 | "dod"
70 | {
71 | "Signatures"
72 | {
73 | "GetSpew"
74 | {
75 | "library" "engine"
76 | "windows" "\x55\x8B\xEC\x83\xEC\x20\x8D\x45\xE0\x53\x57\x6A\x01\x68\x49"
77 | }
78 | }
79 | }
80 |
81 | "csgo"
82 | {
83 | "Keys"
84 | {
85 | "UseFastcall" "yes"
86 | }
87 |
88 | "Signatures"
89 | {
90 | "GetSpew"
91 | {
92 | "library" "engine"
93 | "linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x1C\xE8\x2A\x2A\x2A\x2A\x89\xC2\xA1\x2A\x2A\x2A\x2A\x39\xD0\x0F\x84\x2A\x2A\x2A\x2A\x31\xC0\xF0\x0F\xB1\x15\x2A\x2A\x2A\x2A\x0F\x84\x2A\x2A\x2A\x2A\xF3\x90\x83\xEC\x04\x6A\x00\x52\x68\x2A\x2A\x2A\x2A\xE8\x2A\x2A\x2A\x2A\x83\xC4\x10\x8B\x45\x2A"
94 | }
95 |
96 | "GetSpewFastcall"
97 | {
98 | "library" "engine"
99 | "windows" "\x55\x8B\xEC\x51\x53\x56\x57\x8B\xFA\x8B\xD9"
100 | }
101 | }
102 | }
103 |
104 | "left4dead"
105 | {
106 | "Signatures"
107 | {
108 | "GetSpew"
109 | {
110 | "library" "engine"
111 | "windows" "\x53\x56\xFF\x15\x2A\x2A\x2A\x2A\x8B\xC8\xA1\x2A\x2A\x2A\x2A\x3B\xC8"
112 | /* 53 56 FF 15 ? ? ? ? 8B C8 A1 ? ? ? ? 3B C8 */
113 | }
114 | }
115 | }
116 |
117 | "left4dead2"
118 | {
119 | "Signatures"
120 | {
121 | "GetSpew"
122 | {
123 | "library" "engine"
124 | "windows" "\x55\x8B\xEC\x53\x56\xFF\x15\x2A\x2A\x2A\x2A\x8B\xC8\xA1\x2A\x2A\x2A\x2A\x3B\xC8"
125 | /* 55 8B EC 53 56 FF 15 ? ? ? ? 8B C8 A1 ? ? ? ? 3B C8 */ // Thanks to "cravenge" for the signature
126 | }
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/patches/0001-Ignore-invalid-modules-rather-than-bailing-on-the-en.patch:
--------------------------------------------------------------------------------
1 | From 1f35257db226d6d0189cff0832a97cccc639c91a Mon Sep 17 00:00:00 2001
2 | From: Asher Baker
3 | Date: Sun, 13 Jan 2019 12:34:45 +0000
4 | Subject: [PATCH 1/5] Ignore invalid modules rather than bailing on the entire
5 | module list
6 |
7 | ---
8 | src/processor/minidump.cc | 13 ++++++++++---
9 | 1 file changed, 10 insertions(+), 3 deletions(-)
10 |
11 | diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
12 | index 83e5a868..a7d564f2 100644
13 | --- a/src/processor/minidump.cc
14 | +++ b/src/processor/minidump.cc
15 | @@ -3195,7 +3195,7 @@ bool MinidumpModuleList::Read(uint32_t expected_size) {
16 | BPLOG(ERROR) << "MinidumpModuleList could not read required module "
17 | "auxiliary data for module " <<
18 | module_index << "/" << module_count;
19 | - return false;
20 | + continue;
21 | }
22 |
23 | // It is safe to use module->code_file() after successfully calling
24 | @@ -3207,7 +3207,14 @@ bool MinidumpModuleList::Read(uint32_t expected_size) {
25 | BPLOG(ERROR) << "MinidumpModuleList found bad base address for module "
26 | << module_index << "/" << module_count << ", "
27 | << module.code_file();
28 | - return false;
29 | + continue;
30 | + }
31 | +
32 | + if (module_size == static_cast(-1)) {
33 | + BPLOG(ERROR) << "MinidumpModuleList found bad size for module "
34 | + << module_index << "/" << module_count << ", "
35 | + << module.code_file();
36 | + continue;
37 | }
38 |
39 | // Some minidumps have additional modules in the list that are duplicates.
40 | @@ -3234,7 +3241,7 @@ bool MinidumpModuleList::Read(uint32_t expected_size) {
41 | << module_index << "/" << module_count << ", "
42 | << module.code_file() << ", " << HexString(base_address)
43 | << "+" << HexString(module_size);
44 | - return false;
45 | + continue;
46 | }
47 |
48 | // If failed due to apparent range overlap the cause may be the client
49 |
--------------------------------------------------------------------------------
/patches/0002-Write-FUNC-records-instead-of-PUBLIC-for-ELF-symbols.patch:
--------------------------------------------------------------------------------
1 | From 8aaf6e84a6704eb538f68a3e6fb6c3a8c93f1d8d Mon Sep 17 00:00:00 2001
2 | From: Asher Baker
3 | Date: Sun, 13 Jan 2019 12:35:05 +0000
4 | Subject: [PATCH 2/5] Write FUNC records instead of PUBLIC for ELF symbols with
5 | sizes
6 | ---
7 | src/common/linux/elf_symbols_to_module.cc | 17 +++++++++++++----
8 | 1 file changed, 13 insertions(+), 4 deletions(-)
9 |
10 |
11 | diff --git a/src/common/linux/elf_symbols_to_module.cc b/src/common/linux/elf_symbols_to_module.cc
12 | index 70d50f89..f21460bf 100644
13 | --- a/src/common/linux/elf_symbols_to_module.cc
14 | +++ b/src/common/linux/elf_symbols_to_module.cc
15 | @@ -163,19 +163,28 @@ bool ELFSymbolsToModule(const uint8_t* symtab_section,
16 | while(!iterator->at_end) {
17 | if (ELF32_ST_TYPE(iterator->info) == STT_FUNC &&
18 | iterator->shndx != SHN_UNDEF) {
19 | - auto ext = std::make_unique(iterator->value);
20 | - ext->name = SymbolString(iterator->name_offset, strings);
21 | + string name = SymbolString(iterator->name_offset, strings);
22 | #if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle.
23 | int status = 0;
24 | char* demangled =
25 | - abi::__cxa_demangle(ext->name.c_str(), NULL, NULL, &status);
26 | + abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
27 | if (demangled) {
28 | if (status == 0)
29 | - ext->name = demangled;
30 | + name = demangled;
31 | free(demangled);
32 | }
33 | #endif
34 | +#if 1
35 | + if (iterator->size) {
36 | + Module::Function *fun = new Module::Function(module->AddStringToPool(name), iterator->value);
37 | + fun->ranges.push_back(Module::Range(iterator->value, iterator->size));
38 | + module->AddFunction(fun);
39 | + }
40 | +#else
41 | + Module::Extern *ext = new Module::Extern(iterator->value);
42 | + ext->name = name;
43 | module->AddExtern(std::move(ext));
44 | +#endif
45 | }
46 | ++iterator;
47 | }
48 |
--------------------------------------------------------------------------------
/patches/0003-Support-compilation-on-VS-2015.patch:
--------------------------------------------------------------------------------
1 | From 2f217812634d5e6c56f0cf9e7a4c9b7fb390e954 Mon Sep 17 00:00:00 2001
2 | From: patches
3 | Date: Sat, 20 Jul 2019 15:52:37 +0100
4 | Subject: [PATCH 3/5] Support compilation on VS 2015
5 |
6 | ---
7 | src/common/windows/pe_util.cc | 1 +
8 | src/common/windows/pe_util.h | 2 +-
9 | src/tools/windows/dump_syms/dump_syms.cc | 1 +
10 | 3 files changed, 3 insertions(+), 1 deletion(-)
11 |
12 |
13 | diff --git a/src/common/windows/pe_util.h b/src/common/windows/pe_util.h
14 | index 6c6b364f..80aba5e5 100644
15 | --- a/src/common/windows/pe_util.h
16 | +++ b/src/common/windows/pe_util.h
17 | @@ -59,7 +59,7 @@ wstring GenerateDebugIdentifier(DWORD age, DWORD signature);
18 |
19 | // Converts |machine| enum value to the corresponding string used by Breakpad.
20 | // The enum is IMAGE_FILE_MACHINE_*, contained in winnt.h.
21 | -constexpr const wchar_t* FileHeaderMachineToCpuString(WORD machine) {
22 | +static inline const wchar_t* FileHeaderMachineToCpuString(WORD machine) {
23 | switch (machine) {
24 | case IMAGE_FILE_MACHINE_I386: {
25 | return L"x86";
26 |
--------------------------------------------------------------------------------
/patches/0004-Fix-Linux-a-out-include.patch:
--------------------------------------------------------------------------------
1 | Subject: [PATCH 4/5] Fixes path to a.out.h file
2 |
3 | diff --git a/configure b/configure
4 | index 3442e796..3e7c321c 100755
5 | --- a/configure
6 | +++ b/configure
7 | @@ -7532,11 +7532,16 @@ then :
8 | printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
9 |
10 | fi
11 | -ac_fn_c_check_header_compile "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default"
12 | +ac_fn_c_check_header_compile "$LINENO" "linux/a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default"
13 | if test "x$ac_cv_header_a_out_h" = xyes
14 | then :
15 | printf "%s\n" "#define HAVE_A_OUT_H 1" >>confdefs.h
16 | -
17 | +else
18 | + ac_fn_c_check_header_compile "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default"
19 | + if test "x$ac_cv_header_a_out_h" = xyes
20 | + then :
21 | + printf "%s\n" "#define HAVE_A_OUT_H_EX 1" >>confdefs.h
22 | + fi
23 | fi
24 | ac_fn_c_check_header_compile "$LINENO" "sys/mman.h" "ac_cv_header_sys_mman_h" "$ac_includes_default"
25 | if test "x$ac_cv_header_sys_mman_h" = xyes
26 | diff --git a/src/common/stabs_reader.h b/src/common/stabs_reader.h
27 | index 655683f1..1e6a76b8 100644
28 | --- a/src/common/stabs_reader.h
29 | +++ b/src/common/stabs_reader.h
30 | @@ -52,6 +52,8 @@
31 | #ifdef HAVE_MACH_O_NLIST_H
32 | #include
33 | #elif defined(HAVE_A_OUT_H)
34 | +#include
35 | +#elif defined(HAVE_A_OUT_H_EX)
36 | #include
37 | #endif
38 |
39 |
--------------------------------------------------------------------------------
/product.version:
--------------------------------------------------------------------------------
1 | 2.6.0
2 |
--------------------------------------------------------------------------------
/third_party/AMBuilder:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import os, sys
3 |
4 | def AddSourceFilesFromDir(path, files):
5 | list = []
6 | for file in files:
7 | list.append(os.path.join(path, file))
8 | return list
9 |
10 | builder.SetBuildFolder('./third_party')
11 |
12 | libz = builder.StaticLibraryProject('libz')
13 | libz.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'zlib'),[
14 | 'adler32.c',
15 | 'compress.c',
16 | 'crc32.c',
17 | 'deflate.c',
18 | 'gzclose.c',
19 | 'gzlib.c',
20 | 'gzread.c',
21 | 'gzwrite.c',
22 | 'infback.c',
23 | 'inffast.c',
24 | 'inflate.c',
25 | 'inftrees.c',
26 | 'trees.c',
27 | 'uncompr.c',
28 | 'zutil.c'
29 | ])
30 |
31 | for cxx in Accelerator.targets:
32 | if cxx.target.platform == 'linux':
33 | binary = Accelerator.ConfigureLibrary(libz, cxx, builder)
34 | binary.compiler.cflags += ['-Wno-implicit-function-declaration']
35 |
36 | Accelerator.libz = builder.Add(libz)
37 |
38 | libbreakpad_client = builder.StaticLibraryProject('libbreakpad_client')
39 | libbreakpad_client.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[
40 | 'common/convert_UTF.cc',
41 | 'common/string_conversion.cc',
42 | ])
43 |
44 | libbreakpad_client_linux = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[
45 | 'client/minidump_file_writer.cc',
46 | 'client/linux/crash_generation/crash_generation_client.cc',
47 | 'client/linux/crash_generation/crash_generation_server.cc',
48 | 'client/linux/dump_writer_common/thread_info.cc',
49 | 'client/linux/dump_writer_common/ucontext_reader.cc',
50 | 'client/linux/handler/exception_handler.cc',
51 | 'client/linux/handler/minidump_descriptor.cc',
52 | 'client/linux/log/log.cc',
53 | 'client/linux/microdump_writer/microdump_writer.cc',
54 | 'client/linux/minidump_writer/linux_core_dumper.cc',
55 | 'client/linux/minidump_writer/linux_dumper.cc',
56 | 'client/linux/minidump_writer/linux_ptrace_dumper.cc',
57 | 'client/linux/minidump_writer/minidump_writer.cc',
58 | 'client/linux/minidump_writer/pe_file.cc',
59 | 'common/linux/elf_core_dump.cc',
60 | 'common/linux/elfutils.cc',
61 | 'common/linux/file_id.cc',
62 | 'common/linux/guid_creator.cc',
63 | 'common/linux/linux_libc_support.cc',
64 | 'common/linux/memory_mapped_file.cc',
65 | 'common/linux/safe_readlink.cc'
66 | ])
67 |
68 | libbreakpad_client_windows = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[
69 | 'client/windows/crash_generation/client_info.cc',
70 | 'client/windows/crash_generation/crash_generation_client.cc',
71 | 'client/windows/crash_generation/crash_generation_server.cc',
72 | 'client/windows/crash_generation/minidump_generator.cc',
73 | 'client/windows/handler/exception_handler.cc',
74 | 'common/windows/guid_string.cc'
75 | ])
76 |
77 | libbreakpad = builder.StaticLibraryProject('libbreakpad')
78 | libbreakpad.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[
79 | 'processor/basic_code_modules.cc',
80 | 'processor/basic_source_line_resolver.cc',
81 | 'processor/call_stack.cc',
82 | 'processor/cfi_frame_info.cc',
83 | 'processor/convert_old_arm64_context.cc',
84 | 'processor/disassembler_x86.cc',
85 | 'processor/dump_context.cc',
86 | 'processor/dump_object.cc',
87 | 'processor/exploitability.cc',
88 | 'processor/exploitability_linux.cc',
89 | 'processor/exploitability_win.cc',
90 | 'processor/fast_source_line_resolver.cc',
91 | 'processor/logging.cc',
92 | 'processor/microdump.cc',
93 | 'processor/microdump_processor.cc',
94 | 'processor/minidump.cc',
95 | 'processor/minidump_processor.cc',
96 | 'processor/module_comparer.cc',
97 | 'processor/module_serializer.cc',
98 | 'processor/pathname_stripper.cc',
99 | 'processor/process_state.cc',
100 | 'processor/proc_maps_linux.cc',
101 | 'processor/simple_symbol_supplier.cc',
102 | 'processor/source_line_resolver_base.cc',
103 | 'processor/stack_frame_cpu.cc',
104 | 'processor/stack_frame_symbolizer.cc',
105 | 'processor/stackwalk_common.cc',
106 | 'processor/stackwalker.cc',
107 | 'processor/stackwalker_amd64.cc',
108 | 'processor/stackwalker_arm.cc',
109 | 'processor/stackwalker_arm64.cc',
110 | 'processor/stackwalker_address_list.cc',
111 | 'processor/stackwalker_mips.cc',
112 | 'processor/stackwalker_ppc.cc',
113 | 'processor/stackwalker_ppc64.cc',
114 | 'processor/stackwalker_riscv.cc',
115 | 'processor/stackwalker_riscv64.cc',
116 | 'processor/stackwalker_sparc.cc',
117 | 'processor/stackwalker_x86.cc',
118 | 'processor/symbolic_constants_win.cc',
119 | 'processor/tokenize.cc'
120 | ])
121 |
122 | libbreakpad_linux = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src'),[
123 | 'common/linux/scoped_pipe.cc',
124 | 'common/linux/scoped_tmpfile.cc',
125 | 'processor/disassembler_objdump.cc'
126 | ])
127 |
128 | libbreakpad_windows = []
129 |
130 | libdisasm = builder.StaticLibraryProject('libdisasm')
131 | libdisasm.sources = AddSourceFilesFromDir(os.path.join(builder.currentSourcePath, 'breakpad', 'src', 'third_party', 'libdisasm'),[
132 | 'ia32_implicit.c',
133 | 'ia32_insn.c',
134 | 'ia32_invariant.c',
135 | 'ia32_modrm.c',
136 | 'ia32_opcode_tables.c',
137 | 'ia32_operand.c',
138 | 'ia32_reg.c',
139 | 'ia32_settings.c',
140 | 'x86_disasm.c',
141 | 'x86_format.c',
142 | 'x86_imm.c',
143 | 'x86_insn.c',
144 | 'x86_misc.c',
145 | 'x86_operand_list.c'
146 | ])
147 |
148 | libraries = [libbreakpad_client, libbreakpad, libdisasm]
149 | libraries_platform_src = [(libbreakpad_client_linux, libbreakpad_client_windows), (libbreakpad_linux, libbreakpad_windows), ([],[])]
150 |
151 | for cxx in Accelerator.targets:
152 | for (library, (linux_src, windows_src)) in zip(libraries, libraries_platform_src):
153 | binary = Accelerator.ConfigureLibrary(library, cxx, builder)
154 | compiler = binary.compiler
155 | # Wait for breakpad to be patched
156 | compiler.sourcedeps += Accelerator.breakpad_patch
157 |
158 | if compiler.target.platform == 'linux':
159 | # Link against our zlib if linux
160 | Accelerator.link_libz(cxx, builder)
161 | binary.sources += linux_src
162 | if compiler.target.platform == 'windows':
163 | binary.sources += windows_src
164 |
165 | compiler.cxxincludes += [
166 | os.path.join(builder.currentSourcePath, 'breakpad', 'src')
167 | ]
168 |
169 | compiler.defines += ['UNICODE']
170 |
171 | Accelerator.libbreakpad = builder.Add(libbreakpad)
172 | Accelerator.libbreakpad_client = builder.Add(libbreakpad_client)
173 | Accelerator.libdisasm = builder.Add(libdisasm)
--------------------------------------------------------------------------------
/third_party/Configure:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import os, sys
3 |
4 | builder.SetBuildFolder('third_party/config')
5 | for compiler in Accelerator.targets:
6 | output = """
7 | /* define if the compiler supports basic C++17 syntax */
8 | #define HAVE_CXX17 1
9 |
10 | /* Define to 1 if you have the header file. */
11 | #define HAVE_INTTYPES_H 1
12 |
13 | /* Define to 1 if you have the header file. */
14 | #define HAVE_STDINT_H 1
15 |
16 | /* Define to 1 if you have the header file. */
17 | #define HAVE_STDIO_H 1
18 |
19 | /* Define to 1 if you have the header file. */
20 | #define HAVE_STDLIB_H 1
21 |
22 | /* Define to 1 if you have the header file. */
23 | #define HAVE_STRINGS_H 1
24 |
25 | /* Define to 1 if you have the header file. */
26 | #define HAVE_STRING_H 1
27 |
28 | /* Define to 1 if you have the header file. */
29 | #define HAVE_UNISTD_H 1
30 | """
31 | if compiler.target.platform == "linux":
32 | output += """
33 | /* Define to 1 if you have the header file. */
34 | #define HAVE_A_OUT_H 1
35 |
36 | /* Define to 1 if you have the `getcontext' function. */
37 | #define HAVE_GETCONTEXT 1
38 |
39 | /* Define to 1 if you have the `getrandom' function. */
40 | #define HAVE_GETRANDOM 1
41 |
42 | /* Define to 1 if you have the header file. */
43 | #define HAVE_SYS_MMAN_H 1
44 |
45 | /* Define to 1 if you have the header file. */
46 | #define HAVE_SYS_RANDOM_H 1
47 |
48 | /* Define to 1 if you have the header file. */
49 | #define HAVE_SYS_STAT_H 1
50 |
51 | /* Define to 1 if you have the header file. */
52 | #define HAVE_SYS_TYPES_H 1
53 |
54 | /* Define to 1 if you have the `memfd_create' function. */
55 | #define HAVE_MEMFD_CREATE 1
56 | """
57 |
58 | if '-lrustc_demangle' in compiler.postlink:
59 | output += """
60 | /* Define to 1 if you have the `rustc_demangle' library (-lrustc_demangle). */
61 | #define HAVE_LIBRUSTC_DEMANGLE 1
62 |
63 | /* Define to 1 if you have the header file. */
64 | #define HAVE_RUSTC_DEMANGLE_H 1
65 | """
66 |
67 | if '-pthread' in compiler.postlink:
68 | output += """
69 | /* Define if you have POSIX threads libraries and header files. */
70 | #define HAVE_PTHREAD 1
71 | """
72 |
73 | if '-lzstd' in compiler.postlink:
74 | output += """
75 | /* Define to 1 if you have the `zstd' library (-lzstd). */
76 | #define HAVE_LIBZSTD 1
77 | """
78 |
79 | output += """
80 | /* Name of package */
81 | #define PACKAGE "breakpad"
82 |
83 | /* Define to the address where bug reports for this package should be sent. */
84 | #define PACKAGE_BUGREPORT "google-breakpad-dev@googlegroups.com"
85 |
86 | /* Define to the full name of this package. */
87 | #define PACKAGE_NAME "breakpad"
88 |
89 | /* Define to the full name and version of this package. */
90 | #define PACKAGE_STRING "breakpad 0.1"
91 |
92 | /* Define to the one symbol short name of this package. */
93 | #define PACKAGE_TARNAME "breakpad"
94 |
95 | /* Define to the home page for this package. */
96 | #define PACKAGE_URL ""
97 |
98 | /* Define to the version of this package. */
99 | #define PACKAGE_VERSION "0.1"
100 |
101 | /* Version number of package */
102 | #define VERSION "0.1"
103 | """
104 | Accelerator.breakpad_config[compiler.target.arch] = [builder.AddOutputFile(os.path.join(compiler.target.arch, 'config.h'), output.encode())]
--------------------------------------------------------------------------------
/third_party/Patch:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import os, sys
3 | import re
4 |
5 | def Normalize(path):
6 | return os.path.abspath(os.path.normpath(os.path.join(path)))
7 |
8 | def AddFilesFromDir(path, files):
9 | list = []
10 | for file in files:
11 | list.append(os.path.join(path, file))
12 | return list
13 |
14 | def AddCwdCommand(context, cwd, inputs,
15 | argv,
16 | outputs,
17 | folder = -1,
18 | dep_type = None,
19 | weak_inputs = [],
20 | shared_outputs = [],
21 | env_data = None,
22 | fake_output = None):
23 |
24 | base_argv = [
25 | sys.executable,
26 | Normalize(os.path.join(context.currentSourcePath, 'cwd_cmd.py')),
27 | cwd,
28 | os.path.join(context.buildPath, 'third_party', fake_output) if fake_output else '_',
29 | ]
30 | if fake_output :
31 | outputs += [fake_output]
32 | return context.AddCommand(inputs=inputs, argv=base_argv + argv, outputs=outputs, folder=folder, dep_type=dep_type, weak_inputs=weak_inputs, shared_outputs=shared_outputs, env_data=env_data)
33 |
34 |
35 | git_dir = Normalize(os.path.join(builder.currentSourcePath, 'breakpad'))
36 | patches = AddFilesFromDir(os.path.join(builder.currentSourcePath, '..', 'patches'), [
37 | "0001-Ignore-invalid-modules-rather-than-bailing-on-the-en.patch",
38 | "0002-Write-FUNC-records-instead-of-PUBLIC-for-ELF-symbols.patch",
39 | "0003-Support-compilation-on-VS-2015.patch",
40 | "0004-Fix-Linux-a-out-include.patch",
41 | "0005-Add-LSS.patch"
42 | ])
43 |
44 | # Reset the breakpad directory before applying patches
45 | argv = [
46 | 'git',
47 | 'clean',
48 | '-fdx',
49 | '&&',
50 | 'git',
51 | 'reset',
52 | '--hard'
53 | ]
54 |
55 | git_reset = AddCwdCommand(builder, git_dir,
56 | inputs=patches, # We want the reset to happen again if we modify the patch files
57 | outputs=[],
58 | fake_output='git_clean.txt',
59 | argv=argv)
60 |
61 | # Apply each of the patch available to the breakpad repository
62 | argv = [
63 | 'git',
64 | 'apply',
65 | '--reject'
66 | ]
67 |
68 | git_patches = []
69 | num = 0
70 | for patch in patches:
71 | patch_argv = argv + [patch]
72 | git_patches += AddCwdCommand(builder, git_dir,
73 | inputs=patches, # Redo the command if the patches changed
74 | outputs=[],
75 | fake_output=str(num) + '_patch.txt',
76 | argv=patch_argv,
77 | weak_inputs=git_reset if num == 0 else [git_patches[-1]] + git_reset # We need to for the git reset to be performed
78 | )
79 | num = num + 1
80 |
81 | Accelerator.breakpad_patch = git_reset + git_patches
--------------------------------------------------------------------------------
/third_party/cwd_cmd.py:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 sw=2 tw=99 noet ft=python:
2 | import subprocess, sys
3 |
4 | argv = sys.argv[3:]
5 | cwd = sys.argv[1]
6 | print('CMD: ' + ' '.join(argv))
7 | print('CWD: ' + cwd)
8 | print('OUT: ' + sys.argv[2])
9 | open(sys.argv[2], 'a').close()
10 |
11 | args = { 'args': ' '.join(argv),
12 | 'stdout': subprocess.PIPE,
13 | 'stderr': subprocess.PIPE,
14 | 'shell': True,
15 | 'cwd': cwd }
16 | p = subprocess.Popen(**args)
17 | stdout, stderr = p.communicate()
18 | print(stdout.decode("utf-8"))
19 | if p.returncode != 0:
20 | print(stderr.decode("utf-8"))
21 | sys.exit(p.returncode)
22 |
--------------------------------------------------------------------------------
/upload.py:
--------------------------------------------------------------------------------
1 | import re, os, sys
2 | import subprocess
3 |
4 | import zipfile
5 | import base64
6 | try:
7 | import urllib.request as urllib
8 | except ImportError:
9 | import urllib2 as urllib
10 |
11 | project = 'accelerator'
12 |
13 | platform = 'unknown'
14 | if sys.platform.startswith('linux'):
15 | platform = 'linux'
16 | elif sys.platform.startswith('win32'):
17 | platform = 'windows'
18 | elif sys.platform.startswith('darwin'):
19 | platform = 'mac'
20 |
21 | def GITHash():
22 | p = subprocess.Popen(['git', 'rev-parse', '--short', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
23 | (stdout, stderr) = p.communicate()
24 | stdout = stdout.decode('UTF-8')
25 | return stdout.rstrip('\r\n')
26 |
27 | def GITBranch():
28 | travis_branch = os.environ.get('TRAVIS_BRANCH', False)
29 | if travis_branch:
30 | return travis_branch
31 |
32 | appveyor_branch = os.environ.get('APPVEYOR_REPO_BRANCH', False)
33 | if appveyor_branch:
34 | return appveyor_branch
35 |
36 | p = subprocess.Popen(['git', 'rev-parse', '--abbrev-ref', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
37 | (stdout, stderr) = p.communicate()
38 | stdout = stdout.decode('UTF-8')
39 | return stdout.rstrip('\r\n')
40 |
41 | def GITVersion():
42 | p = subprocess.Popen(['git', 'rev-list', '--count', '--first-parent', 'HEAD'], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
43 | (stdout, stderr) = p.communicate()
44 | stdout = stdout.decode('UTF-8')
45 | return stdout.rstrip('\r\n')
46 |
47 | def ReleaseVersion():
48 | productFile = open('../product.version', 'r')
49 | productContents = productFile.read()
50 | productFile.close()
51 |
52 | m = re.match('(\d+)\.(\d+)\.(\d+)(.*)', productContents)
53 | if m == None:
54 | raise Exception('Could not detremine product version')
55 |
56 | major, minor, release, tag = m.groups()
57 | return '.'.join([major, minor, release])
58 |
59 | filename = '-'.join([project, ReleaseVersion(), 'git' + GITVersion(), GITHash(), platform])
60 |
61 | debug_build = os.environ.get('is_debug_build', False) == "1"
62 |
63 | if debug_build:
64 | filename += '-debug'
65 |
66 | filename += '.zip'
67 |
68 | zip = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
69 |
70 | for base, dirs, files in os.walk('package'):
71 | for file in files:
72 | fn = os.path.join(base, file)
73 | fns = fn[(len('package') + 1):]
74 |
75 | zip.write(fn, fns)
76 |
77 | print("%-33s %-10s %21s %12s" % ("File Name", "CRC32", "Modified ", "Size"))
78 | for zinfo in zip.infolist():
79 | date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time[:6]
80 | print("%-33s %-10d %21s %12d" % (zinfo.filename, zinfo.CRC, date, zinfo.file_size))
81 |
82 | zip.close()
83 |
84 | if 'ftp_username' in os.environ:
85 | print('')
86 |
87 | request = urllib.Request("https://builds.limetech.io/upload.php?project=%s&branch=%s&filename=%s" % (project, GITBranch(), filename), open(filename, 'rb'), headers={
88 | 'Authorization': "Basic %s" % (base64.encodestring(("%s:%s" % (os.environ['ftp_username'], os.environ['ftp_password'])).encode()).decode()[:-1]),
89 | 'Content-Type': 'application/zip',
90 | 'Content-Length': os.stat(filename).st_size,
91 | })
92 | print(urllib.urlopen(request).read().decode('utf-8'))
93 |
94 | print('Uploaded as \'' + filename + '\'')
95 |
--------------------------------------------------------------------------------