To help gdb reload symbol files from /proc/<pid>/maps, making it
possible to load debug info for ELF and PE modules for Wine processes.
When sourced (from ~/.gdbinit for instance), this adds a new
"load-symbol-files" command (with an "lsf" alias), which automatically
calls add-symbol-file on every mapped file that can be read as ELF or
PE, with the correct section offset.
The command has to be run manually, for instance after executing for
a while, to load new modules that may have been …
[View More]loaded, as there's no
way for gdb to be notified of such changes automatically.
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
This is some script I've been using for a while, and more or less broken
versions are also used by other people, so I figured maybe it would be
better to have a proper working version officially distributed with Wine
source instead, as it's pretty useful for debugging Wine under gdb.
It's still a bit manual to use, as gdb cannot easily be notified of
dynamic library loading [1], but I think it's much better than having
nothing.
[1] in theory there's ways to do it using systemtap probes, but it's
going to be hard to make it work, especially for PE modules.
tools/gdbinit.py | 108 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)
create mode 100644 tools/gdbinit.py
diff --git a/tools/gdbinit.py b/tools/gdbinit.py
new file mode 100644
index 00000000000..265d97722b7
--- /dev/null
+++ b/tools/gdbinit.py
@@ -0,0 +1,108 @@
+#!/bin/env python3
+
+# Copyright 2021 Rémi Bernon for CodeWeavers
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+from __future__ import print_function
+
+import gdb
+import re
+import subprocess
+import sys
+
+class LoadSymbolFiles(gdb.Command):
+ 'Command to load symbol files directly from /proc/<pid>/maps.'
+
+ def __init__(self):
+ sup = super(LoadSymbolFiles, self)
+ sup.__init__('load-symbol-files', gdb.COMMAND_FILES, gdb.COMPLETE_NONE,
+ False)
+
+ self.libs = {}
+ gdb.execute('alias -a lsf = load-symbol-files', True)
+
+ def invoke(self, arg, from_tty):
+ pid = gdb.selected_inferior().pid
+ if not pid in self.libs: self.libs[pid] = {}
+
+ def command(cmd, confirm=from_tty):
+ to_string = not from_tty
+ gdb.execute(cmd, from_tty=confirm, to_string=to_string)
+
+ def execute(cmd):
+ return subprocess.check_output(cmd, stderr=subprocess.STDOUT) \
+ .decode('utf-8')
+
+ # load mappings addresses
+ libs = {}
+ with open('/proc/{}/maps'.format(pid), 'r') as maps:
+ for line in maps:
+ addr, _, _, _, node, path = re.split(r'\s+', line, 5)
+ path = path.strip()
+ if node == '0': continue
+ if path in libs: continue
+ libs[path] = int(addr.split('-')[0], 16)
+
+ # unload symbol file if address changed
+ for k in set(libs) & set(self.libs[pid]):
+ if libs[k] != self.libs[pid][k]:
+ command('remove-symbol-file "{}"'.format(k), confirm=False)
+ del self.libs[k]
+
+ # load symbol file for new mappings
+ for k in set(libs) - set(self.libs[pid]):
+ if arg is not None and re.search(arg, k) is None: continue
+ addr = self.libs[pid][k] = libs[k]
+ offs = None
+
+ try:
+ out = execute(['file', k])
+ except:
+ continue
+
+ # try loading mapping as ELF
+ try:
+ out = execute(['readelf', '-l', k])
+ for line in out.split('\n'):
+ if not 'LOAD' in line: continue
+ base = int(line.split()[2], 16)
+ break
+
+ out = execute(['objdump', '-h', k])
+ for line in out.split('\n'):
+ if not '.text' in line: continue
+ offs = int(line.split()[3], 16) - base
+ break
+ if offs is None: continue
+
+ # try again, assuming mapping is PE
+ except:
+ try:
+ out = execute(['objdump', '-h', k])
+ for line in out.split('\n'):
+ if not '.text' in line: continue
+ offs = int(line.split()[5], 16)
+ break
+ if offs is None: continue
+
+ except:
+ continue
+
+ command('add-symbol-file "{}" 0x{:x}'.format(k, addr + offs),
+ confirm=False)
+
+
+LoadSymbolFiles()
--
2.31.0
[View Less]
I'm sending this as an RFC for now as there's been some discussion
already about how to handle PE dependencies. I think the approach I'm
taking here, by including FAudio headers directly in the source and
loading it dynamically is the easiest way, and the least disruptive for
building and running Wine, if not the cleanest. All the other solutions
have higher friction, either on third party packages, or on the system
distribution.
Upstream FAudio now has a pure Win32 backend, which makes it …
[View More]much easier
to build as PE. This was never a requirement, and this series should
work too with the SDL2 backend, as it tries to load Wine Mono SDL2 and
FAudio as a fallback after trying from the prefix system libraries.
It could help the transition though, and is going to be required anyway
to support WMA decoding. Right now, with or without the Win32 backend,
this would also introduce a functional regression as although supported
by the new FAudio Win32 backend, it also depends on the WMA decoder MF
transform to be implemented, which isn't the case yet in Wine.
Cheers,
Rémi Bernon (5):
xactengine3_7: Move sources to xaudio2_7.
xaudio2_7: Load FAudio dynamically.
xaudio2_7: Import FAudio headers for private use.
xaudio2_7: Build with msvcrt.
xaudio2_7: Use msvcrt allocation functions.
configure.ac | 55 -
dlls/x3daudio1_0/Makefile.in | 4 +-
dlls/x3daudio1_1/Makefile.in | 4 +-
dlls/x3daudio1_2/Makefile.in | 4 +-
dlls/x3daudio1_3/Makefile.in | 4 +-
dlls/x3daudio1_4/Makefile.in | 4 +-
dlls/x3daudio1_5/Makefile.in | 4 +-
dlls/x3daudio1_6/Makefile.in | 4 +-
dlls/x3daudio1_7/Makefile.in | 4 +-
dlls/xactengine2_0/Makefile.in | 6 +-
dlls/xactengine2_4/Makefile.in | 6 +-
dlls/xactengine2_7/Makefile.in | 6 +-
dlls/xactengine2_9/Makefile.in | 6 +-
dlls/xactengine3_0/Makefile.in | 6 +-
dlls/xactengine3_1/Makefile.in | 6 +-
dlls/xactengine3_2/Makefile.in | 6 +-
dlls/xactengine3_3/Makefile.in | 6 +-
dlls/xactengine3_4/Makefile.in | 6 +-
dlls/xactengine3_5/Makefile.in | 6 +-
dlls/xactengine3_6/Makefile.in | 6 +-
dlls/xactengine3_7/Makefile.in | 5 +-
dlls/xapofx1_1/Makefile.in | 4 +-
dlls/xapofx1_2/Makefile.in | 4 +-
dlls/xapofx1_3/Makefile.in | 4 +-
dlls/xapofx1_4/Makefile.in | 4 +-
dlls/xapofx1_5/Makefile.in | 4 +-
dlls/xaudio2_0/Makefile.in | 4 +-
dlls/xaudio2_1/Makefile.in | 4 +-
dlls/xaudio2_2/Makefile.in | 4 +-
dlls/xaudio2_3/Makefile.in | 4 +-
dlls/xaudio2_4/Makefile.in | 4 +-
dlls/xaudio2_5/Makefile.in | 4 +-
dlls/xaudio2_6/Makefile.in | 4 +-
dlls/xaudio2_7/FAudio/F3DAudio.h | 262 ++++
dlls/xaudio2_7/FAudio/FACT.h | 814 ++++++++++
dlls/xaudio2_7/FAudio/FACT3D.h | 127 ++
dlls/xaudio2_7/FAudio/FAPO.h | 207 +++
dlls/xaudio2_7/FAudio/FAPOBase.h | 264 ++++
dlls/xaudio2_7/FAudio/FAPOFX.h | 178 +++
dlls/xaudio2_7/FAudio/FAudio.h | 1322 +++++++++++++++++
dlls/xaudio2_7/FAudio/FAudioFX.h | 308 ++++
dlls/xaudio2_7/Makefile.in | 4 +-
dlls/xaudio2_7/compat.c | 92 +-
dlls/xaudio2_7/faudio.c | 326 ++++
dlls/xaudio2_7/x3daudio.c | 35 +-
.../xact_classes.idl | 0
dlls/{xactengine3_7 => xaudio2_7}/xact_dll.c | 182 ++-
dlls/xaudio2_7/xapo.c | 29 +-
dlls/xaudio2_7/xapofx.c | 21 +-
dlls/xaudio2_7/xaudio_allocator.c | 2 -
dlls/xaudio2_7/xaudio_dll.c | 267 ++--
dlls/xaudio2_7/xaudio_private.h | 149 +-
dlls/xaudio2_8/Makefile.in | 4 +-
dlls/xaudio2_9/Makefile.in | 4 +-
54 files changed, 4298 insertions(+), 505 deletions(-)
create mode 100644 dlls/xaudio2_7/FAudio/F3DAudio.h
create mode 100644 dlls/xaudio2_7/FAudio/FACT.h
create mode 100644 dlls/xaudio2_7/FAudio/FACT3D.h
create mode 100644 dlls/xaudio2_7/FAudio/FAPO.h
create mode 100644 dlls/xaudio2_7/FAudio/FAPOBase.h
create mode 100644 dlls/xaudio2_7/FAudio/FAPOFX.h
create mode 100644 dlls/xaudio2_7/FAudio/FAudio.h
create mode 100644 dlls/xaudio2_7/FAudio/FAudioFX.h
create mode 100644 dlls/xaudio2_7/faudio.c
rename dlls/{xactengine3_7 => xaudio2_7}/xact_classes.idl (100%)
rename dlls/{xactengine3_7 => xaudio2_7}/xact_dll.c (87%)
--
2.31.0
[View Less]
Signed-off-by: Derek Lesho <dlesho(a)codeweavers.com>
---
dlls/winevulkan/make_vulkan | 35 ++++++++++++++++++++++++-----------
1 file changed, 24 insertions(+), 11 deletions(-)
diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan
index e8534cbd5f5..c46ef02dcb9 100755
--- a/dlls/winevulkan/make_vulkan
+++ b/dlls/winevulkan/make_vulkan
@@ -124,6 +124,11 @@ UNSUPPORTED_EXTENSIONS = [
"VK_NV_external_memory_win32",
]
+# Either internal extensions which aren't …
[View More]present on the win32 platform which
+# winevulkan may nonetheless use, or extensions we want to generate headers for
+# but not expose to applications (useful for test commits)
+UNEXPOSED_EXTENSIONS = {}
+
# The Vulkan loader provides entry-points for core functionality and important
# extensions. Based on vulkan-1.def this amounts to WSI extensions on 1.0.51.
CORE_EXTENSIONS = [
@@ -513,8 +518,8 @@ class VkEnumValue(object):
class VkFunction(object):
- def __init__(self, _type=None, name=None, params=[], extensions=[], alias=None):
- self.extensions = []
+ def __init__(self, _type=None, name=None, params=[], alias=None):
+ self.extensions = set()
self.name = name
self.type = _type
self.params = params
@@ -657,6 +662,10 @@ class VkFunction(object):
def needs_private_thunk(self):
return self.thunk_type == ThunkType.PRIVATE
+ def needs_exposing(self):
+ # The function needs exposed if at-least one extension isn't both UNSUPPORTED and UNEXPOSED
+ return self.is_required() and (not self.extensions or not self.extensions.issubset(UNEXPOSED_EXTENSIONS))
+
def pfn(self, prefix="p", call_conv=None, conv=False):
""" Create function pointer. """
@@ -2682,7 +2691,7 @@ class VkGenerator(object):
# Create thunks for instance and device functions.
# Global functions don't go through the thunks.
for vk_func in self.registry.funcs.values():
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
if vk_func.is_global_func():
@@ -2702,6 +2711,8 @@ class VkGenerator(object):
for ext in self.registry.extensions:
if ext["type"] != "device":
continue
+ if ext["name"] in UNEXPOSED_EXTENSIONS:
+ continue
f.write(" \"{0}\",\n".format(ext["name"]))
f.write("};\n\n")
@@ -2711,6 +2722,8 @@ class VkGenerator(object):
for ext in self.registry.extensions:
if ext["type"] != "instance":
continue
+ if ext["name"] in UNEXPOSED_EXTENSIONS:
+ continue
f.write(" \"{0}\",\n".format(ext["name"]))
f.write("};\n\n")
@@ -2770,7 +2783,7 @@ class VkGenerator(object):
f.write("const struct unix_funcs loader_funcs =\n")
f.write("{\n")
for vk_func in self.registry.funcs.values():
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
if vk_func.loader_thunk_type == ThunkType.NONE:
continue
@@ -2789,7 +2802,7 @@ class VkGenerator(object):
# Generate prototypes for device and instance functions requiring a custom implementation.
f.write("/* Functions for which we have custom implementations outside of the thunks. */\n")
for vk_func in self.registry.funcs.values():
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
if vk_func.needs_thunk() and not vk_func.needs_private_thunk():
continue
@@ -2888,7 +2901,7 @@ class VkGenerator(object):
f.write("WINE_DEFAULT_DEBUG_CHANNEL(vulkan);\n\n")
for vk_func in self.registry.funcs.values():
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
if vk_func.loader_thunk_type != ThunkType.PUBLIC:
continue
@@ -2897,7 +2910,7 @@ class VkGenerator(object):
f.write("static const struct vulkan_func vk_device_dispatch_table[] =\n{\n")
for vk_func in self.registry.device_funcs:
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
@@ -2905,7 +2918,7 @@ class VkGenerator(object):
f.write("static const struct vulkan_func vk_phys_dev_dispatch_table[] =\n{\n")
for vk_func in self.registry.phys_dev_funcs:
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
@@ -2913,7 +2926,7 @@ class VkGenerator(object):
f.write("static const struct vulkan_func vk_instance_dispatch_table[] =\n{\n")
for vk_func in self.registry.instance_funcs:
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
f.write(" {{\"{0}\", &{0}}},\n".format(vk_func.name))
@@ -2970,7 +2983,7 @@ class VkGenerator(object):
f.write("struct unix_funcs\n")
f.write("{\n")
for vk_func in self.registry.funcs.values():
- if not vk_func.is_required():
+ if not vk_func.needs_exposing():
continue
if vk_func.loader_thunk_type == ThunkType.NONE:
continue
@@ -3464,7 +3477,7 @@ class VkRegistry(object):
# the XML file to handle this, but because of the manner in which we parse the XML
# file we pre-populate from <commands> before we check if a command is enabled.
if cmd_name in self.funcs:
- self.funcs[cmd_name].extensions.append(ext_name)
+ self.funcs[cmd_name].extensions.add(ext_name)
# Some extensions are not ready or have numbers reserved as a place holder.
if ext.attrib["supported"] == "disabled":
--
2.32.0
[View Less]
The IAudioClient implementation from both Windows and winepulse.drv
never sets the event more than once per period, which is usually
around 10 ms long. Some codecs produce audio samples shorter than
10 ms, so it is critical that the SAR is able to process more than
a sample per period.
This is not currently the case: a new sample is requested only in
audio_renderer_render, which is executed (at most) once per period.
This results in the SAR not being able to keep up with the audio
client, and …
[View More]eventually underrunning.
With this patch the SAR keeps a count of how many frames are
currently queued, and a new sample is immediately requested if
the internal queue has less than a buffer worth of frames.
This patch fixes audio stuttering problems in the logo videos
of Borderlands 3, Deep Rock Galactic and Mutant Year Zero.
Signed-off-by: Giovanni Mascellani <gmascellani(a)codeweavers.com>
---
v2: Remove some changes that didn't really help the solution and
update the description accordingly.
v3: Reimplement from scratches, using a different strategy and
avoiding manually setting the event.
v4: Resent with no changes, because it fails to apply without
the first patch.
v5:
* Zero the count when flushing the queue.
* Only request more sample when running.
v6: Store a buffer worth of frames in the queue, instead of
two periods.
dlls/mf/sar.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/dlls/mf/sar.c b/dlls/mf/sar.c
index eba822ae0fe..e35531acb71 100644
--- a/dlls/mf/sar.c
+++ b/dlls/mf/sar.c
@@ -101,6 +101,8 @@ struct audio_renderer
HANDLE buffer_ready_event;
MFWORKITEM_KEY buffer_ready_key;
unsigned int frame_size;
+ unsigned int queued_frames;
+ unsigned int max_frames;
struct list queue;
enum stream_state state;
unsigned int flags;
@@ -234,6 +236,7 @@ static void audio_renderer_release_audio_client(struct audio_renderer *renderer)
{
release_pending_object(obj);
}
+ renderer->queued_frames = 0;
renderer->buffer_ready_key = 0;
if (renderer->audio_client)
{
@@ -1330,6 +1333,13 @@ static HRESULT WINAPI audio_renderer_stream_GetMediaTypeHandler(IMFStreamSink *i
static HRESULT stream_queue_sample(struct audio_renderer *renderer, IMFSample *sample)
{
struct queued_object *object;
+ DWORD sample_len, sample_frames;
+ HRESULT hr;
+
+ if (FAILED(hr = IMFSample_GetTotalLength(sample, &sample_len)))
+ return hr;
+
+ sample_frames = sample_len / renderer->frame_size;
if (!(object = calloc(1, sizeof(*object))))
return E_OUTOFMEMORY;
@@ -1339,6 +1349,7 @@ static HRESULT stream_queue_sample(struct audio_renderer *renderer, IMFSample *s
IMFSample_AddRef(object->u.sample.sample);
list_add_tail(&renderer->queue, &object->entry);
+ renderer->queued_frames += sample_frames;
return S_OK;
}
@@ -1357,9 +1368,17 @@ static HRESULT WINAPI audio_renderer_stream_ProcessSample(IMFStreamSink *iface,
return MF_E_STREAMSINK_REMOVED;
EnterCriticalSection(&renderer->cs);
+
if (renderer->state == STREAM_STATE_RUNNING)
hr = stream_queue_sample(renderer, sample);
renderer->flags &= ~SAR_SAMPLE_REQUESTED;
+
+ if (renderer->queued_frames < renderer->max_frames && renderer->state == STREAM_STATE_RUNNING)
+ {
+ IMFMediaEventQueue_QueueEventParamVar(renderer->stream_event_queue, MEStreamSinkRequestSample, &GUID_NULL, S_OK, NULL);
+ renderer->flags |= SAR_SAMPLE_REQUESTED;
+ }
+
LeaveCriticalSection(&renderer->cs);
return hr;
@@ -1428,6 +1447,7 @@ static HRESULT WINAPI audio_renderer_stream_Flush(IMFStreamSink *iface)
release_pending_object(obj);
}
}
+ renderer->queued_frames = 0;
LeaveCriticalSection(&renderer->cs);
return hr;
@@ -1576,6 +1596,12 @@ static HRESULT audio_renderer_create_audio_client(struct audio_renderer *rendere
return hr;
}
+ if (FAILED(hr = IAudioClient_GetBufferSize(renderer->audio_client, &renderer->max_frames)))
+ {
+ WARN("Failed to get buffer size, hr %#x.\n", hr);
+ return hr;
+ }
+
if (SUCCEEDED(hr = MFCreateAsyncResult(NULL, &renderer->render_callback, NULL, &result)))
{
if (FAILED(hr = MFPutWaitingWorkItem(renderer->buffer_ready_event, 0, result, &renderer->buffer_ready_key)))
@@ -1789,6 +1815,7 @@ static void audio_renderer_render(struct audio_renderer *renderer, IMFAsyncResul
IAudioRenderClient_ReleaseBuffer(renderer->audio_render_client, dst_frames, 0);
obj->u.sample.frame_offset += dst_frames;
+ renderer->queued_frames -= dst_frames;
}
keep_sample = FAILED(hr) || src_frames > max_frames;
--
2.32.0
[View Less]