问题

欲测试cairo DRM后端的性能,上次解决完radeon显卡drm backend中create_context函数钩子错位问题后,重新编译运行,发现继续崩溃。

这次直接core,没有提示信息,查看搜集到的core文件,现象如下:

Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000000fff19dc3f0 in _cairo_drm_surface_get_extents () from /lib64/libcairo.so.2
(gdb) bt
#0  0x000000fff19dc3f0 in _cairo_drm_surface_get_extents () from /lib64/libcairo.so.2
#1  0x000000fff19a64f0 in _cairo_surface_paint () from /lib64/libcairo.so.2
#2  0x000000fff19846a0 in _cairo_gstate_paint () from /lib64/libcairo.so.2
#3  0x000000fff19420b0 in cairo_paint () from /lib64/libcairo.so.2
#4  0x0000000120007264 in fill_surface (surface=<optimized out>)
    at cairo-perf-trace.c:180
#5  cairo_perf_trace.lto_priv.89 (perf=0xfffff94030, 
    target=0x120024670 <targets.lto_priv>, 
    trace=0xfffff973ee "../trimmed-cairo-traces-master/benchmark/t-gnome-terminal-vim")
    at cairo-perf-trace.c:719
#6  0x0000000120004df4 in main (argc=<optimized out>, argv=<optimized out>)
    at cairo-perf-trace.c:1032

还是在fill_surface()函数中出了问题,这次是段错误。

分析

现场分析

还是先从故障现场开始分析:

(gdb) disassemble 
Dump of assembler code for function _cairo_drm_surface_get_extents:
=> 0x000000fff19dc3f0 <+0>:	sw	zero,0(a1)
   0x000000fff19dc3f4 <+4>:	li	v0,1
   0x000000fff19dc3f8 <+8>:	sw	zero,4(a1)
   0x000000fff19dc3fc <+12>:	lw	v1,332(a0)
   0x000000fff19dc400 <+16>:	sw	v1,8(a1)
   0x000000fff19dc404 <+20>:	lw	v1,336(a0)
   0x000000fff19dc408 <+24>:	jr	ra
   0x000000fff19dc40c <+28>:	sw	v1,12(a1)
End of assembler dump.

可以看出是a1寄存器的值不对导致段错误,查看寄存器信息:

(gdb) info registers 
                  zero               at               v0               v1
 R0   0000000000000000 0000000000000001 000000fff1a77e88 000000fff1a50e60 
                    a0               a1               a2               a3
 R4   000000012047af80 0000000000000002 000000fffff93930 0000000000000000 
                    a4               a5               a6               a7
 R8   0000000000000000 0000000000000000 3ff0000000000000 3fe0000000000000 
                    t0               t1               t2               t3
 R12  3ff0000000000000 0000000000000000 0000000000000000 8000400040004000 
                    s0               s1               s2               s3
 R16  000000012047af80 0000000000000000 000000fffff93930 0000000000000002 
                    s4               s5               s6               s7
 R20  0000000000000000 0000000000000000 000000fffff94030 0000000120020000 
                    t8               t9               k0               k1
 R24  0000000000000020 000000fff19dc3f0 0000000000000000 0000000000000000 
                    gp               sp               s8               ra
 R28  000000fff1a81840 000000fffff938f0 0000000120024670 000000fff19a64f0 
---Type <return> to continue, or q <return> to quit---q

可以看出a1寄存器值为0000000000000002,显然作为指针访问的话,肯定会段错误。那这个值是如何来的呢?还是得分析代码流程。

代码分析

分析调用流程,发现_cairo_surface_paint()函数还是不太可能调用到_cairo_drm_surface_get_extents()函数中去,两者的功能都不同。

于是还是怀疑是跟之前类似的问题,再次确认radeon_surface_backend结构体的内容:

static const cairo_surface_backend_t radeon_surface_backend = {
    CAIRO_SURFACE_TYPE_DRM,
    radeon_surface_finish,
    _cairo_default_context_create,

    radeon_surface_create_similar,

    NULL,
    radeon_surface_acquire_source_image,
    radeon_surface_release_source_image,

    NULL, NULL, NULL,
    NULL, /* composite */
    NULL, /* fill */
    NULL, /* trapezoids */
    NULL, /* span */
    NULL, /* check-span */

    NULL, /* copy_page */
    NULL, /* show_page */
    _cairo_drm_surface_get_extents,
    NULL, /* old-glyphs */
    _cairo_drm_surface_get_font_options,

    radeon_surface_flush,
    NULL, /* mark dirty */
    NULL, NULL, /* font/glyph fini */

    radeon_surface_paint,
    radeon_surface_mask,
    radeon_surface_stroke,
    radeon_surface_fill,
    radeon_surface_glyphs,
};

数一下,_cairo_drm_surface_get_extents接口在该结构第18个字段,

再看看cairo_surface_backend_t的定义:

struct _cairo_surface_backend {
    cairo_surface_type_t type;

    cairo_warn cairo_status_t
    (*finish)			(void			*surface);

    cairo_t *
    (*create_context)		(void			*surface);

    cairo_surface_t *
    (*create_similar)		(void			*surface,
				 cairo_content_t	 content,
				 int			 width,
				 int			 height);
    cairo_surface_t *
    (*create_similar_image)	(void			*surface,
				 cairo_format_t		format,
				 int			 width,
				 int			 height);

    cairo_image_surface_t *
    (*map_to_image)		(void			*surface,
				 const cairo_rectangle_int_t  *extents);
    cairo_int_status_t
    (*unmap_image)		(void			*surface,
				 cairo_image_surface_t	*image);

    cairo_surface_t *
    (*source)			(void                    *abstract_surface,
				 cairo_rectangle_int_t  *extents);

    cairo_warn cairo_status_t
    (*acquire_source_image)	(void                    *abstract_surface,
				 cairo_image_surface_t  **image_out,
				 void                   **image_extra);

    cairo_warn void
    (*release_source_image)	(void                   *abstract_surface,
				 cairo_image_surface_t  *image_out,
				 void                   *image_extra);

    cairo_surface_t *
    (*snapshot)			(void			*surface);

    cairo_warn cairo_int_status_t
    (*copy_page)		(void			*surface);

    cairo_warn cairo_int_status_t
    (*show_page)		(void			*surface);

    /* Get the extents of the current surface. For many surface types
     * this will be as simple as { x=0, y=0, width=surface->width,
     * height=surface->height}.
     *
     * If this function is not implemented, or if it returns
     * FALSE the surface is considered to be
     * boundless and infinite bounds are used for it.
     */
    cairo_bool_t
    (*get_extents)		(void			 *surface,
				 cairo_rectangle_int_t   *extents);

    void
    (*get_font_options)         (void                  *surface,
				 cairo_font_options_t  *options);

    cairo_warn cairo_status_t
    (*flush)                    (void                  *surface,
				 unsigned               flags);

    cairo_warn cairo_status_t
    (*mark_dirty_rectangle)     (void                  *surface,
				 int                    x,
				 int                    y,
				 int                    width,
				 int                    height);

    cairo_warn cairo_int_status_t
    (*paint)			(void			*surface,
				 cairo_operator_t	 op,
				 const cairo_pattern_t	*source,
				 const cairo_clip_t		*clip);

    cairo_warn cairo_int_status_t
    (*mask)			(void			*surface,
				 cairo_operator_t	 op,
				 const cairo_pattern_t	*source,
				 const cairo_pattern_t	*mask,
				 const cairo_clip_t		*clip);

    cairo_warn cairo_int_status_t
    (*stroke)			(void			*surface,
				 cairo_operator_t	 op,
				 const cairo_pattern_t	*source,
				 const cairo_path_fixed_t	*path,
				 const cairo_stroke_style_t	*style,
				 const cairo_matrix_t	*ctm,
				 const cairo_matrix_t	*ctm_inverse,
				 double			 tolerance,
				 cairo_antialias_t	 antialias,
				 const cairo_clip_t		*clip);

    cairo_warn cairo_int_status_t
    (*fill)			(void			*surface,
				 cairo_operator_t	 op,
				 const cairo_pattern_t	*source,
				 const cairo_path_fixed_t	*path,
				 cairo_fill_rule_t	 fill_rule,
				 double			 tolerance,
				 cairo_antialias_t	 antialias,
				 const cairo_clip_t           *clip);

    cairo_warn cairo_int_status_t
    (*fill_stroke)		(void			*surface,
				 cairo_operator_t	 fill_op,
				 const cairo_pattern_t	*fill_source,
				 cairo_fill_rule_t	 fill_rule,
				 double			 fill_tolerance,
				 cairo_antialias_t	 fill_antialias,
				 const cairo_path_fixed_t*path,
				 cairo_operator_t	 stroke_op,
				 const cairo_pattern_t	*stroke_source,
				 const cairo_stroke_style_t	*stroke_style,
				 const cairo_matrix_t	*stroke_ctm,
				 const cairo_matrix_t	*stroke_ctm_inverse,
				 double			 stroke_tolerance,
				 cairo_antialias_t	 stroke_antialias,
				 const cairo_clip_t	*clip);

    cairo_warn cairo_int_status_t
    (*show_glyphs)		(void			*surface,
				 cairo_operator_t	 op,
				 const cairo_pattern_t	*source,
				 cairo_glyph_t		*glyphs,
				 int			 num_glyphs,
				 cairo_scaled_font_t	*scaled_font,
				 const cairo_clip_t     *clip);

    cairo_bool_t
    (*has_show_text_glyphs)	(void			    *surface);

    cairo_warn cairo_int_status_t
    (*show_text_glyphs)		(void			    *surface,
				 cairo_operator_t	     op,
				 const cairo_pattern_t	    *source,
				 const char		    *utf8,
				 int			     utf8_len,
				 cairo_glyph_t		    *glyphs,
				 int			     num_glyphs,
				 const cairo_text_cluster_t *clusters,
				 int			     num_clusters,
				 cairo_text_cluster_flags_t  cluster_flags,
				 cairo_scaled_font_t	    *scaled_font,
				 const cairo_clip_t               *clip);

    const char **
    (*get_supported_mime_types)	(void			    *surface);
};

数一下,该结构体的第18个字段的钩子为paint接口,这才是应该调用的接口,也就是说这个两个结构体又错位了,之前值修改了create_context接口,没有修改彻底,这次需要彻底核对一下了,核对后发现其中很多的钩子都没对上位,全部重新调整后解决。