Złapanie wyjątku przy otwieraniu pliku

0

Hej!
Cześć mego kodu działającego jako proces w tle, który w pętli uruchamiany jest co 3 sek., wygląda tak:

try {
	try {
		SaveLog(txt2string("Będzie zaraz fopen pliku ").append(nazwa));

		struct stat stats;
		struct tm dt;
		char  ctime_txt[70], mtime_txt[70], atime_txt[70];
		stat(nazwa.c_str(), &stats);
		string	faccess;

		dt = *(localtime(&stats.st_ctime));
		sprintf(ctime_txt, "created: %4d-%02d-%02d %02d:%02d:%02d",		dt.tm_year + 1900, dt.tm_mon+1, dt.tm_mday,		dt.tm_hour, dt.tm_min, dt.tm_sec);
		dt = *(localtime(&stats.st_mtime));
		sprintf(mtime_txt, "   modified: %4d-%02d-%02d %02d:%02d:%02d",		dt.tm_year + 1900, dt.tm_mon+1, dt.tm_mday,		dt.tm_hour, dt.tm_min, dt.tm_sec);
		dt = *(localtime(&stats.st_atime));
		sprintf(atime_txt, "   accessed: %4d-%02d-%02d %02d:%02d:%02d",		dt.tm_year + 1900, dt.tm_mon+1, dt.tm_mday,		dt.tm_hour, dt.tm_min, dt.tm_sec);

		faccess = "File access: ";
		if (stats.st_mode & R_OK)	faccess.append("r");	else	faccess.append("-");
		if (stats.st_mode & W_OK)	faccess.append("w");	else	faccess.append("-");
		if (stats.st_mode & X_OK)	faccess.append("x");	else	faccess.append("-");

		SaveLog(txt2string(ctime_txt).append(txt2string(mtime_txt)).append(txt2string(atime_txt)).append("    fileSize=").append(int2string(stats.st_size)).append("B    ").append(faccess));

		SaveLog("before fopen");
		fi = fopen(nazwa.c_str(), "rb");
		SaveLog("after fopen");
	} catch (exception &e) {
		SaveLog("exception przy fopen");
		SaveLog(txt2string("Błąd otwierania pliku '").append(nazwa).append("':  ").append(e.what()));
	}
}
catch (...) {
	SaveLog(txt2string("Nieudane otwieranie pliku '").append(nazwa).append("'"));
}

log = DEBUGproc_txt;
log.append("  try #0.b ");
SaveLog(log);


Do loga zapisuje mi on:

2022-11-07 17:12:45   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:45   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:45   before fopen
2022-11-07 17:12:45   after fopen
2022-11-07 17:12:45   __ DEBUG_proc: main > GetInVector  try #0.b 
2022-11-07 17:12:48   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:48   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:48   before fopen
2022-11-07 17:12:48   after fopen
2022-11-07 17:12:48   __ DEBUG_proc: main > GetInVector  try #0.b 
2022-11-07 17:12:51   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:51   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:51   before fopen
2022-11-07 17:12:51   after fopen
2022-11-07 17:12:51   __ DEBUG_proc: main > GetInVector  try #0.b 
2022-11-07 17:12:54   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:54   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:54   before fopen

W tym miejscu log się kończy, a proces przestaje istnieć.
Nie jestem w stanie dowiedzieć się, z jakiego powodu nie nagle nie udało się otworzyć pliku, a zamiast tego proces znika. TRY-CATCH powinno chyba przechwycić wyjątek, a tu nic.

Proszę o pomoc,
Jacek

5
  1. Bardzo źle moje oczy akceptują straszny mix C i C++
    jak widzę str.c_ctr() aby uzyskać kompatybilność z C, to mi sie coś w środku przekręca.

  2. funkcje C nie rzucają wyjątkami, gdybyś używał zamiast fopen() np strumieni, by się włączyły w obługe wyjatków

  3. w C/C++ jeszcze jedną nieprzyjemną cechą jest, ze może zajść wyjątek systemowy (general protecion fault etcx), który by się tu nie złapał w catch, tylko proces zostanie zabity

  4. pominę, ze jest tu wiele atrybutów niskiej jakosci, nie zamykane pliki itd. Jak mówisz to uruchamiasz współbieżnie, strach się bać.

  5. jak widzę drabinkę try { bez żadnego kodu który by różnicował, to nie wierze, że panujesz nad projektem.
    Prywatnie mnie przeraża 90% przewaga kody logujacego nad merytorycznym, ale być może w szczególnym projekcie itd...

    Jesli funckaj SaveLog() buforuje sama w sobie, lub robi to "underlying level", właściwie trudno coś wnioskować z końca logu. MOŻE się niektóre wykonują, może nie.,

  6. nigdy bym tak nie używał * locatime() (to funkcja, może czytałeś, zwracająca pointer na jeden bufor statyczny). Jak bym jej nigdy nie kopiował operatorem gwiazdkowym, tylko korzystał ze wskaznika. Nie umiem wytłumaczyć dlaczego, takie wzorce w palcach

0
ZrobieDobrze napisał(a):

Bardzo źle moje oczy akceptują straszny mix C i C++
jak widzę str.c_ctr() aby uzyskać kompatybilność z C, to mi sie coś w środku przekręca.

funkcje C nie rzucają wyjątkami, gdybyś używał zamiast fopen() np strumieni, by się włączyły w obługe wyjatków

w C/C++ jeszcze jedną nieprzyjemną cechą jest, ze może zajść wyjątek systemowy (general protecion fault etcx), który by się tu nie złapał.

pominę, ze jest tu wiele atrybutów niskiej jakosci, nie zamykane pliki itd. Jak mówisz to uruchamiasz współbieżnie, strach się bać.

Ad 2) Możesz mnie nakierować na dokumentację, czego użyć zamiast fopen?
Ad 4) Plik jest zamykany trochę dalej, więc to akurat nie błąd.

2

Jak działa txt2string? Bo te użycia txt2string("coś").append("coś").append("coś") bardzo przypominają use after free (tzn, używasz obiektu zwróconego z txt2string, a potem append zwraca tylko referencję do niego, natomiast sam obiekt umiera). Poza tym, jeśli proces kończy się sigsegv, albo abortem, to spore szanse, że bufor związany z twoim plikiem z logami nie jest spłukiwany, i po prostu nigdy do tego pliku faktycznie nie trafia.

0
enedil napisał(a):

Jak działa txt2string? Bo te użycia txt2string("coś").append("coś").append("coś") bardzo przypominają use after free (tzn, używasz obiektu zwróconego z txt2string, a potem append zwraca tylko referencję do niego, natomiast sam obiekt umiera). Poza tym, jeśli proces kończy się sigsegv, albo abortem, to spore szanse, że bufor związany z twoim plikiem z logami nie jest spłukiwany, i po prostu nigdy do tego pliku faktycznie nie trafia.

txt2string jest dziwaczne, bo to tylko:

string txt2string(string p_txt) {
	return p_txt;
}

abym mógł dokładać .append()

1
Blackhole napisał(a):
enedil napisał(a):

Jak działa txt2string? Bo te użycia txt2string("coś").append("coś").append("coś") bardzo przypominają use after free (tzn, używasz obiektu zwróconego z txt2string, a potem append zwraca tylko referencję do niego, natomiast sam obiekt umiera). Poza tym, jeśli proces kończy się sigsegv, albo abortem, to spore szanse, że bufor związany z twoim plikiem z logami nie jest spłukiwany, i po prostu nigdy do tego pliku faktycznie nie trafia.

txt2string jest dziwaczne, bo to tylko:

string txt2string(string p_txt) {
	return p_txt;
}

abym mógł dokładać .append()

Plomba (kolejna) żeby (przez niejawne rzutowanie) mieć "jakaś" kompatybilność z C-stringiem.
Suma tych plomb jest DLA MNIE zupełnie niewiarygodna.

A co do podstawowego zagadnienia: może użyć debugger'a?

1

@Blackhole czemu nie użyjesz:

stringstream ss;
ss<<txt2string(ctime_txt)<<txt2string(mtime_txt)<<txt2string(atime_txt)<<"    fileSize="<<stats.st_size<<"B    "<<faccess;
SaveLog(ss.str().c_str());

?

4
  1. Wywal cały ten kod, serio.
  2. Jeśli już łapiesz wyjątki, to łap std::exception const&.
  3. Jeśli masz w projekcie wyjątki niedziedziczące z std::exception, to patrz punkt pierwszy.
  4. Jeśli ten kod jest wynikiem Twojej pracy, to - no off - nie powinieneś zabierać się za nic latającego na produkcji. Jeszcze przed Tobą wiele nauki.
0

Czym mam zastąpić fopen() (bo to on powoduje zabicie procesu)?

PS. Jestem świadom, że moja znajomość C++ jest słaba.

1
Blackhole napisał(a):

Czym mam zastąpić fopen (bo to on powoduje zabicie procesu)?

PS. Jestem świadom, że moja znajomość C++ jest słaba.

  1. odróżniasz wyjątki języka i wyjatki systemowe (w językach dajacych kod maszynowy) ?
  2. Czym zastąpić ... zacytuję moją własną wypowiedź "ta wiedza jest na pierwszych 10 kartkach książki do C++, gdybyś takową czytał"

Co do ubijania procesu, zakładajac, że szerszy kod jest tej samej jakości, to tu już może chodzić po wszelkich UB, które przygotowałes wcześniej ... sądzę ze na pewno masz wiele UB

@kq +1

Jest nieetyczne, aby kolega kodował na produkcję.

0
ZrobieDobrze napisał(a):

Jest nieetyczne, aby kolega kodował na produkcję.

Bez obaw, ja tylko hobbystycznie.

0

Podstawowe pytanie: A co "debuger" na Twój problem ?
Zasugerował bym zainteresowanie się gotowcem np. spdlog
oraz fmt (spdlog używa fmt)

1
Blackhole napisał(a):
ZrobieDobrze napisał(a):

Jest nieetyczne, aby kolega kodował na produkcję.

Bez obaw, ja tylko hobbystycznie.

W mojej opinii wzięcie tematu który przerósł posiadane podstawy nie jest dydaktyczne.
Owszem, pot krew i łzy nauczą tej czy innej techniki, ale chaotyczne przypadkowe łapanie myszy (pytanie czy skuteczne, bo na pewno nie ekonomiczne) uczy potworną ilosć złych wzorców. +15% w jednym obszarze -40% w innym

Powiedziałbym że w C/C++ jest o wiele gorsze niż w innych jezykach, bo pacjent buduje w swoim mózgu skojarzenia "przeciez działa", ślepotę na UB / subtelne błędy
Np szalone drukowanie outputu zamiast podciągniecie się co do debugegra

Utrzymany w harmonijnych proporcjach rozwój teorii i praktyki, budowanie fundamentów - tego tzreba

2
Blackhole napisał(a):
try {
	try {
    ....
	} catch (exception &e) {
		SaveLog("exception przy fopen");
		SaveLog(txt2string("Błąd otwierania pliku '").append(nazwa).append("':  ").append(e.what()));
	}
}
catch (...) {
	SaveLog(txt2string("Nieudane otwieranie pliku '").append(nazwa).append("'"));
}

Zastanowiłeś się w ogóle, cze taka forma tego kodu ma w ogóle sens?
Pierwszy raz widzę, by ktoś się jąkał w kodzie (to chyba najlepsze określenie).

Blackhole napisał(a):

Do loga zapisuje mi on:

2022-11-07 17:12:45   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:45   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:45   before fopen
2022-11-07 17:12:45   after fopen
2022-11-07 17:12:45   __ DEBUG_proc: main > GetInVector  try #0.b 
2022-11-07 17:12:48   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:48   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:48   before fopen
2022-11-07 17:12:48   after fopen
2022-11-07 17:12:48   __ DEBUG_proc: main > GetInVector  try #0.b 
2022-11-07 17:12:51   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:51   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:51   before fopen
2022-11-07 17:12:51   after fopen
2022-11-07 17:12:51   __ DEBUG_proc: main > GetInVector  try #0.b 
2022-11-07 17:12:54   Będzie zaraz fopen pliku in/zt.in
2022-11-07 17:12:54   created: 2022-11-07 17:00:03   modified: 2022-11-07 17:00:03   accessed: 2022-11-07 17:00:05    fileSize=717B    File access: r--
2022-11-07 17:12:54   before fopen

W tym miejscu log się kończy, a proces przestaje istnieć.
Nie jestem w stanie dowiedzieć się, z jakiego powodu nie nagle nie udało się otworzyć pliku, a zamiast tego proces znika. TRY-CATCH powinno chyba przechwycić wyjątek, a tu nic.

To jest zwykły objaw crash-a. Debugowałeś ten kod? Użyłeś sanitizer-a/debuger-a?
Jak podasz kompletny kod, który się kompiluje, to znajdziemy ci to w 5 minut, bez czytania kodu, przepchnie sie kod przez standardowe narzędzia i od razu błąd sie znajdzie.
Swoją drogą czy masz jakieś ostrzeżenia podczas kompilacji?

3

Próba połatania brakującego kodu, nie powoduje problemów (address sanitizer nic nie znajduje): https://godbolt.org/z/f4P9x9ara
Możliwe, że błąd jest w kodzie, którego nie widać, albo przez przypadek załatałem błąd.

Wniosek z łatania jest jeden. Nie opanowałeś podstaw C++ i bierzesz się za coś, co wykracza poza zakres twoich obecnych możliwości (próbujesz napisać proces działający w tle).
Lepiej zrobisz jak skupisz się na opanowani podstaw bez sięgania do API systemowego.

1
  1. Skąd wiesz, że wywala się fopen(). Może nawet nie jest wołane tylko proces ginie wcześniej? To prosto sprawdzić, wystarczy wywoływać proces via strace i logować wywołania systemowe.
  2. Masz jakiegoś core dumpa z tych wywaleń procesu? (Ustawione ulimit -S -c unlimited ?)
  3. Jeśli chcesz debugować ręcznie, to wywal te mechanizmy własnego logowania, wal komunikaty prosto na std::cout , przy uruchomieniu przekieruj stdout/err do pliku. Wykluczysz w ten sposób mechanizm logowania jako źródło problemu.
0

Tworzysz taką oto klasę:

#include <iostream>
#include <sstream>
#include <iomanip>
#include <string>
using namespace std;

void SaveLog(const string &message)
{
	cerr<<message<<endl;
}

class Log
{
	private:
	stringstream ss;
	public:
	string str()const { return ss.str(); }
	void reset() { ss.str(""); }
	operator string()const { return str(); }
	void save()
	{
		SaveLog(str());
		reset();
	}
	template<typename T> Log &operator<<(const T &str) { ss<<str; return *this; }
};


int main()
{
	Log log;
	(log<<"A="<<1<<"; B="<<2.5<<";").save();
	(log<<"C="<<setfill('0')<<setw(3)<<7<<";").save();
	(log<<"D=0x"<<hex<<setfill('0')<<setw(2)<<15<<";").save();
	return 0;
}

i masz problemy pozamiatane.

Aczkolwiek warto tą klasę mocno rozbudować, np. poprzez dodanie formatera daty i nie tylko.

2

Próba ogarnięcia tego bałaganu:
https://godbolt.org/z/qb4xaxPc9

#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <chrono>

#include <fmt/chrono.h>
#include <fmt/ostream.h>

#include <sys/stat.h>
#include <unistd.h>

void print_file_status(const std::string& f)
{
    struct stat stats;
    struct tm dt;
    if (stat(f.c_str(), &stats) != 0) {
        std::perror("stat check");
        return;
    }

    dt = *(localtime(&stats.st_ctime));
    fmt::print(
        "    name: {}\n"
        " created: {:%Y-%m-%d %H:%M:%S}\n"
        "modified: {:%Y-%m-%d %H:%M:%S}\n"
        "accessed: {:%Y-%m-%d %H:%M:%S}\n"
        "fileSize: {}B\n"
        "File access: {}{}{}\n",
        f,
        fmt::localtime(stats.st_ctime),
        fmt::localtime(stats.st_mtime),
        fmt::localtime(stats.st_atime),
        stats.st_size,
        "-r"[!!(stats.st_mode & R_OK)],
        "-w"[!!(stats.st_mode & W_OK)],
        "-x"[!!(stats.st_mode & X_OK)]);
}

int main()
{
    const std::string nazwa = "example.cpp";

    try {
        fmt::print("now: {}\n\n", std::chrono::system_clock::now());
        print_file_status(nazwa);
        fmt::print("\nbefore open\n");
        std::this_thread::sleep_for(std::chrono::seconds(2));
        fmt::print("now: {}\n\n", std::chrono::system_clock::now());

        std::ifstream f{nazwa};
        f.exceptions(std::ios::badbit | std::ios::failbit);
        fmt::print("after open\n");
        print_file_status(nazwa);
    }
    catch (const std::system_error& e)
    {
        fmt::print("exception {} - {}\n", e.what(), e.code());
    }
    catch (const std::exception& e) {
        fmt::print("exception {}\n", e.what());
    }

    return 0;
}
0

a sprawdź zamiast

fi = fopen(nazwa.c_str(), "rb");

samo wywołanie

nazwa.c_str();
2
Miang napisał(a):

a sprawdź zamiast

fi = fopen(nazwa.c_str(), "rb");

samo wywołanie

nazwa.c_str();

Ja obstawiam każdą inną hipotezę.
nazwa.s_str() wydaje się jest drukowana niewiele wcześniej, i nie zachodzi z tym jakaś patologia. Oficjalnie nic jej nie zmienia.

Obstawiam również intensywnie takie scenariusze, jak przejechanie się po pamięci, gdzie jest tablica biblioteki standardowej FILE[] itd
Przy tak niskiej jakości kodu taka hipoteza wcale nie podlega natychmiastowemu odrzuceniu.
Wystarczy zamienić parametry fread() i mieć w d..ie warningi

NIE WIEMY, co jest / jakie szokujace rzeczy są w szerszym kontekście kodu, ten "profilaktycznie" jest ukrywany

3
Miang napisał(a):

a sprawdź zamiast

fi = fopen(nazwa.c_str(), "rb");

samo wywołanie

nazwa.c_str();

to nie to. w tym kodzie otworzył plik w teori. Został zwrócony wskaźnik na FILE. Jak by tam były pierdoły to powinien null pointer zwrócić. A później przy użyci tego fi teoretycznie powinien wszystko sprawdzić.

w ogóle o czym my też gadamy @Blackhole pierwsza sprawa przy debugowaniu tego bagna. GDB. podpiąć do procesu i obserwować co wyrzuci gdb. to powinien być twój pierwszy krok. To może rzucić nieco światła jak nie to ustawić breakpoint i krok po kroku.
edit:
gdybys nie znał gdb
link
można go używać z linii komend albo z jakiegoś IDE odpalić.

0

Przerobiłem dostęp do plików z C (fopen) na C++ (fstream) i już się nie wywala.
Dzięki za pomoc.

1 użytkowników online, w tym zalogowanych: 0, gości: 1