博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何在嵌入式Linux产品中做立体、覆盖产品生命期的调试 (5)
阅读量:2520 次
发布时间:2019-05-11

本文共 12639 字,大约阅读时间需要 42 分钟。

 
上接:如何在嵌入式Linux产品中做立体、覆盖产品生命期的调试 ( 4)
 
这篇谈谈
error code
的使用和做法:
程序不出错则大家都好,出错了,首先找谁,
error code!
 
大家知道,嵌入式
Linux
产品的软件一般有个分层的:
从上到下依次为:
application
middleware
/Enabler
driver
kernel
 
每一层都会有
error code,
下层的
error code
提供给上层调用时用;
所以说,上面一层的代码调用下面一层的
API
时,如果该
API
提供了
error code
则说明这个
API
还是较好的,如果没有,对于你纠错帮助不大。
 
下面我举几个常用的:
application
调用
glib
的函数:
 
 
 
application
调用
sqlite
的函数:
 
//sqlite
提供
error
的手段比较多
1.      
直接返回错误原因
2.      
返回错误码,有个函数可以根据错误码提取出错原因:
这个函数是
sqlite
里面比较亲切的函数:
const char *sqlite_error_string (int rc);
多用这个函数,你会发现
sqlite
原来这么简单;
 
sqlite
算是在嵌入式
Linux
产品中用的较广泛的数据库了,这个开源项目提供的
error code/error string
是继承自
Glib,
并发扬广大了;
 
 
 
application
调用
gconf
 
//
原型
gboolean gconf_init
GError **error
;
 
GError *error = NULL;
if (!gconf_init (&error))
{
fprintf (stderr, “Failed to initialize gconf, reason: %s/n”, error->message);
g_error_free (error);
error = NULL;
}
 
//
gconf
系统里面,主要复制了
GError
的一套内容,并且把出错信息填充到
GError
中,
这样调用者就可以很好的获取出错信息;
 
gconf
也是嵌入式
Linux
产品中使用的较多的用于记录
setting
的开源项目,对于出错的处理,继承自
Glib
,并没有向
Sqlite
那样做了扩充,应该说是中规中矩吧,也可以了。
 
 
 
上面的
sqlite/gconf/dbus/gstreamre
等都是使用比较多的开源软件,本省提供的
error code
error string
也是比较好的。
 
如果我们自己写程序,比如写一些
middleware
这一层的代码,就需要给上层的
application
提供足够好的
error code
的支持。
 
下面就看看如何在
middleware
层增加对
error code
的很好支持,首先研究4
个开源项目的
error code
的做法:
A) sqlite,
B) gconf
C) gstreamer
D) dbus
 
A) sqlite
的做法
    
==================sqlite
error code
做法
=============
Sqlite
error code
的做法比较简单、直白!没有什么花花肠子。
就是
error code + error string.
 
1
定义
error code
(也称
return code, rc
)的枚举
 
/*
** Return values for sqlite3_exec() and sqlite3_step()
*/
#define SQLITE_OK           0   /* Successful result */
/* beginning-of-error-codes */
#define SQLITE_ERROR        1   /* SQL error or missing database */
#define SQLITE_INTERNAL     2   /* NOT USED. Internal logic error in SQLite */
// …
参考
sqlite.h.in
文件,编译后放在
sqlite.h
#define SQLITE_DONE        101 /* sqlite3_step() has finished executing */
/* end-of-error-codes */
 
2 定义与error code对应的字串
 
const char *sqlite3ErrStr(int rc){
 const char *z;
 switch( rc & 0xff ){
    case SQLITE_ROW:
    case SQLITE_DONE:
    case SQLITE_OK:         z = "not an error";                          break;
    case SQLITE_ERROR:      z = "SQL logic error or missing database";   break;
 
 
 //
参考
sqlite/src/main.c
  
    default:                z = "unknown error";                         break;
 }
 return z;
}
 
 
3 sqlite
GError
的关系不大,另外
sqlite
还有一个很有个性的东西:
提取
“最近”
发生的错误码或者错误信息!
这三个可是
sqlite
调试的大功臣啊!当调用
sqlite
的函数发生错误时,不要客气,调用这几个函数去查查错误原因,事半功倍!
const char *sqlite3_errmsg(sqlite3 *db)
const void *sqlite3_errmsg16(sqlite3 *db)
int sqlite3_errcode(sqlite3 *db)
 
 
 
B) gconf
的做法
 
==================gconf
error code
做法
=============
Gconf的error code最没有个性,就是想方设法满足GError! 往GError中填充信息。
 
1
定义
gconf
中可能出现的错误的种类,这里使用的是枚举
/* Sync with ConfigErrorType in GConf.idl, and some switch statements in the code */
typedef enum { /*< prefix=GCONF_ERROR >*/
 GCONF_ERROR_SUCCESS = 0,
 GCONF_ERROR_FAILED = 1,         /* Something didn't work, don't know why, probably unrecoverable
 
// …
参考
gconf-error.h  
 GCONF_ERROR_IN_SHUTDOWN = 16    /* server is shutting down */
} GConfError;
 
2
定义与上面枚举对应的错误信息字符串数组:
static const gchar* err_msgs[] = {
 N_("Success"),
 N_("Failed"),
 
// …
参考
gconf-error.c
 N_("No database available to save your configuration")
};
这点和
GStreamer
的差距:没有
GSteamer
做的巧妙
   
DBus
的差距:不能像
dbus
那样灵活修改;
 
3
定义
gconf
的身份证,就是声明一个
quark,
GStreamer
类似
#define GCONF_ERROR gconf_error_quark ()
 
GQuark
gconf_error_quark (void)
{
 static GQuark err_q = 0;
 
 if (err_q == 0)
    err_q = g_quark_from_static_string ("gconf-error-quark");
 
 return err_q;
}
 
 
4 把错误信息填充到GError中
 
static
GError*
gconf_error_new_valist(GConfError en, const gchar* fmt, va_list args);
 
GError*
gconf_error_new(GConfError en, const gchar* fmt, ...);
 
void
gconf_set_error       (
GError** err, GConfError en, const gchar* fmt, ...);
 
// … 具体参考gconf-error.c
从上面可以看出,GConf的error是委身于GError的,没有独立性。
 
5 外部调用GConf的接口API,大部分接口API都有一个GError** err参数,就是用这个参数接收Gconf返回的错误信息的;
 
C) gstreamer
的做法
 
==================Gstreamer
error code
做法
=============
 
Gstreamer
中如何做
error code/error string
的?
GStreamer
中有
4
大类
error:
1 GST_CORE_ERROR
2 GST_LIBRARY_ERROR
3 GST_RESOURCE_ERROR
4 GST_STREAM_ERROR
 
每一大类都有一些细分的
error:
 
1, core
error
,主要是
gstreamer
核心概念方面的错误
typedef enum
{
 GST_
CORE_ERROR_FAILED = 1,
 GST_
CORE_ERROR_TOO_LAZY,
 GST_
CORE_ERROR_NOT_IMPLEMENTED,
 GST_
CORE_ERROR_STATE_CHANGE,
 GST_
CORE_ERROR_PAD,
 GST_
CORE_ERROR_THREAD,
 GST_
CORE_ERROR_NEGOTIATION,
 GST_
CORE_ERROR_EVENT,
 GST_
CORE_ERROR_SEEK,
 GST_
CORE_ERROR_CAPS,
 GST_
CORE_ERROR_TAG,
 GST_
CORE_ERROR_MISSING_PLUGIN,
 GST_
CORE_ERROR_CLOCK,
 GST_CORE_ERROR_NUM_ERRORS
} GstCoreError;
 
2,
库操作方面的
error
typedef enum
{
 GST_
LIBRARY_ERROR_FAILED = 1,
 GST_
LIBRARY_ERROR_TOO_LAZY,
 GST_
LIBRARY_ERROR_INIT,
 GST_
LIBRARY_ERROR_SHUTDOWN,
 GST_
LIBRARY_ERROR_SETTINGS,
 GST_
LIBRARY_ERROR_ENCODE,
 GST_LIBRARY_ERROR_NUM_ERRORS
} GstLibraryError;
 
3
,资源方面的
error
 
typedef enum
{
 GST_
RESOURCE_ERROR_FAILED = 1,
 GST_
RESOURCE_ERROR_TOO_LAZY,
 GST_
RESOURCE_ERROR_NOT_FOUND,
 GST_
RESOURCE_ERROR_BUSY,
 GST_
RESOURCE_ERROR_OPEN_READ,
 GST_
RESOURCE_ERROR_OPEN_WRITE,
 GST_
RESOURCE_ERROR_OPEN_READ_WRITE,
 GST_
RESOURCE_ERROR_CLOSE,
 GST_
RESOURCE_ERROR_READ,
 GST_
RESOURCE_ERROR_WRITE,
 GST_
RESOURCE_ERROR_SEEK,
 GST_
RESOURCE_ERROR_SYNC,
 GST_
RESOURCE_ERROR_SETTINGS,
 GST_
RESOURCE_ERROR_NO_SPACE_LEFT,
 GST_RESOURCE_ERROR_NUM_ERRORS
} GstResourceError;
 
4
编解码方面的
error
 
typedef enum
{
 GST_
STREAM_ERROR_FAILED = 1,
 GST_
STREAM_ERROR_TOO_LAZY,
 GST_
STREAM_ERROR_NOT_IMPLEMENTED,
 GST_
STREAM_ERROR_TYPE_NOT_FOUND,
 GST_
STREAM_ERROR_WRONG_TYPE,
 GST_
STREAM_ERROR_CODEC_NOT_FOUND,
 GST_
STREAM_ERROR_DECODE,
 GST_
STREAM_ERROR_ENCODE,
 GST_
STREAM_ERROR_DEMUX,
 GST_
STREAM_ERROR_MUX,
 GST_
STREAM_ERROR_FORMAT,
 GST_STREAM_ERROR_NUM_ERRORS
} GstStreamError;
 
上面定义这么多的枚举量,很多人会直接去返回这个错误,没错,可以这么做。但是不直观,不能充分表达错误的原因!
为了充分把错误的原因传达给调用者,
GStreamer
把上面的
error
枚举都做了一个对应的字符串,作的比较巧妙。
 
做法如下:
 
1
定义一个
quark
#define QUARK_FUNC(string) /
GQuark gst_ ## string ## _error_quark (void) {  /
 static GQuark quark;   /
 if (!quark)  /
    quark = g_quark_from_static_string ("gst-" # string "-error-quark"); /
 return quark; }
 
大家知道:夸克是比原子还小的单位。在
glib
里面
quark
是用来区分唯一性的;
一般是根据一个
字符串
得出一个唯一的
quark,
这个产出的
quark
就用来唯一标识那个字符串;
 
2
为每种
error
颁发一个身份证明:
QUARK_FUNC (
core
);
QUARK_FUNC (
library
);
QUARK_FUNC (
resource
);
QUARK_FUNC (
stream
);
上面这
4
句话就是
4
个函数的
implementation
了:
 
GQuark gst_core_error_quark (void){ //…}
GQuark gst_library_error_quark (void) { //…}
GQuark gst_ resource _error_quark (void) { //…}
GQuark gst_stream_error_quark (void) { //…}
 
//
为了方便起见,再定义
二代身份证
#define
GST_LIBRARY_ERROR
   gst_library_error_quark ()
#define
GST_RESOURCE_ERROR
 gst_resource_error_quark ()
#define
GST_CORE_ERROR
      gst_core_error_quark ()
#define
GST_STREAM_ERROR
    gst_stream_error_quark ()
有了表示
4
error
的身份证了。
 
 
3
定义一个做字符串数组的宏
 
#define TABLE(t, d, a, b) t[GST_ ## d ## _ERROR_ ## a] = g_strdup (b)
这里面
t 是字符串数组的名字;
d 是domain,上述枚举量的中间部分(
蓝色标出);
a 是上述枚举量的后面半截(
红色标出);
b 是错误字符串,这里可以给出详细的信息;
 
4 把上面定义的4类枚举变量对应的错误信息做4个字符串数组
 
4-1: core方面的详细错误信息
 
 
static gchar **
_gst_core_errors_init (void)
{
 gchar **t = NULL;
 
 t = g_new0 (gchar *, GST_CORE_ERROR_NUM_ERRORS);
 
 TABLE (t, CORE, FAILED,
      N_("
GStreamer encountered a general core library error."));
 TABLE (t, CORE, TOO_LAZY,
      N_(
"GStreamer developers were too lazy to assign an error code "
          "to this error." FILE_A_BUG));
 TABLE (t, CORE, NOT_IMPLEMENTED,
      N_("
Internal GStreamer error: code not implemented." FILE_A_BUG));
 TABLE (t, CORE, STATE_CHANGE,
      N_("
Internal GStreamer error: state change failed." FILE_A_BUG));
 TABLE (t, CORE, PAD, N_("
Internal GStreamer error: pad problem." FILE_A_BUG));
 TABLE (t, CORE, THREAD,
      N_("
Internal GStreamer error: thread problem." FILE_A_BUG));
 TABLE (t, CORE, NEGOTIATION,
      N_("
Internal GStreamer error: negotiation problem." FILE_A_BUG));
 TABLE (t, CORE, EVENT,
      N_("
Internal GStreamer error: event problem." FILE_A_BUG));
 TABLE (t, CORE, SEEK,
      N_("
Internal GStreamer error: seek problem." FILE_A_BUG));
 TABLE (t, CORE, CAPS,
      N_("
Internal GStreamer error: caps problem." FILE_A_BUG));
 TABLE (t, CORE, TAG, N_("
Internal GStreamer error: tag problem." FILE_A_BUG));
 TABLE (t, CORE, MISSING_PLUGIN,
      N_("
Your GStreamer installation is missing a plug-in."));
 TABLE (t, CORE, CLOCK,
      N_("
Internal GStreamer error: clock problem." FILE_A_BUG));
 
 return t;
}
 
//上面的函数的执行后的结果
gchar **gst_core_errors = _gst_core_errors_init ();
 
gst_core_errors [GST_
CORE_ERROR_FAILED] = "GStreamer encountered a general core library error."
gst_core_errors [GST_
CORE_ERROR_TOO_LAZY] = "GStreamer developers were too lazy to assign an error code to this error."
 
//…
 
这样,用户就可以根据error code得出对应的error string了。
 
 
static gchar **
_gst_library_errors_init (void)
{
 // ….
省略
 return t;
}
 
/* initialize the dynamic table of translated resource errors */
static gchar **
_gst_resource_errors_init (void)
{
 
// ….
省略
}
 
static gchar **
_gst_stream_errors_init (void)
{
 
// ….
省略
}
 
具体可以参考gsterror.h/.c
 
这样就有了4个字符串数组。怎么来区分呢,就是用quark和error code:
 
gchar *
gst_error_get_message (GQuark domain, gint code)
{
 static gchar **gst_core_errors = NULL;
 static gchar **gst_library_errors = NULL;
 static gchar **gst_resource_errors = NULL;
 static gchar **gst_stream_errors = NULL;
 
 gchar *message = NULL;
 
 /* initialize error message tables if necessary */
 if (gst_core_errors == NULL)
    gst_core_errors = _gst_core_errors_init ();
 if (gst_library_errors == NULL)
    gst_library_errors = _gst_library_errors_init ();
 if (gst_resource_errors == NULL)
    gst_resource_errors = _gst_resource_errors_init ();
 if (gst_stream_errors == NULL)
    gst_stream_errors = _gst_stream_errors_init ();
 
 
//
根据
身份证
error code ,
提取
error string.
 if (domain ==
GST_CORE_ERROR)
    message = gst_core_errors[code];
 else if (domain ==
GST_LIBRARY_ERROR)
    message = gst_library_errors[code];
 else if (domain ==
GST_RESOURCE_ERROR)
    message = gst_resource_errors[code];
 else if (domain ==
GST_STREAM_ERROR)
    message = gst_stream_errors[code];
 else {
    g_warning ("No error messages for domain %s", g_quark_to_string (domain));
    return g_strdup_printf (_("No error message for domain %s."),
        g_quark_to_string (domain));
 }
 if (message)
    return g_strdup (_(message));
 else
    return
        g_strdup_printf (_
        ("No standard error message for domain %s and code %d."),
        g_quark_to_string (domain), code);
}
 
 
如果开发middleware的话,GStreamer的error code的做法就非常值得参考;如果做application可以直接return error code, 或者也这么做,好像application很少有人这么干过。
 
 
D) dbus
的做法
 
==================dbus
error code
做法
=============
 
Dbus
error code
的做法有另外一种风格,可以接收
可变参数
 
1
定义一个数据结构
DBusError
 
typedef struct DBusError DBusError;
 
/**
 * Object representing an exception.
 */
struct DBusError
{
 const char *name;    /**< error name */ //
主要的
 const char *message; /**< error message */ //
主要的
 
 unsigned int dummy1 : 1; /**< placeholder */
 unsigned int dummy2 : 1; /**< placeholder */
 unsigned int dummy3 : 1; /**< placeholder */
 unsigned int dummy4 : 1; /**< placeholder */
 unsigned int dummy5 : 1; /**< placeholder */
 
 void *padding1; /**< placeholder */
};
 
2
然后
dbus
提供几个函数对这个数据结构进行分配内存、赋值、释放等的操作;
 
//
分配内存
void        dbus_error_init      (DBusError       *error);
 
//
释放
void         dbus_error_free      (DBusError       *error);
 
//
这是
dbus
的特色菜肴,能灵活的对
DBusError
填充想填充的内容,不必事先定死
void         dbus_set_error       (DBusError       *error,
                                  const char      *name,
                                  const char      *message,
                                  ...);
//
这个是上面那个函数的简化版本
void         dbus_set_error_const (DBusError       *error,
                                  const char      *name,
                                  const char      *message);
 
void         dbus_move_error      (DBusError       *src,
                                  DBusError       *dest);
//
查询之用
dbus_bool_t dbus_error_has_name (const DBusError *error,
                                  const char      *name);
dbus_bool_t dbus_error_is_set     (const DBusError *error);
 
具体实现,大家可以参考
dbus-error.c
 
如何使用上面的
Error
呢?分为外部和内部使用
1
先看
dbus
内部如何使用的:
1-1
定义一些常见的
error string,
这是准备填充到
DBusError name
字段上去的;
 
#define DBUS_ERROR_FAILED "org.freedesktop.DBus.Error.Failed"
#define DBUS_ERROR_NO_MEMORY "org.freedesktop.DBus.Error.NoMemory"
//…….,
参考
dbus-protocol.h
#define DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"
 
1-2
初始化一个
DBusError
,并且填充错误信息给它
 
DBusError error;
dbus_error_init (&error);
 
比如说
dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
 
或者:
//
这个就是往
DBusError
message
字段填充可变参数,这样信息可以灵活点
        dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_SIGNALED,
                        "Process %s received signal %d",
                        sitter->executable, WTERMSIG (sitter->status));
 
2 外部如果调用dbus的API,并且该API有个参数DBusError *error的话,可以这样调用
 
//同样也是初始化一个error
DBusError error;
dbus_error_init (&error);
 
//
然后调用
dbus API
If (!dbus_message_get_args (msg, &error, …)
{
    printf (“Can’t get arguments: %s”, error.message);
    dbus_error_free (&error); //
别忘记释放
}
 
大家可以看到,外部调用非常简单,其实我在这里聊dbus的error code,主要是想借鉴dbus内部调用的方式,以及DBusError的实现方式;
我们知道,dbus一般是作为middleware一层的代表,这种灵活多变的error code的做法也是继承自Glib的。在middleware一层,可以多考虑这种做法,比较专业,能传递更多的信息。
总结:
 
比较而言:
1 GconfError就是GError的升级版本;
2 GStreamer的Error分类思想很好,特别对于提供复杂功能的middleware,可以借鉴;
3 DBusError的可变填充比较好;
4 Sqlite 完全就是个一对一的关系,比较有特色的就是可以提取“最新”的错误,很有用;
 
 

转载地址:http://oitgb.baihongyu.com/

你可能感兴趣的文章
利用正则表达式群发定制邮件
查看>>
【原】RDD专题
查看>>
第三周——构建一个简单的Linux系统MenuOS
查看>>
Docker 的两类存储资源 - 每天5分钟玩转 Docker 容器技术(38)
查看>>
Codeforces 257D
查看>>
常用的20个强大的 Sublime Text 插件
查看>>
ajaxfileupload.js在IE中的支持问题
查看>>
tensorflow学习之(十)使用卷积神经网络(CNN)分类手写数字0-9
查看>>
当document.write里含有script标签时
查看>>
工作中常见问题
查看>>
JAVA 从一个List里删除包含另一个List的数据
查看>>
外国的月亮比较圆吗?外籍团队工作有感
查看>>
CentOS 关闭烦人的屏保
查看>>
分布式系统事务一致性解决方案
查看>>
ShuffleNet总结
查看>>
前后台验证字符串长度
查看>>
《算法导论 - 思考题》7-1 Hoare划分的正确性
查看>>
UVa 10491 奶牛和轿车(全概率公式)
查看>>
[Hadoop]-HDFS-架构篇
查看>>
Metronic-最优秀的基于Bootstrap的响应式网站模版
查看>>