diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index 5d8942f262e8..bfbcf2df9bda 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -115,3 +115,83 @@ jobs: SCONS_CACHE: ${{github.workspace}}/.scons_cache/ run: | scons -j2 verbose=yes warnings=all werror=yes platform=x11 target=release tools=no module_mono_enabled=yes mono_glue=no + + linux-sanitizer: + runs-on: "ubuntu-20.04" + name: Editor and exported project with sanitizers(target=debug/release, tools=yes/no, debug_symbols=yes/full, use_ubsan=yes, use_asan=yes) + + steps: + - uses: actions/checkout@v2 + + # Azure repositories are not reliable, we need to prevent azure giving us packages. + - name: Make apt sources.list use the default Ubuntu repositories + run: | + sudo cp -f misc/ci/sources.list /etc/apt/sources.list + sudo apt-get update + + # Install all packages (except scons) + - name: Configure dependencies + run: | + sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \ + libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm \ + xvfb wget2 unzip + + # Upload cache on completion and check it out now + - name: Load .scons_cache directory + id: linux-editor-cache + uses: actions/cache@v2 + with: + path: ${{github.workspace}}/.scons_cache/ + key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} + restore-keys: | + ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}} + ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}} + ${{github.job}}-${{env.GODOT_BASE_BRANCH}} + + # Use python 3.x release (works cross platform; best to keep self contained in it's own step) + - name: Set up Python 3.x + uses: actions/setup-python@v2 + with: + # Semantic version range syntax or exact version of a Python version + python-version: '3.x' + # Optional - x64 or x86 architecture, defaults to x64 + architecture: 'x64' + + # Setup scons, print python version and scons version info, so if anything is broken it won't run the build. + - name: Configuring Python packages + run: | + python -c "import sys; print(sys.version)" + python -m pip install scons + python --version + scons --version + + # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags + - name: Compilation + env: + SCONS_CACHE: ${{github.workspace}}/.scons_cache/ + run: | + scons -j2 verbose=yes warnings=all werror=yes platform=x11 tools=yes target=debug use_ubsan=yes use_asan=yes + scons -j2 verbose=yes warnings=all werror=yes platform=x11 tools=no target=release debug_symbols=full use_ubsan=yes use_asan=yes + + # Download and test project to check leaks and invalid memory usage + - name: Importing and running project project + run: | + wget2 https://github.com/qarmin/RegressionTestProject/archive/3.2.zip + unzip 3.2.zip + mv "RegressionTestProject-3.2" "test_project" + DRI_PRIME=0 timeout 25s xvfb-run bin/godot.x11.tools.64s -e --path test_project 2>&1 | tee sanitizers_log.txt || true + misc/scripts/check_ci_log.py sanitizers_log.txt + DRI_PRIME=0 xvfb-run bin/godot.x11.tools.64s 20 --video-driver GLES3 --path test_project 2>&1 | tee sanitizers_log.txt || true + misc/scripts/check_ci_log.py sanitizers_log.txt + + # Export project and run it to check for possible leaks and invalid memory usage + - name: Exporting and running project + run: | + mkdir exported_project + sed -i 's:PATH_TO_CHANGE:'`pwd`/bin/godot.x11.debug.64s':' test_project/export_presets.cfg + cd test_project + DRI_PRIME=0 xvfb-run ../bin/godot.x11.tools.64s --export-debug "Linux/X11" ../exported_project/test_project 2>&1 | tee sanitizers_log.txt || true + cd .. + misc/scripts/check_ci_log.py sanitizers_log.txt + DRI_PRIME=0 xvfb-run exported_project/test_project 20 2>&1 | tee sanitizers_log.txt || true + misc/scripts/check_ci_log.py sanitizers_log.txt diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index f63d4884e275..fcebd14ea9c3 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -168,6 +168,9 @@ void EditorResourcePreview::_generate_preview(Ref &r_texture, Ref< } r_texture = generated; + if (!EditorNode::get_singleton()->get_theme_base()) + return; + int small_thumbnail_size = EditorNode::get_singleton()->get_theme_base()->get_icon("Object", "EditorIcons")->get_width(); // Kind of a workaround to retrieve the default icon size small_thumbnail_size *= EDSCALE; diff --git a/misc/scripts/check_ci_log.py b/misc/scripts/check_ci_log.py new file mode 100755 index 000000000000..19d0362d6181 --- /dev/null +++ b/misc/scripts/check_ci_log.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys + +if len(sys.argv) < 2: + print("ERROR: You must run program with file name as argument.") + sys.exit(1) + +fname = sys.argv[1] + +fileread = open(fname.strip(), "r") +file_contents = fileread.read() + +# If find "ERROR: AddressSanitizer:", then happens invalid read or write +# This is critical bug, so we need to fix this as fast as possible + +if file_contents.find("ERROR: AddressSanitizer:") != -1: + print("FATAL ERROR: An incorrectly used memory was found.") + sys.exit(1) + +# There is also possible, that program crashed with or without backtrace. + +if ( + file_contents.find("Program crashed with signal") != -1 + or file_contents.find("Dumping the backtrace") != -1 + or file_contents.find("Segmentation fault (core dumped)") != -1 +): + print("FATAL ERROR: Godot has been crashed.") + sys.exit(1) + +# Finding memory leaks in Godot is quite difficult, because we need to take into +# account leaks also in external libraries. They are usually provided without +# debugging symbols, so the leak report from it usually has only 2/3 lines, +# so searching for 5 element - "#4 0x" - should correctly detect the vast +# majority of memory leaks + +if file_contents.find("ERROR: LeakSanitizer:") != -1: + if file_contents.find("#4 0x") != -1: + print("ERROR: Memory leak was found") + sys.exit(1) + +# It may happen that Godot detects leaking nodes/resources and removes them, so +# this possibility should also be handled as a potential error, even if +# LeakSanitizer doesn't report anything + +if file_contents.find("ObjectDB instances leaked at exit") != -1: + print("ERROR: Memory leak was found") + sys.exit(1) + +sys.exit(0) diff --git a/platform/osx/detect.py b/platform/osx/detect.py index bb9e27da462e..04e9da138050 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -43,10 +43,11 @@ def configure(env): ## Build type if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize", "-msse2"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize", "-msse2"]) + if env["debug_symbols"] != "full": + if env["optimize"] == "speed": # optimize for speed (default) + env.Prepend(CCFLAGS=["-O3", "-fomit-frame-pointer", "-ftree-vectorize", "-msse2"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os", "-ftree-vectorize", "-msse2"]) if env["debug_symbols"] == "yes": env.Prepend(CCFLAGS=["-g1"]) @@ -54,11 +55,14 @@ def configure(env): env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O2"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os"]) + if env["debug_symbols"] != "full": + if env["optimize"] == "speed": # optimize for speed (default) + env.Prepend(CCFLAGS=["-O2"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os"]) + env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) + if env["debug_symbols"] == "yes": env.Prepend(CCFLAGS=["-g1"]) if env["debug_symbols"] == "full": diff --git a/platform/windows/detect.py b/platform/windows/detect.py index e529991cf632..bad6e871ecb0 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -177,19 +177,21 @@ def configure_msvc(env, manual_msvc_config): # Build type if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["/O2"]) - else: # optimize for size - env.Append(CCFLAGS=["/O1"]) + if env["debug_symbols"] != "full": + if env["optimize"] == "speed": # optimize for speed (default) + env.Append(CCFLAGS=["/O2"]) + else: # optimize for size + env.Append(CCFLAGS=["/O1"]) env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) env.Append(LINKFLAGS=["/OPT:REF"]) elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Append(CCFLAGS=["/O2"]) - else: # optimize for size - env.Append(CCFLAGS=["/O1"]) + if env["debug_symbols"] != "full": + if env["optimize"] == "speed": # optimize for speed (default) + env.Append(CCFLAGS=["/O2"]) + else: # optimize for size + env.Append(CCFLAGS=["/O1"]) env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"]) env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) env.Append(LINKFLAGS=["/OPT:REF"]) diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 716930db236a..99a58065beba 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -85,10 +85,11 @@ def configure(env): ## Build type if env["target"] == "release": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O3"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os"]) + if env["debug_symbols"] != "full": + if env["optimize"] == "speed": # optimize for speed (default) + env.Prepend(CCFLAGS=["-O3"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os"]) if env["debug_symbols"] == "yes": env.Prepend(CCFLAGS=["-g1"]) @@ -96,10 +97,12 @@ def configure(env): env.Prepend(CCFLAGS=["-g2"]) elif env["target"] == "release_debug": - if env["optimize"] == "speed": # optimize for speed (default) - env.Prepend(CCFLAGS=["-O2"]) - else: # optimize for size - env.Prepend(CCFLAGS=["-Os"]) + if env["debug_symbols"] != "full": + if env["optimize"] == "speed": # optimize for speed (default) + env.Prepend(CCFLAGS=["-O2"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os"]) + env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) if env["debug_symbols"] == "yes": @@ -146,11 +149,24 @@ def configure(env): env.extra_suffix += "s" if env["use_ubsan"]: - env.Append(CCFLAGS=["-fsanitize=undefined"]) - env.Append(LINKFLAGS=["-fsanitize=undefined"]) + env.Append( + CCFLAGS=[ + "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin" + ] + ) + + if env["use_llvm"]: + env.Append( + CCFLAGS=[ + "-fsanitize=nullability-return,nullability-arg,function,nullability-assign,implicit-integer-sign-change,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation" + ] + ) + else: + env.Append(CCFLAGS=["-fsanitize=bounds-strict"]) + env.Append(LINKFLAGS=["-fsanitize=undefined"]) if env["use_asan"]: - env.Append(CCFLAGS=["-fsanitize=address"]) + env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"]) env.Append(LINKFLAGS=["-fsanitize=address"]) if env["use_lsan"]: