Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RMagick, migrate to ImageMagick 7 #RubyKaigi #RubyKaigi2019

Watson
April 18, 2019

RMagick, migrate to ImageMagick 7 #RubyKaigi #RubyKaigi2019

Watson

April 18, 2019
Tweet

More Decks by Watson

Other Decks in Programming

Transcript

  1. RMagick,
    migrate to ImageMagick 7
    Shizuo Fujita

    View Slide

  2. self.inspect
    • @watson1978

    • Ubiregi, Inc

    • Ruby committer

    • RMagick maintainer

    View Slide

  3. What's RMagick

    View Slide

  4. "RMagick is an interface between the
    Ruby programming language and the
    ImageMagick image processing
    library."

    View Slide

  5. Active members
    ˏmockdeep
    ˏwatson1978 ˏdlemstra

    View Slide

  6. My Motivation

    View Slide

  7. I want to use Ruby ...
    •Web programming

    View Slide

  8. I want to use Ruby ...
    •Web programming

    •iOS programming

    View Slide

  9. I want to use Ruby ...
    •Web programming

    •iOS programming

    •Image processing

    View Slide

  10. I want to use Ruby ...
    •Web programming

    •iOS programming

    •Image processing

    •Machine learning, etc

    View Slide

  11. I want to use Ruby ...
    •Web programming

    •iOS programming

    •Image processing

    •Machine learning, etc
    To meet crazy
    great people
    at Ruby World

    View Slide

  12. Current Status of RMagick

    View Slide

  13. View Slide

  14. Fixed major problems
    since v2.16.0
    •Set up

    •Memory leaks

    •SEGVs

    View Slide

  15. Set up problems

    View Slide

  16. Set up problems
    •macOS (Homebrew)

    •Windows

    View Slide

  17. macOS (Homebrew)

    View Slide

  18. macOS (Homebrew)

    View Slide

  19. Windows
    •Ruby has some environments

    • MingW

    • MSWin

    • Cygwin

    • Windows Subsystem for Linux

    View Slide

  20. Windows
    •Ruby has some environments

    • MingW

    • MSWin

    • Cygwin

    • Windows Subsystem for Linux

    View Slide

  21. MingW...?

    View Slide

  22. Set up : MingW

    View Slide

  23. Solution
    https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers

    View Slide

  24. MingW works

    View Slide

  25. MSWin...?

    View Slide

  26. Set up : MSWin

    View Slide

  27. MSWin supported...?
    Looks like MSWin
    supporting code...

    View Slide

  28. Not supported...
    Why !?
    For MSWin

    View Slide

  29. MSWin works well

    View Slide

  30. Memory leak

    View Slide

  31. Fixed many memory leaks
    55 Closed

    View Slide

  32. Case 1
    VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }

    View Slide

  33. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory

    View Slide

  34. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory
    Destroy memory

    View Slide

  35. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory
    Destroy memory
    Raise exception with
    unexpected value

    View Slide

  36. VALUE
    Image_shear(VALUE self, VALUE x_shear, VALUE y_shear)
    {
    Image *image, *new_image;
    ExceptionInfo *exception;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    new_image = ShearImage(image, NUM2DBL(x_shear), NUM2DBL(y_shear), exception);
    rm_check_exception(exception, new_image, DestroyOnError);
    (void) DestroyExceptionInfo(exception);
    rm_ensure_result(new_image);
    return rm_image_new(new_image);
    }
    Case 1
    Allocate memory
    Destroy memory
    Raise exception with
    unexpected value
    Unreachable by
    exception

    View Slide

  37. Case 1: Exception causes Memory Leak
    image =
    Magick::Image.read('sample.png').first
    begin
    image.shear('x', 'y')
    rescue
    end

    View Slide

  38. Case 2
    static VALUE
    has_attribute(VALUE self, MagickBooleanType....[snip])
    {
    Image *image;
    ExceptionInfo *exception;
    MagickBooleanType r;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    r = (attr_test)(image, exception);
    CHECK_EXCEPTION()
    return r ? Qtrue : Qfalse;
    }

    View Slide

  39. static VALUE
    has_attribute(VALUE self, MagickBooleanType....[snip])
    {
    Image *image;
    ExceptionInfo *exception;
    MagickBooleanType r;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    r = (attr_test)(image, exception);
    CHECK_EXCEPTION()
    return r ? Qtrue : Qfalse;
    }
    Case 2
    Allocate memory

    View Slide

  40. static VALUE
    has_attribute(VALUE self, MagickBooleanType....[snip])
    {
    Image *image;
    ExceptionInfo *exception;
    MagickBooleanType r;
    image = rm_check_destroyed(self);
    exception = AcquireExceptionInfo();
    r = (attr_test)(image, exception);
    CHECK_EXCEPTION()
    return r ? Qtrue : Qfalse;
    }
    Case 2
    Allocate memory
    Missing destroy

    View Slide

  41. Case 2: Missing destroy lead to
    Memory Leak
    image =
    Magick::Image.read('sample.png').first
    image.gray?

    View Slide

  42. Case 3
    Unnecessary clone
    API returns NEW image
    in allocated memory

    View Slide

  43. Case 3: Memory Leak
    by incorrect API usage
    image =
    Magick::Image.read('sample.png').first
    image.sharpen_channel

    View Slide

  44. Case 4

    View Slide

  45. Case 4 Found
    memory leak in
    ImageMagick

    View Slide

  46. Case 4

    View Slide

  47. Case 4: Huge software has
    Memory Leak

    View Slide

  48. How to detect memory leak

    View Slide

  49. Profiler tool

    View Slide

  50. Profiler tool
    Memory leak
    detected

    View Slide

  51. Profiler tool

    View Slide

  52. Profiler tool...
    •Detects at executed code only

    •unit-test

    •RSpec

    •Needs better coverage...

    View Slide

  53. Code reading...
    •To find same pattern that profiler detects

    View Slide

  54. SEGV

    View Slide

  55. Closed 5 SEGVs
    4 Fixed
    1 Workaround

    View Slide

  56. Workaround Case
    Help wanted

    View Slide

  57. Workaround Case
    •Call back image processing progress to
    Ruby
    User's
    Ruby code
    RMagick ImageMagick
    Callback

    View Slide

  58. ruby_stack_length() shows
    51119378542 = 47 GB
    • Invalid stack length
    • GC running
    →SEGV

    View Slide

  59. RMagick is better than ever.

    View Slide

  60. Future...

    View Slide

  61. ImageMagick 7 support

    View Slide

  62. Preparing draft code... now

    View Slide

  63. Plan 1: Split
    RMagick ver 4.x
    RMagick ver 3.x
    ver 3.0 ver 3.1
    ver 2.16.1
    ImageMagick 7
    ImageMagick 6

    View Slide

  64. Plan 2 : Mix both
    ImageMagick 7
    ImageMagick 7
    ImageMagick 6
    Using many #if defined
    ...

    View Slide

  65. Plan 3: Bundle both
    ImageMagick 7
    ImageMagick 6

    View Slide

  66. We welcome good ideas for
    the future

    View Slide

  67. Announcement
    •RubyData Workshop (Tomorrow, 14:20 ~)

    •RubyKaigi 2019 Code Party (Tomorrow, 19:00 ~)

    View Slide

  68. Thank you

    View Slide