c++ 질문 드립니다.

lalupo20의 이미지

간단히 질문드리면 함수 내부를 수정하지 않고 에러처리를 할 수 있을까요?

예를 들어

int a() {
 
b();
c();
 
}

이런 코드가 있을 시에 b함수 내부에서 에러가 발생해서 프로그램이 뻗는 증상이 있을 경우

b 함수에 손 안대고 에러를 처리할 수 있을까요?

지금 libjpeg 라이브러리 활용중이고, mp3파일의 앨범파일을 받아와서 화면에 출력하는 기능을 구현중인데, 에러가 나는 파일들이 있네요. 음원사이트를 이용해서 정상적인 루트를 통해서 구한 mp3파일들이구요.

다음과 같은 코드이고 에러나는 부분은 jpeg_read_header()함수 입니다.

unsigned char* GLTexture::loadJPEGTextureFromSDcard(const char* path, int* width, int* height) {
	unsigned char* buf;
	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	int bytes_per_pixel;
	JSAMPROW row_pointer[1];
 
	FILE* infile = fopen(path, "rb");
//	int err = ferror(infile);
 
	if (infile == NULL) return NULL;
 
	unsigned long location = 0;
	int i = 0;
 
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
 
	jpeg_stdio_src(&cinfo, infile);
	jpeg_read_header(&cinfo, TRUE);
 
	*width = cinfo.image_width;
	*height = cinfo.image_height;
 
	jpeg_start_decompress(&cinfo);
 
	buf = new unsigned char[cinfo.output_width * cinfo.output_height * cinfo.num_components];
	row_pointer[0] = (unsigned char*)malloc(cinfo.output_width * cinfo.num_components);
 
	memset(buf, 0, sizeof(buf));
 
	size_t size = cinfo.output_width * cinfo.output_height * cinfo.num_components;
	while (cinfo.output_scanline < cinfo.image_width) {
		jpeg_read_scanlines(&cinfo, row_pointer, 1);
		int row = cinfo.image_width * cinfo.num_components;
		for (i = 0; i < row; i += 4) {
			if (i > row) break;
			if (location > size) break;
			//
			buf[location++] = row_pointer[0][row - i - 0];
			buf[location++] = row_pointer[0][row - i - 1];
			buf[location++] = row_pointer[0][row - i - 2];
			buf[location++] = row_pointer[0][row - i - 3];
		}
		if (location > size) break;
	}
	for (int i = 0; i < size; i += 3) {
		unsigned char temp = buf[1 + i];
		buf[1 + i] = buf[2 + i];
		buf[2 + i] = temp;
	}
 
	jpeg_destroy_decompress(&cinfo);
	free(row_pointer[0]);
	fclose(infile);
	return buf;
}

어떻게든 고쳐보려고 디버깅하면서 계속 파고 들어가 봤는데 잘 모르겠습니다.

혹시 제가 잘 모르는 방법이 있나 해서 질문드립니다.

나빌레라의 이미지

에러가 어떻게 나는지에 따라 다릅니다.
예외를 던진다면 예외를 잡아서 처리하면 되고,
리턴 코드로 에러를 알린다면 리턴을 처리하면 되죠.

----------------------
얇은 사 하이얀 고깔은 고이 접어서 나빌레라

글쎄요의 이미지

chanik의 이미지

일부 이미지에서만 문제가 생긴다면 그 이미지에 문제가 생겼을 수 있으니 검사해보면 어떨까요. 아래 페이지에 jpeginfo라는 툴이 나오는군요.

https://photo.stackexchange.com/questions/46919/is-there-a-tool-to-check-the-file-integrity-of-a-series-of-images

jpeginfo는 libjpeg8 라이브러리에 의존(참고: https://packages.ubuntu.com/focal/jpeginfo)하므로, jpeginfo에서 이미지 문제가 발견되지 않는다면 지금 쓰시는 libjpeg 버전이나 사용법을 바꿔볼 여지가 있음을 시사하는 셈일 것입니다.

https://en.wikipedia.org/wiki/Libjpeg#Versions 을 보니 6x, 7, 8, ... 식으로 libjpeg 라이브러리가 변경돼오며 이미지포맷도 확장됐던 모양이네요. 이전에 올리신 질문글(https://kldp.org/node/164220)의 버전 오류가 이와 관련있지 않나 싶기도 합니다.

다른 분들 조언대로 jpeg_read_header() 함수의 반환코드를 살펴보시는게 출발점이 되겠네요.

swish95의 이미지

원하는게 try .. catch 로 안되나요?

------------------------------------------------------------
ProgrammingHolic

lalupo20의 이미지

안되는거 같아요.

swish95의 이미지

설마 몰라서 그런건 아닐것 같고
다른 이유가 있는듯 합니다.

b() 함수 에러면 그냥 감싸면 되지만 내부적으로 에러가 나면서 프로그램이 종료되나 본데요?

에러 로그를 보여주시는게 도움이 되겟네요

------------------------------------------------------------
ProgrammingHolic

chanik의 이미지

오류를 일으키는 이미지 하나를 올려보시면 어떨까요? 문제가 재현되는 짧은 샘플코드와 빌드명령도 있으면 도움을 얻기가 더 수월할 것입니다.

libjpeg과 해당 이미지만의 문제인지 PC의 리눅스에서 확인해보시는 것도 도움이 될 것 같고요.

lalupo20의 이미지

이미지가 깨져서 보이는데 그것때문에 그럴까요?

이 파일은 그냥 인터넷에서 구한 mp3 파일이네요. 정상적인 경로로 만들어진 파일이 아니라서 에러가 나는걸까요?

댓글 첨부 파일: 
첨부파일 크기
Image icon art[1].jpg551.78 KB
익명 사용자의 이미지

다운받아서 살펴보니

1. png 파일이군요.
2. png chunk 중 하나에 crc 에러가 있는 것 같습니다.

흠...?

chanik의 이미지

위의 익명님 말씀대로 이미지에 문제가 있었던 모양이네요. 문제를 일으키는 다른 이미지들도 확인해보시면 비슷하게 이미지에 이상이 있지 않을까 추측합니다.

$ jpeginfo __art\[1\].jpg
__art[1].jpg  Not a JPEG file: starts with 0x89 0x50  [ERROR]

위와 같이 jpeginfo에서는 그냥 JPEG 파일이 아니라고만 알려주기에,
ImageMagick 패키지에 포함된 identify로 검사해보니 PNG 이미지라고 나오고,

$ identify.exe __art\[1\].jpg
__art[1].jpg PNG 700x700 700x700+0+0 8-bit sRGB 565KB 0.000u 0:00.047

-verbose 옵션을 주니까 이미지에 오류가 있다고도 알려주네요.

$ identify.exe -verbose __art\[1\].jpg
identify: bad adaptive filter value `__art[1].jpg' @ error/png.c/MagickPNGErrorHandler/1630.
identify: corrupt image `__art[1].jpg' @ error/png.c/ReadPNGImage/3958.

이런 식의 검사 이전에, 그냥 뷰어로 띄워보셨을때 깨진 것만 봐도 알 수 있고요.

파일 전송 과정에서 한 비트 정도 틀리게 받아지는 일은 비교적 흔하게 발생합니다. 리눅스 배포판 설치용 DVD 이미지 다운로드 사이트에 항상 체크섬 파일이 함께 있는 이유도 이것 때문이고, 체크섬 파일을 제공해도 사람들이 그걸로 검사를 잘 안 하니까 DVD 구워서 부팅하면 DVD 내용물 검사하는 메뉴까지 넣어놓곤 하는 것이죠.

lalupo20의 이미지

답변 감사드립니당

chanik의 이미지

이번에 겪으신 jpeg_read_header() 실행중 프로그램이 종료돼버리는 현상 관련하여 아래 페이지에 참고할만한 내용이 있네요.

https://www.linuxquestions.org/questions/programming-9/libjpeg-error-handling-15248/

Quote:
The libjpeg docs say:

Error handling
--------------

When the default error handler is used, any error detected inside the JPEG
routines will cause a message to be printed on stderr, followed by exit().
You can supply your own error handling routines to override this behavior
and to control the treatment of nonfatal warnings and trace/debug messages.

jpeg_read_header() 실행중 내부에서 문제가 검출되자 표준오류로 메시지를 출력하고 제멋대로 exit() 해버린 것인가 봅니다. 출력된 메시지는 "Not a JPEG file: starts with 0x89 0x50" 였을 것이고요. 이 기본행동을 바꾸려면 오류핸들러를 만들어 등록해주면 되고, c++에서는 오류핸들러를 좀 응용하여 exception이 발생되도록 처리할수도 있는 모양입니다.

참고: https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg

위 페이지의 내용을 참조하여, 올리신 코드에서 아래 부분을

	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
 
	jpeg_stdio_src(&cinfo, infile);
	jpeg_read_header(&cinfo, TRUE);
	.
	.

아래와 같은 식으로 수정하면 프로그램이 그냥 죽는 대신 exception으로 잡힐 것입니다. jpg 파일이 아닌 경우 exception으로 검출되므로, jpg가 맞는지 확인코드는 따로 짜넣지 않아도 되겠습니다. 그리고, libjpeg 함수들중 반환값이 있는 경우 확인코드를 넣으셔야 미지의 오류에 대해 더 튼튼하게 동작하겠죠.

	cinfo.err = jpeg_std_error(&jerr);
	jerr.error_exit = [](j_common_ptr cinfo){throw cinfo->err;};
	try
	{
		jpeg_create_decompress(&cinfo);
 
		jpeg_stdio_src(&cinfo, infile);
		int rc = jpeg_read_header(&cinfo, TRUE);
		if (rc != 1) {	/* 오류 처리 */ }
	.
	.
	}
	catch (struct jpeg_error_mgr *err)
	{
		char pszErr[1024];
		(cinfo.err->format_message)((j_common_ptr)&cinfo, pszErr);
		std::cerr << pszErr << std::endl;
		... // fclose() 등 청소 후 오류 리턴
		return NULL;
	}

이런 식의 오류핸들러가, 처음 올리셨던 질문에 대한 답이 될 수 있겠습니다.

대충 짜깁기한 코드이니 동작이 잘 안 되면 위에 링크한 페이지를 참고하시고요.

lalupo20의 이미지

집에가서 해봐야 겠네요!

lalupo20의 이미지

abort? 예외가 처리되지 않음 뜨네요.

chanik의 이미지

Ubuntu-20.04 및 CentOS 7.9 PC에서 했고, 아래 페이지의 memdjpeg.c 샘플코드를 받아서 exception 발생시키는 오류핸들러 추가하고, 올려주신 이미지를 대상으로 실행했습니다.

https://gist.github.com/PhirePhly/3080633

$ diff -u memdjpeg.c memdjpeg.cpp
--- memdjpeg.c  2021-02-22 13:11:41.830191114 +0900
+++ memdjpeg.cpp        2021-02-24 08:41:44.959063413 +0900
@@ -20,8 +20,9 @@
 #include <syslog.h>
 #include <sys/stat.h>
 
-#include <jpeglib.h>
+#include <iostream>
 
+#include <jpeglib.h>
 
 int main (int argc, char *argv[]) {
        int rc, i, j;
@@ -98,6 +99,7 @@
        // so it's likely you'll want to replace it or supplement it with
        // your own.
        cinfo.err = jpeg_std_error(&jerr);
+       jerr.error_exit = [](j_common_ptr cinfo){throw cinfo->err;};
        jpeg_create_decompress(&cinfo);
 
 
@@ -118,7 +120,18 @@
        // Have the decompressor scan the jpeg header. This won't populate
        // the cinfo struct output fields, but will indicate if the
        // jpeg is valid.
-       rc = jpeg_read_header(&cinfo, TRUE);
+       try
+       {
+               rc = jpeg_read_header(&cinfo, TRUE);
+       }
+       catch (struct jpeg_error_mgr *err)
+       {
+               char pszErr[1024];
+               (cinfo.err->format_message)((j_common_ptr)&cinfo, pszErr);
+               std::cerr << "Exception Caught : ";
+               std::cerr << pszErr << std::endl;
+               return 100;
+       }
 
        if (rc != 1) {
                syslog(LOG_ERR, "File does not seem to be a normal JPEG");

$ g++ memdjpeg.cpp -ljpeg -o memdjpeg
$ ./memdjpeg __art\[1\].jpg
./memdjpeg[3107904]: Input: Read 565022/565022 bytes
./memdjpeg[3107904]: Proc: Create Decompress struct
./memdjpeg[3107904]: Proc: Set memory buffer as source
./memdjpeg[3107904]: Proc: Read the JPEG header
Exception Caught : Not a JPEG file: starts with 0x89 0x50
$ echo $?
100

과정은 위와 같고 수정된 소스파일도 첨부했으니, 우선 일반 리눅스PC에서 이 샘플 동작을 확인하여 코드에 대한 의구심부터 해결하시길 권합니다.

댓글 첨부 파일: 
첨부파일 크기
Package icon memdjpeg.zip2.94 KB
chanik의 이미지

혹시 PC에서는 되고 안드로이드 환경에서는 안 되는건가 싶어서 "NDK c++ exception abort" 검색어로 뒤져보니 관련 이슈 글들이 좀 나오네요. 참고가 될지도 모르겠습니다.

lalupo20의 이미지

jpeg_mem_src 함수에서 undefined reference 에러가 뜨네요.

익명 사용자의 이미지

링크 옵션을 안 주거나 잘못 줘서 그렇습니다.

잘 주세요.

chanik의 이미지

제가 샘플을 테스트할때 Ubuntu-20.04, CentOS-7.9, CentOS-5.11 등 세 군데서 해봤는데, 이중 CentOS-5.11에서 동일한 링크오류가 있었습니다. 5.11은 매우 오래된 릴리스이고 libjpeg 6b만 있더군요. 그래서 Ubuntu-20.04와 CentOS-7.9에서 libjpeg 버전 8로 테스트한 결과를 답변으로 단 것입니다.

지금 libjpeg 6b 버전을 쓰시는 것 같은데요. libjpeg 8 버전을 쓰시면 샘플빌드는 해결됩니다.

꼭 6b를 쓰셔야 할 이유가 있다면, 제가 사용한 샘플에서 open() ~ jpeg_mem_src() 식으로 처리하는 부분을 lalupo20님의 코드에서처럼 fopen() ~ jpeg_stdio_src() 식으로 바꾸시거나, 구글에 jpeg_mem_src 검색하면 나오는 https://stackoverflow.com/questions/5280756/libjpeg-ver-6b-jpeg-stdio-src-vs-jpeg-mem-src 등의 글이 참고가 되겠습니다. 어차피 C++ 예외 발생을 확인하는 것이 중요하지 제가 사용한 샘플을 꼭 써야 할 이유는 없으니, 6b에서 동작하는 다른 샘플을 구해서 오류핸들러 추가하셔도 되고요. 멀리서 찾을 것 없이, 질문글에 올리신 코드를 떼어서 샘플로 쓰셔도 되겠네요.


lalupo20님의 이전글(https://kldp.org/node/164220)이 libjpeg 버전 오류 관련 질문이었는데, 그때 오류처리하는 코드를 그냥 주석처리했더니 실행되더라 하면서 넘어가신 것 같던데요. 지금 어떤 상태로 libjpeg을 쓰시는 것인지도 명확히 확인해보시길 권합니다. 사용중인 libjpeg 버전에 대한 자각이 없는 것 같기도 합니다.


이 질문글의 질답 진행과정을 찬찬히 다시 검토하면서 문제해결방식에 대해 숙고해보시면 도움이 될 것이라 생각합니다.

lalupo20의 이미지

오늘 집에 가서 최신버전 구해서 빌드해봐야겠네여. 감사합니다!

swish95의 이미지

님의 열정에 박수를 보냅니다. ^^

------------------------------------------------------------
ProgrammingHolic

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.