Why is my function returning early unless I return `nullptr`? - visual-c++

I'm currently in the process of writing a function that tries to parse a .fbx file (the library used is a .c and .h pair that I compile together with my code, called ufbx) and I am puzzled by some behavior I'm seeing while debugging. The code looks like this, imagine there's a breakpoint on ufbx_free_scene at the end:
std::shared_ptr<geometry::Mesh> AssetManager::LoadFbxMesh(const std::string& fullFilePath)
{
ufbx_load_opts opts = { 0 }; // Optional, pass NULL for defaults
ufbx_error error; // Optional, pass NULL if you don't care about errors
ufbx_scene* scene = ufbx_load_file(fullFilePath.c_str(), &opts, &error);
if (!scene)
{
throw std::runtime_error("Failed to load fbx file: " + fullFilePath + ": " + std::string(error.description.data));
}
bool meshFound = false;
auto newMesh = std::make_shared<geometry::Mesh>();
for (size_t ni = 0; ni < scene->nodes.count; ni++)
{
ufbx_node* node = scene->nodes.data[ni];
if (node->is_root)
{
continue;
}
if (node->mesh)
{
if (meshFound)
{
throw std::runtime_error("Only fbx files with a single mesh are supported.");
}
meshFound = true;
auto fbxMesh = node->mesh;
for (size_t i = 0; i < fbxMesh->vertices.count; ++i)
{
auto pos = fbxMesh->vertices.data[i];
}
}
}
ufbx_free_scene(scene);
return nullptr;
}
If I run the code as-is, the breakpoint is hit, it runs as expected. However, if I replace nullptr with the newMesh defined above, the breakpoint is never hit. Instead it jumps straight to the return, completely ignoring the for loop and the call to ufbx_free_scene.
I tried to look at the disassembly and it seems it introduces an early return right after it creates the new shared ptr? Why would it do that? The disassembly for the non-nullptr case looks like I'd expect (no early return).
I'm invoking msvc via SCons, if it helps this is the full command it ran (where only AssetManager was changed i.e. the class that contains my function):
scons: done reading SConscript files.
scons: Building targets ...
cl /FoduskEngine\asset\AssetManager.obj /c duskEngine\asset\AssetManager.cpp /TP /nologo /Od /std:c++20 /MD /EHsc /DEBUG /Zi /DTRACY_ENABLE /IduskEngine /IC:\Users\myuser\code\thirdparty\duskengine\glfw-3.3.7\include /IC:\Users\myuser\code\thirdparty\duskengine\glad\include /IC:\Users\myuser\code\thirdparty\duskengine\glm /IC:\Users\myuser\code\thirdparty\duskengine\boost_1_79_0 /IC:\Users\myuser\code\thirdparty\duskengine\tracy\public
AssetManager.cpp
link /nologo /DEBUG /OUT:a.exe /LIBPATH:. ./../thirdparty/duskengine/glfw-3.3.7/lib/glfw3.lib ./../thirdparty/duskengine/boost_1_79_0/stage/lib/libboost_container-vc143-mt-x64-1_79.lib ./../thirdparty/duskengine/boost_1_79_0/stage/lib/libboost_json-vc143-mt-x64-1_79.lib opengl32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib duskEngine\DuskMisc.obj duskEngine\main.obj duskEngine\asset\AssetManager.obj duskEngine\asset\Texture.obj duskEngine\asset\third_party\stb_image_impl.obj duskEngine\camera\Camera.obj duskEngine\editor\CameraController.obj duskEngine\editor\Editor.obj duskEngine\editor\EditorGUI.obj duskEngine\editor\InputManager.obj duskEngine\editor\gizmos\EditorGizmos.obj duskEngine\editor\gizmos\TransformHandles.obj duskEngine\editor\gui_subpanes\AssetBrowserGUI.obj duskEngine\editor\gui_subpanes\EntityListEditorGUI.obj duskEngine\editor\gui_subpanes\EntityPropertiesGUI.obj duskEngine\editor\gui_subpanes\ScenePropertiesGUI.obj duskEngine\imgui\imgui.obj duskEngine\imgui\ImGuizmo.obj duskEngine\imgui\imgui_demo.obj duskEngine\imgui\imgui_draw.obj duskEngine\imgui\imgui_impl_glfw.obj duskEngine\imgui\imgui_impl_opengl3.obj duskEngine\imgui\imgui_stdlib.obj duskEngine\imgui\imgui_tables.obj duskEngine\imgui\imgui_widgets.obj duskEngine\math\Math.obj duskEngine\projects\ProjectLoader.obj duskEngine\rendering\shaderset.obj duskEngine\rendering\opengl\GLModelRenderable.obj duskEngine\rendering\opengl\GLRenderer.obj duskEngine\scene\Scene.obj duskEngine\scene\SceneLoading.obj duskEngine\glad.obj duskEngine\asset\third_party\ufbx\ufbx.obj C:\Users\myuser\code\thirdparty\duskengine\tracy\public\TracyClient.obj
LINK : a.exe not found or not built by the last incremental link; performing full link
scons: done building targets.
Shouldn't /Od disable such optimizations? Assuming it's an optimization (but then, why not return nullptr early?)

MSVC has enabled named returned value optimization (NRVO) when built with /std:c++20 even in debug builds:
Visual Studio 2022 version 17.4 increases the number of places where the compiler does optional copy or move elisions under /Zc:nrvo, whether enabled explicitly, or automatically by using the /O2, /permissive-, or /std:c++20 or later options.
It can be explicitly disabled with /Zc:nrvo- compiler option.
Note, nameless return value optimization (RVO) is required by the standard since C++17 and will always be performed regardless of /Zc:nrvo:
When the nameless temporary is the operand of a return statement, this variant of copy elision is known as RVO, "return value optimization".
(until C++17)
Return value optimization is mandatory and no longer considered as copy elision; see above. (since C++17)

Related

Where is kernel machine_desc table information?

I'm trying to understand how devicetrees work.
According to the kernel documentation, they are used, in arm architecture, in the following manner:
In the majority of cases, the machine identity is irrelevant, and the kernel will instead select setup code based on the machine’s core CPU or SoC. On ARM for example, setup_arch() in arch/arm/kernel/setup.c will call setup_machine_fdt() in arch/arm/kernel/devtree.c which searches through the machine_desc table and selects the machine_desc which best matches the device tree data. It determines the best match by looking at the ‘compatible’ property in the root device tree node, and comparing it with the dt_compat list in struct machine_desc (which is defined in arch/arm/include/asm/mach/arch.h if you’re curious).
The ‘compatible’ property contains a sorted list of strings starting with the exact name of the machine, followed by an optional list of boards it is compatible with sorted from most compatible to least.
I found the source code related to the comparison of machine_desc to the compatible parameter set in the dts file:
const struct machine_desc * __init setup_machine_fdt(void *dt_virt)
{
const struct machine_desc *mdesc, *mdesc_best = NULL;
#if defined(CONFIG_ARCH_MULTIPLATFORM) || defined(CONFIG_ARM_SINGLE_ARMV7M)
DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
.l2c_aux_val = 0x0,
.l2c_aux_mask = ~0x0,
MACHINE_END
mdesc_best = &__mach_desc_GENERIC_DT;
#endif
if (!dt_virt || !early_init_dt_verify(dt_virt))
return NULL;
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
if (!mdesc) {
const char *prop;
int size;
unsigned long dt_root;
early_print("\nError: unrecognized/unsupported "
"device tree compatible list:\n[ ");
dt_root = of_get_flat_dt_root();
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
while (size > 0) {
early_print("'%s' ", prop);
size -= strlen(prop) + 1;
prop += strlen(prop) + 1;
}
early_print("]\n\n");
dump_machine_table(); /* does not return */
}
/* We really don't want to do this, but sometimes firmware provides buggy data */
if (mdesc->dt_fixup)
mdesc->dt_fixup();
early_init_dt_scan_nodes();
/* Change machine number to match the mdesc we're using */
__machine_arch_type = mdesc->nr;
return mdesc;
}
However, I didn't find machine_desc table definition.
If I'd like to read all machine_desc, Where can I find it?
TL;DR - The machine description is built by building and linking different source files into the kernel. So each machine source file adds an entry into the table.
The table is based in arch/arm/kernel/vmlinux.lds.S (or relevant architecture linker file). It is built with the macros MACHINE_START and MACHINE_END. This places a structure in the 'arch.info.init' sections of the object file. All of these objects get globbed together by the linker. This forms the table. So, it is constructed by linking different source files with the MACHINE_START and MACHINE_END macros. Therefore, it doesn't exist in one place.
However, you can use git grep -A10 MACHINE_START to get a fairly good list. This command works well, as typically, it is the last thing in the file so only five or six lines may print. Or you could write init code to dump the table by printing the machine_desc entries.
That said, the table is not too interesting as it is just function pointers to call at different times. The majority will be NULL as it used designated initializers.
Related: Control to 'dt_machine_start' on Android

What are "retainedNodes" in LLVMs debug metadata?

Using the LLVM 8.0.1 library, I try to create the debug info for a function with the following code:
DIFile *Unit = DebugBuilder->createFile(CompileUnit->getFilename(), CompileUnit->getDirectory());
DIScope *FContext(Unit);
DISubprogram *SP = DebugBuilder->createFunction(
FContext, def->Name, def->Name, Unit, LineNo,
CreateFunctionType(ft, CompileUnit->getFile()), 0);
func->setSubprogram(SP);
This, however, results in IR like the following:
define i32 #main(i32 %y) !dbg !3 {
entry:
ret i32 2
}
; ...
!3 = !DISubprogram(name: "main", linkageName: "main", scope: !2, file: !2, type: !4, spFlags: 0, retainedNodes: !7)
; ...
!7 = <temporary!> !{}
Which, upon calling DebugBuilder->finalize(), throws Assertion failed: !N->isTemporary() && "Expected all forward declarations to be resolved"
I have not found a description of the retainedNodes field in the official reference nor other tutorials, and web searches only lead to uncommented sections of the LLVM source. What is the meaning or purpose of this field? How is a temporary node created there?
I found this solved by adding, before generating the DebugBuilder,
TheModule->addModuleFlag(llvm::Module::Warning, "CodeView", 1);
... as explained apparently nowhere in the official documentation.
I had the same problem with the LLVM C API (using inkwell-rs as a wrapper). I fixed this problem by invoking LLVMDIBuilderCreateFunction with IsDefinition = true and IsLocalToUnit = true. This keeps the retainedNodes metadata node, but its value is empty metadata (!{}) instead of a temporary.
I solved a similar problem by finalizing the subprogram explicitly with finalizeSubprogram.
DebugBuilder->finalizeSubprogram(SP);
This seems to resolve the temporary, but I still got some warnings, when compiling the generated IR.
If you make the function a definition by adding the DISubprogram::SPFlagDefinition flag to DebugBuilder->createFunction call, retainedNodes will be set to an empty node instead of a temporary.

Why does initscr() after a delwin() return undef?

Why does the delwin cause the second initscr to return nothing? I thought the endwin would reset to the state as it was before calling initscr.
use NCurses;
my $win = initscr();
addstr( 'AAA' );
nc_refresh();
sleep 2;
delwin( $win );
endwin();
...
my $new_win = initscr();
if ! $new_win.defined {
endwin();
dd $new_win; # NCurses::WINDOW $new_win = NCurses::WINDOW
die "win undefined"; # win undefined
}
addstr( 'BBB' );
nc_refresh();
sleep 2;
delwin( $new_win );
endwin;
What's actually happening is that initscr returns stdscr (the standard window). The delwin deleted it (and the pointer is part of a SCREEN structure which is duly updated), so a subsequent call to initscr (not having created a new screen with newterm) will return that NULL pointer. In principle, an application could reference curscr and newscr (two other windows created during initialization), but the Perl interface likely ignores those.
It helps to read the documentation however. Quoting the Differences section of the initscr manual page:
Differences
X/Open specifies that portable applications must not call initscr more
than once:
The portable way to use initscr is once only, using refresh (see
curs_refresh(3x)) to restore the screen after endwin.
This implementation allows using initscr after endwin.
Old versions of curses, e.g., BSD 4.4, may have returned a null pointer
from initscr when an error is detected, rather than exiting. It is
safe but redundant to check the return value of initscr in XSI Curses.

Cairo.Surface is leaking... How to debug it with Monodevelop?

I have many doubts related with Cairo and GTK# (that runs on .NET and Mono). I'm developing a GTK# application for MS Windows and Linux. I'm using GTK# 2.12 over .NET right now while I'm working on the application.
I've created a custom widget that uses Cairo.ImageSurface and Cairo.Context objects. As far as I know, I'm calling the Dispose method of every ImageSurface object and every Context object I create inside the widget code.
The widget responds to the "MouseOver" event, redrawing some parts of its DrawingArea.
The (first) problem:
almost every redrawing operation increases a little bit the amount of used memory. When the amount of used memory has increased 3 or 4 Kbytes the Monodevelop tracelog panel shows me the following message:
Cairo.Surface is leaking, programmer is missing a call to Dispose
Set MONO_CAIRO_DEBUG_DISPOSE to track allocation traces
The code that redraws a part of the widget is something like:
// SRGB is a custom struct, not from Gdk nor Cairo
void paintSingleBlock(SRGB color, int i)
{
using (Cairo.Context g = CairoHelper.Create (GdkWindow)) {
paintSingleBlock (g, color, i);
// We do this to avoid memory leaks. Cairo does not work well with the GC.
g.GetTarget().Dispose ();
g.Dispose ();
}
}
void paintSingleBlock(Cairo.Context g, SRGB color, int i)
{
var scale = Math.Pow (10.0, TimeScale);
g.Save();
g.Rectangle (x(i), y(i), w(i), h(i));
g.ClosePath ();
g.Restore ();
// We don't directly use stb.Color because in some cases we need more flexibility
g.SetSourceRGB (color.R, color.G, color.B);
g.LineWidth = 0;
g.Fill ();
}
The (second) problem: Ok, Monodevelop tells me that I should set MONO_CAIRO_DEBUG_DISPOSE to "track allocation traces" (In order to find the leak, I suppose)... but I don't know how to set this environment variable (I'm in Windows). I've tried using bash and executing something like:
MONO_CAIRO_DEBUG_DISPOSE=1 ./LightCreator.exe
But nothing appears in stderr nor stdout... (neither the messages that appear in the Monodevelop's applicationt trace panel). I also don't know how to get the debugging messages that see inside Monodevelop but without Monodevelop.
There's anyone with experience debugging GTK# or Cairo# memory leaks?
Thanks in advance.
Just wanted to throw my 2c here as I was fighting a similar leak problem in Cairo with surfaces. What I noticed is that if I create a Surface object the ReferenceCount property becomes 1 and if I attach this surface to a Context if becomes not 2 but 3. After disposing the Context the ReferenceCount comes back but to 2.
So I used some reflection to call the native methods in Cairo to decrease the ReferenceCount when I really want to Dispose a surface. I use this code:
public static void HardDisposeSurface (this Surface surface)
{
var handle = surface.Handle;
long refCount = surface.ReferenceCount;
surface.Dispose ();
refCount--;
if (refCount <= 0)
return;
var asm = typeof (Surface).Assembly;
var nativeMethods = asm.GetType ("Cairo.NativeMethods");
var surfaceDestroy = nativeMethods.GetMethod ("cairo_surface_destroy", BindingFlags.Static | BindingFlags.NonPublic);
for (long i = refCount; i > 0; i--)
surfaceDestroy.Invoke (null, new object [] { handle });
}
After using it I still have some leaks, but they seem to be related to other parts of Cairo and not with the surfaces.
I have found that a context created with CairoHelper.Create() will have a reference count of two.
A call to dispose reduces the reference count by one. Thus the context is never freed and keeps its target alive, too.
The native objects have manual reference counting, but the Gtk# wrappers want to keep a native object alive as long as there is a C# instance referencing it.
If a native object is created for a C# wrapper instance it does not need to increment the reference count because the wrapper instance 'owns' the native object and the reference count has the correct value of one. But if a wrapper instance is created for an already existing native object the reference count of the native object needs to be manually incremented to keep the object alive.
This is decided by a bool parameter when a wrapper instance is created.
Looking at the code for CairoHelper.Create() will show something like this
public static Cairo.Context Create(Gdk.Window window) {
IntPtr raw_ret = gdk_cairo_create(window == null ? IntPtr.Zero : window.Handle);
Cairo.Context ret = new Cairo.Context (raw_ret, false);
return ret;
}
Even though the native context was just created 'owned' will be false and the C# context will increment the reference count.
There is no fixed version right now, it can only be corrected by fixing the source and building Gtk# yourself.
CairoHelper is an auto-generated file, to change the parameter to true this attribute must be included in gdk/Gdk.metadata.
<attr path="/api/namespace/class[#cname='GdkCairo_']/method[#name='Create']/return-type" name="owned">true</attr>
Everything to build Gtk# can be found here.
https://github.com/mono/gtk-sharp

InstallShield calling advapi32.dll method type mismatch error

I am trying to call Advapi32.LsaOpenPolicy() from basic MSI InstallShield code. I've successfully called other avdapi32.dll methods; But LsaOPenPolicy is throwing a mismatched type error.
My prototype is:
prototype INT Advapi32.LsaOpenPolicy(POINTER, POINTER, INT, POINTER);
The windows definition is:
NTSTATUS LsaOpenPolicy(
_In_ PLSA_UNICODE_STRING SystemName,
_In_ PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
_In_ ACCESS_MASK DesiredAccess,
_Inout_ PLSA_HANDLE PolicyHandle
);
I've noted in C++ samples that the ObjectAttriibute structure is zeroed out. So I do something similar here in the InstallShield code -- pArray points to the array contents.
for i = 0 to 11
array(i) = 0;
endfor;
array(0) = 24;
// current error is 80020005 type mismatch.
try
i = POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES;
pArray = array;
pPolicy = NULL;
nvOSResult = LsaOpenPolicy(NULL, pArray, i, pPolicy);
catch
Sprintf(errString, "0x%08x", Err.Number);
_Logger(hMSI, methodName, "LsaOpenPolicy Exception "+errString, INFORMATION, FALSE);
nvOSResult = Err.Number;
endcatch;
There not much other information I can find other than the 80020005 error thrown; I've tried a few different argument constructions, but I can't get past this.
I've posted this in an flexera and microsoft forum -- but I have gotten no traction there. (references for posterity: flexera-link, microsoft-link)
Any help or suggestions are welcome!
The answer to this question was to actually work-around the interface between installshield and the system DLLs by moving all the workings into a C++ DLL. As installation got more complex, I ended up with two separate DLL functions, one executed at dialog (non-admin) mode and one at deferred execution (admin) mode.
In order to pass information I used the MsiGetProperty() API using MSI properties for both input and output variables.
Note that for deferred execution, I needed a CAD function on the installshield side to marshal data into the custom action data location, and on the DLL side extract the data, again by using MsiGetProperty() but getting the "CustomActionData" property and then parse the resulting string which contained the marshaled data.

Resources