Странное поведение std :: cout, не печатающего числа - PullRequest
1 голос
/ 10 июля 2019

Следующий код (скомпилированный как часть большого проекта) не печатает числа (не только однобайтовые целые числа , но и любые из них: std::int32_t, std::size_t, double, так далее). Конечно, этот код, скомпилированный сам по себе, прекрасно работает, и поэтому невозможно представить минимальный пример, который воспроизводит эту проблему: что-то в этом большом проекте вызывает это, но я не могу понять, что именно вызывает его - отсюда этот вопрос.

код:

std::int32_t n = 42;
std::cout << "test 1" << std::endl; // prints
std::cout << 3.14 << std::endl;     // doesn't print
std::cout << 456464 << std::endl;   // doesn't print
std::cout << n << std::endl;        // doesn't print
std::cout << "test 2" << std::endl; // still doesn't print
printf("printf: %d\n", n);          // prints
std::cout.clear();
std::cout << "test 3" << std::endl; // prints
std::cout << 42 << std::endl;       // doesn't print

Выход:

test 1
printf: 1
test 3

Итак, числовые литералы 3.14 и 456464 и переменная n не печатаются так же, как и строка test 2 (я пытаюсь напечатать строку после неудачной печати целого числа). Функция printf работает. Я использую std::endl, поэтому не должно быть никаких проблем с буферизацией.

Использование std::cout.clear(), похоже, решает проблему (мы можем снова печатать строки), но только до тех пор, пока не будет напечатано другое число.

Какие манипуляции с выходными потоками потенциально могут вызвать это? Да, есть using namespace std;.

Код скомпилирован с -std=c++11 с использованием цепочек инструментов GCC (я пробовал разные версии, включая 5.3.1, 6.3.1 и 8.2.0 - результат всегда один и тот же).

Командная строка компиляции:

g++ c -MMD -pipe -std=c++11 -fPIC -O3 \\
-fmax-errors=3 -msse4.1 -mavx2 source.cpp -o target/objects/source.o

Связывание командной строки:

g++ <object files> -o executable -s \\
-Wl,--build-id=uuid -static-libstdc++ -pthread -Wl,--no-undefined

1 Ответ

0 голосов
/ 10 июля 2019

После некоторой отладки мне удалось найти виновника. Это не полный ответ, потому что он не объясняет наблюдаемые симптомы (не печатая конкретно цифры и почему использование std::cout.clear() решает проблему хотя бы частично), но этот ответ предоставит минимальный пример, который можно использовать для воспроизведения проблемы. Надеюсь, это сэкономит кому-то время.

Причиной странного поведения является проблема с использованием памяти, а именно с исчерпанием локального хранилища потоков (TLS). Проблема может быть воспроизведена любым способом, который включает интенсивное использование TLS. В приведенном ниже примере для этого используется OpenCV и статическая ссылка на libstdc++. Чтобы ускорить процесс, мы будем использовать Python, чтобы не писать слишком много кода.

C ++ часть:

#include <iostream>
#pragma GCC visibility push(default)
extern "C" void fun() {
    std::cout << "hello 0" << std::endl;
    std::cout << 42 << std::endl;
    std::cout << "hello 2" << std::endl;
}
#pragma GCC visibility pop

Скомпилируйте и скомпонуйте его (-static-libstdc++ важен здесь, поскольку эта библиотека известна интенсивным использованием TLS):

g++ -std=c++11 -fPIC test.cpp -s -static-libstdc++ -pthread -shared -o libtest.so

Теперь используйте модуль Python и ctypes для загрузки нашей библиотеки:

import numpy as np
lib = np.ctypeslib.load_library('libtest', '.')
lib.fun()

Если запустить так, он напечатает (как и ожидалось):

hello 0
42
hello 1

Теперь давайте добавим еще одну библиотеку с интенсивным использованием TLS:

import numpy as np
import cv2  # <-----
lib = np.ctypeslib.load_library('libtest', '.')
lib.fun()

Будет напечатано:

hello 0

Обратите внимание, что конкретная воспроизводимость может зависеть от настроек вашей ОС для ограничений TLS.

...