Implement `dumpInfo` for .fp files.

Change-Id: I40f6c1a02e194f090e67a0e3f2d7d83cd2317efd
Bug: skia:8434
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/309139
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/gn/core.gni b/gn/core.gni
index 7328e36..4c25c19 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -49,6 +49,7 @@
   "$_include/core/SkImageFilter.h",
   "$_include/core/SkImageGenerator.h",
   "$_include/core/SkImageInfo.h",
+  "$_include/core/SkM44.h",
   "$_include/core/SkMallocPixelRef.h",
   "$_include/core/SkMaskFilter.h",
   "$_include/core/SkMath.h",
diff --git a/src/gpu/effects/generated/GrAARectEffect.cpp b/src/gpu/effects/generated/GrAARectEffect.cpp
index 65be82a..40c12ba 100644
--- a/src/gpu/effects/generated/GrAARectEffect.cpp
+++ b/src/gpu/effects/generated/GrAARectEffect.cpp
@@ -110,6 +110,12 @@
 std::unique_ptr<GrFragmentProcessor> GrAARectEffect::clone() const {
     return std::make_unique<GrAARectEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrAARectEffect::dumpInfo() const {
+    return SkStringPrintf("AARectEffect(edgeType=%d, rect=float4(%f, %f, %f, %f))", (int)edgeType,
+                          rect.left(), rect.top(), rect.right(), rect.bottom());
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrAARectEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrAARectEffect::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrAARectEffect.h b/src/gpu/effects/generated/GrAARectEffect.h
index 04bb662..e8c0af8 100644
--- a/src/gpu/effects/generated/GrAARectEffect.h
+++ b/src/gpu/effects/generated/GrAARectEffect.h
@@ -25,6 +25,9 @@
                 new GrAARectEffect(std::move(inputFP), edgeType, rect));
     }
     GrAARectEffect(const GrAARectEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "AARectEffect"; }
     GrClipEdgeType edgeType;
diff --git a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp
index 74bdabe..04870f7 100644
--- a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.cpp
@@ -96,6 +96,12 @@
 std::unique_ptr<GrFragmentProcessor> GrAlphaThresholdFragmentProcessor::clone() const {
     return std::make_unique<GrAlphaThresholdFragmentProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrAlphaThresholdFragmentProcessor::dumpInfo() const {
+    return SkStringPrintf("AlphaThresholdFragmentProcessor(innerThreshold=%f, outerThreshold=%f)",
+                          innerThreshold, outerThreshold);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrAlphaThresholdFragmentProcessor);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrAlphaThresholdFragmentProcessor::TestCreate(
diff --git a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h
index f90243b..9a5669d 100644
--- a/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrAlphaThresholdFragmentProcessor.h
@@ -26,6 +26,9 @@
                 std::move(inputFP), std::move(maskFP), innerThreshold, outerThreshold));
     }
     GrAlphaThresholdFragmentProcessor(const GrAlphaThresholdFragmentProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "AlphaThresholdFragmentProcessor"; }
     float innerThreshold;
diff --git a/src/gpu/effects/generated/GrArithmeticProcessor.cpp b/src/gpu/effects/generated/GrArithmeticProcessor.cpp
index 29a57f6..5bf0796 100644
--- a/src/gpu/effects/generated/GrArithmeticProcessor.cpp
+++ b/src/gpu/effects/generated/GrArithmeticProcessor.cpp
@@ -81,6 +81,12 @@
 std::unique_ptr<GrFragmentProcessor> GrArithmeticProcessor::clone() const {
     return std::make_unique<GrArithmeticProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrArithmeticProcessor::dumpInfo() const {
+    return SkStringPrintf("ArithmeticProcessor(k=float4(%f, %f, %f, %f), enforcePMColor=%s)", k.x,
+                          k.y, k.z, k.w, (enforcePMColor ? "true" : "false"));
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrArithmeticProcessor);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrArithmeticProcessor::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrArithmeticProcessor.h b/src/gpu/effects/generated/GrArithmeticProcessor.h
index 0a8b9f6..2969e48 100644
--- a/src/gpu/effects/generated/GrArithmeticProcessor.h
+++ b/src/gpu/effects/generated/GrArithmeticProcessor.h
@@ -29,6 +29,9 @@
                 inputs.fEnforcePMColor));
     }
     GrArithmeticProcessor(const GrArithmeticProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "ArithmeticProcessor"; }
     SkV4 k;
diff --git a/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.cpp b/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.cpp
index ff9bb22..a5afc99 100644
--- a/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.cpp
@@ -70,3 +70,8 @@
 std::unique_ptr<GrFragmentProcessor> GrBlurredEdgeFragmentProcessor::clone() const {
     return std::make_unique<GrBlurredEdgeFragmentProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrBlurredEdgeFragmentProcessor::dumpInfo() const {
+    return SkStringPrintf("BlurredEdgeFragmentProcessor(mode=%d)", (int)mode);
+}
+#endif
diff --git a/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.h b/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.h
index e2e5f64..294244b 100644
--- a/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrBlurredEdgeFragmentProcessor.h
@@ -25,6 +25,9 @@
                 new GrBlurredEdgeFragmentProcessor(std::move(inputFP), mode));
     }
     GrBlurredEdgeFragmentProcessor(const GrBlurredEdgeFragmentProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "BlurredEdgeFragmentProcessor"; }
     Mode mode;
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
index d6c1d6f..7a221bf 100644
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
@@ -360,6 +360,15 @@
 std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::clone() const {
     return std::make_unique<GrCircleBlurFragmentProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrCircleBlurFragmentProcessor::dumpInfo() const {
+    return SkStringPrintf(
+            "CircleBlurFragmentProcessor(circleRect=half4(%f, %f, %f, %f), solidRadius=%f, "
+            "textureRadius=%f)",
+            circleRect.left(), circleRect.top(), circleRect.right(), circleRect.bottom(),
+            solidRadius, textureRadius);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleBlurFragmentProcessor);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::TestCreate(
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
index bba26d0..fae3fd1 100644
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
@@ -25,6 +25,9 @@
                                                      const SkRect& circle,
                                                      float sigma);
     GrCircleBlurFragmentProcessor(const GrCircleBlurFragmentProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "CircleBlurFragmentProcessor"; }
     SkRect circleRect;
diff --git a/src/gpu/effects/generated/GrCircleEffect.cpp b/src/gpu/effects/generated/GrCircleEffect.cpp
index 3871b73..36dbe29 100644
--- a/src/gpu/effects/generated/GrCircleEffect.cpp
+++ b/src/gpu/effects/generated/GrCircleEffect.cpp
@@ -121,6 +121,12 @@
 std::unique_ptr<GrFragmentProcessor> GrCircleEffect::clone() const {
     return std::make_unique<GrCircleEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrCircleEffect::dumpInfo() const {
+    return SkStringPrintf("CircleEffect(edgeType=%d, center=float2(%f, %f), radius=%f)",
+                          (int)edgeType, center.fX, center.fY, radius);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrCircleEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrCircleEffect::TestCreate(GrProcessorTestData* testData) {
diff --git a/src/gpu/effects/generated/GrCircleEffect.h b/src/gpu/effects/generated/GrCircleEffect.h
index 4d529d9..375452b 100644
--- a/src/gpu/effects/generated/GrCircleEffect.h
+++ b/src/gpu/effects/generated/GrCircleEffect.h
@@ -31,6 +31,9 @@
                 new GrCircleEffect(std::move(inputFP), edgeType, center, radius)));
     }
     GrCircleEffect(const GrCircleEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "CircleEffect"; }
     GrClipEdgeType edgeType;
diff --git a/src/gpu/effects/generated/GrClampFragmentProcessor.cpp b/src/gpu/effects/generated/GrClampFragmentProcessor.cpp
index 182a2a0..8882860 100644
--- a/src/gpu/effects/generated/GrClampFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrClampFragmentProcessor.cpp
@@ -65,6 +65,12 @@
 std::unique_ptr<GrFragmentProcessor> GrClampFragmentProcessor::clone() const {
     return std::make_unique<GrClampFragmentProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrClampFragmentProcessor::dumpInfo() const {
+    return SkStringPrintf("ClampFragmentProcessor(clampToPremul=%s)",
+                          (clampToPremul ? "true" : "false"));
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrClampFragmentProcessor);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrClampFragmentProcessor::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrClampFragmentProcessor.h b/src/gpu/effects/generated/GrClampFragmentProcessor.h
index fa95acc..6a2b25a 100644
--- a/src/gpu/effects/generated/GrClampFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrClampFragmentProcessor.h
@@ -31,6 +31,9 @@
                 new GrClampFragmentProcessor(std::move(inputFP), clampToPremul));
     }
     GrClampFragmentProcessor(const GrClampFragmentProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "ClampFragmentProcessor"; }
     bool clampToPremul;
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
index 2a75692..765f07a 100644
--- a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.cpp
@@ -123,6 +123,18 @@
 std::unique_ptr<GrFragmentProcessor> GrColorMatrixFragmentProcessor::clone() const {
     return std::make_unique<GrColorMatrixFragmentProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrColorMatrixFragmentProcessor::dumpInfo() const {
+    return SkStringPrintf(
+            "ColorMatrixFragmentProcessor(m=half4x4(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, "
+            "%f, %f, %f, %f, %f), v=half4(%f, %f, %f, %f), unpremulInput=%s, clampRGBOutput=%s, "
+            "premulOutput=%s)",
+            m.rc(0, 0), m.rc(1, 0), m.rc(2, 0), m.rc(3, 0), m.rc(0, 1), m.rc(1, 1), m.rc(2, 1),
+            m.rc(3, 1), m.rc(0, 2), m.rc(1, 2), m.rc(2, 2), m.rc(3, 2), m.rc(0, 3), m.rc(1, 3),
+            m.rc(2, 3), m.rc(3, 3), v.x, v.y, v.z, v.w, (unpremulInput ? "true" : "false"),
+            (clampRGBOutput ? "true" : "false"), (premulOutput ? "true" : "false"));
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrColorMatrixFragmentProcessor);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrColorMatrixFragmentProcessor::TestCreate(
diff --git a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h
index 498fc4d..1fcb12d 100644
--- a/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrColorMatrixFragmentProcessor.h
@@ -57,6 +57,9 @@
                 std::move(inputFP), m44, v4, unpremulInput, clampRGBOutput, premulOutput));
     }
     GrColorMatrixFragmentProcessor(const GrColorMatrixFragmentProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "ColorMatrixFragmentProcessor"; }
     SkM44 m;
diff --git a/src/gpu/effects/generated/GrComposeLerpEffect.cpp b/src/gpu/effects/generated/GrComposeLerpEffect.cpp
index 2b6b610..9155fa6 100644
--- a/src/gpu/effects/generated/GrComposeLerpEffect.cpp
+++ b/src/gpu/effects/generated/GrComposeLerpEffect.cpp
@@ -63,3 +63,8 @@
 std::unique_ptr<GrFragmentProcessor> GrComposeLerpEffect::clone() const {
     return std::make_unique<GrComposeLerpEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrComposeLerpEffect::dumpInfo() const {
+    return SkStringPrintf("ComposeLerpEffect(weight=%f)", weight);
+}
+#endif
diff --git a/src/gpu/effects/generated/GrComposeLerpEffect.h b/src/gpu/effects/generated/GrComposeLerpEffect.h
index 716ba36..46104a1 100644
--- a/src/gpu/effects/generated/GrComposeLerpEffect.h
+++ b/src/gpu/effects/generated/GrComposeLerpEffect.h
@@ -25,6 +25,9 @@
                 new GrComposeLerpEffect(std::move(child1), std::move(child2), weight));
     }
     GrComposeLerpEffect(const GrComposeLerpEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "ComposeLerpEffect"; }
     float weight;
diff --git a/src/gpu/effects/generated/GrConfigConversionEffect.cpp b/src/gpu/effects/generated/GrConfigConversionEffect.cpp
index 4e38451..a495915 100644
--- a/src/gpu/effects/generated/GrConfigConversionEffect.cpp
+++ b/src/gpu/effects/generated/GrConfigConversionEffect.cpp
@@ -70,6 +70,11 @@
 std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::clone() const {
     return std::make_unique<GrConfigConversionEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrConfigConversionEffect::dumpInfo() const {
+    return SkStringPrintf("ConfigConversionEffect(pmConversion=%d)", (int)pmConversion);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrConfigConversionEffect::TestCreate(
diff --git a/src/gpu/effects/generated/GrConfigConversionEffect.h b/src/gpu/effects/generated/GrConfigConversionEffect.h
index 14d123c..fe928c1 100644
--- a/src/gpu/effects/generated/GrConfigConversionEffect.h
+++ b/src/gpu/effects/generated/GrConfigConversionEffect.h
@@ -35,6 +35,9 @@
                 new GrConfigConversionEffect(std::move(fp), pmConversion));
     }
     GrConfigConversionEffect(const GrConfigConversionEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "ConfigConversionEffect"; }
     PMConversion pmConversion;
diff --git a/src/gpu/effects/generated/GrConstColorProcessor.cpp b/src/gpu/effects/generated/GrConstColorProcessor.cpp
index 571287a..6061431 100644
--- a/src/gpu/effects/generated/GrConstColorProcessor.cpp
+++ b/src/gpu/effects/generated/GrConstColorProcessor.cpp
@@ -67,6 +67,12 @@
 std::unique_ptr<GrFragmentProcessor> GrConstColorProcessor::clone() const {
     return std::make_unique<GrConstColorProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrConstColorProcessor::dumpInfo() const {
+    return SkStringPrintf("ConstColorProcessor(color=half4(%f, %f, %f, %f))", color.fR, color.fG,
+                          color.fB, color.fA);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConstColorProcessor);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrConstColorProcessor::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrConstColorProcessor.h b/src/gpu/effects/generated/GrConstColorProcessor.h
index f2ffd15..8dfaa2e 100644
--- a/src/gpu/effects/generated/GrConstColorProcessor.h
+++ b/src/gpu/effects/generated/GrConstColorProcessor.h
@@ -25,6 +25,9 @@
         return std::unique_ptr<GrFragmentProcessor>(new GrConstColorProcessor(color));
     }
     GrConstColorProcessor(const GrConstColorProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "ConstColorProcessor"; }
     SkPMColor4f color;
diff --git a/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp b/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp
index fb3e24d..6e06b93 100644
--- a/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp
+++ b/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp
@@ -53,6 +53,9 @@
 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceEffect::clone() const {
     return std::make_unique<GrDeviceSpaceEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrDeviceSpaceEffect::dumpInfo() const { return SkStringPrintf("DeviceSpaceEffect"); }
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDeviceSpaceEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrDeviceSpaceEffect::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrDeviceSpaceEffect.h b/src/gpu/effects/generated/GrDeviceSpaceEffect.h
index 68432b3..4af4858 100644
--- a/src/gpu/effects/generated/GrDeviceSpaceEffect.h
+++ b/src/gpu/effects/generated/GrDeviceSpaceEffect.h
@@ -26,6 +26,9 @@
         return std::unique_ptr<GrFragmentProcessor>(new GrDeviceSpaceEffect(std::move(fp)));
     }
     GrDeviceSpaceEffect(const GrDeviceSpaceEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "DeviceSpaceEffect"; }
 
diff --git a/src/gpu/effects/generated/GrDitherEffect.cpp b/src/gpu/effects/generated/GrDitherEffect.cpp
index 957a57a..a8de91d 100644
--- a/src/gpu/effects/generated/GrDitherEffect.cpp
+++ b/src/gpu/effects/generated/GrDitherEffect.cpp
@@ -75,6 +75,11 @@
 std::unique_ptr<GrFragmentProcessor> GrDitherEffect::clone() const {
     return std::make_unique<GrDitherEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrDitherEffect::dumpInfo() const {
+    return SkStringPrintf("DitherEffect(range=%f)", range);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDitherEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrDitherEffect::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrDitherEffect.h b/src/gpu/effects/generated/GrDitherEffect.h
index 6eac049..7bac4c4 100644
--- a/src/gpu/effects/generated/GrDitherEffect.h
+++ b/src/gpu/effects/generated/GrDitherEffect.h
@@ -26,6 +26,9 @@
         return std::unique_ptr<GrFragmentProcessor>(new GrDitherEffect(std::move(inputFP), range));
     }
     GrDitherEffect(const GrDitherEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "DitherEffect"; }
     float range;
diff --git a/src/gpu/effects/generated/GrEllipseEffect.cpp b/src/gpu/effects/generated/GrEllipseEffect.cpp
index 76ef4b2..1584236 100644
--- a/src/gpu/effects/generated/GrEllipseEffect.cpp
+++ b/src/gpu/effects/generated/GrEllipseEffect.cpp
@@ -161,6 +161,12 @@
 std::unique_ptr<GrFragmentProcessor> GrEllipseEffect::clone() const {
     return std::make_unique<GrEllipseEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrEllipseEffect::dumpInfo() const {
+    return SkStringPrintf("EllipseEffect(edgeType=%d, center=float2(%f, %f), radii=float2(%f, %f))",
+                          (int)edgeType, center.fX, center.fY, radii.fX, radii.fY);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrEllipseEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrEllipseEffect::TestCreate(GrProcessorTestData* testData) {
diff --git a/src/gpu/effects/generated/GrEllipseEffect.h b/src/gpu/effects/generated/GrEllipseEffect.h
index cd63743..436dfa0 100644
--- a/src/gpu/effects/generated/GrEllipseEffect.h
+++ b/src/gpu/effects/generated/GrEllipseEffect.h
@@ -41,6 +41,9 @@
                 new GrEllipseEffect(std::move(inputFP), edgeType, center, radii)));
     }
     GrEllipseEffect(const GrEllipseEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "EllipseEffect"; }
     GrClipEdgeType edgeType;
diff --git a/src/gpu/effects/generated/GrHSLToRGBFilterEffect.cpp b/src/gpu/effects/generated/GrHSLToRGBFilterEffect.cpp
index a933aca..e577782 100644
--- a/src/gpu/effects/generated/GrHSLToRGBFilterEffect.cpp
+++ b/src/gpu/effects/generated/GrHSLToRGBFilterEffect.cpp
@@ -59,3 +59,6 @@
 std::unique_ptr<GrFragmentProcessor> GrHSLToRGBFilterEffect::clone() const {
     return std::make_unique<GrHSLToRGBFilterEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrHSLToRGBFilterEffect::dumpInfo() const { return SkStringPrintf("HSLToRGBFilterEffect"); }
+#endif
diff --git a/src/gpu/effects/generated/GrHSLToRGBFilterEffect.h b/src/gpu/effects/generated/GrHSLToRGBFilterEffect.h
index 9c3d45a..c3f064d 100644
--- a/src/gpu/effects/generated/GrHSLToRGBFilterEffect.h
+++ b/src/gpu/effects/generated/GrHSLToRGBFilterEffect.h
@@ -36,6 +36,9 @@
         return std::unique_ptr<GrFragmentProcessor>(new GrHSLToRGBFilterEffect(std::move(inputFP)));
     }
     GrHSLToRGBFilterEffect(const GrHSLToRGBFilterEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "HSLToRGBFilterEffect"; }
 
diff --git a/src/gpu/effects/generated/GrHighContrastFilterEffect.cpp b/src/gpu/effects/generated/GrHighContrastFilterEffect.cpp
index 817e9ef..254ba94 100644
--- a/src/gpu/effects/generated/GrHighContrastFilterEffect.cpp
+++ b/src/gpu/effects/generated/GrHighContrastFilterEffect.cpp
@@ -163,6 +163,16 @@
 std::unique_ptr<GrFragmentProcessor> GrHighContrastFilterEffect::clone() const {
     return std::make_unique<GrHighContrastFilterEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrHighContrastFilterEffect::dumpInfo() const {
+    return SkStringPrintf(
+            "HighContrastFilterEffect(contrastMod=%f, hasContrast=%s, grayscale=%s, "
+            "invertBrightness=%s, invertLightness=%s, linearize=%s)",
+            contrastMod, (hasContrast ? "true" : "false"), (grayscale ? "true" : "false"),
+            (invertBrightness ? "true" : "false"), (invertLightness ? "true" : "false"),
+            (linearize ? "true" : "false"));
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrHighContrastFilterEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrHighContrastFilterEffect::TestCreate(
diff --git a/src/gpu/effects/generated/GrHighContrastFilterEffect.h b/src/gpu/effects/generated/GrHighContrastFilterEffect.h
index 626f5ef..602441c 100644
--- a/src/gpu/effects/generated/GrHighContrastFilterEffect.h
+++ b/src/gpu/effects/generated/GrHighContrastFilterEffect.h
@@ -35,6 +35,9 @@
                 linearize));
     }
     GrHighContrastFilterEffect(const GrHighContrastFilterEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "HighContrastFilterEffect"; }
     float contrastMod;
diff --git a/src/gpu/effects/generated/GrLumaColorFilterEffect.cpp b/src/gpu/effects/generated/GrLumaColorFilterEffect.cpp
index 3e1350a..ce3717b 100644
--- a/src/gpu/effects/generated/GrLumaColorFilterEffect.cpp
+++ b/src/gpu/effects/generated/GrLumaColorFilterEffect.cpp
@@ -55,3 +55,8 @@
 std::unique_ptr<GrFragmentProcessor> GrLumaColorFilterEffect::clone() const {
     return std::make_unique<GrLumaColorFilterEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrLumaColorFilterEffect::dumpInfo() const {
+    return SkStringPrintf("LumaColorFilterEffect");
+}
+#endif
diff --git a/src/gpu/effects/generated/GrLumaColorFilterEffect.h b/src/gpu/effects/generated/GrLumaColorFilterEffect.h
index 86910b4..e9ed532 100644
--- a/src/gpu/effects/generated/GrLumaColorFilterEffect.h
+++ b/src/gpu/effects/generated/GrLumaColorFilterEffect.h
@@ -31,6 +31,9 @@
                 new GrLumaColorFilterEffect(std::move(inputFP)));
     }
     GrLumaColorFilterEffect(const GrLumaColorFilterEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "LumaColorFilterEffect"; }
 
diff --git a/src/gpu/effects/generated/GrMagnifierEffect.cpp b/src/gpu/effects/generated/GrMagnifierEffect.cpp
index b176d0c..644abc3 100644
--- a/src/gpu/effects/generated/GrMagnifierEffect.cpp
+++ b/src/gpu/effects/generated/GrMagnifierEffect.cpp
@@ -147,6 +147,16 @@
 std::unique_ptr<GrFragmentProcessor> GrMagnifierEffect::clone() const {
     return std::make_unique<GrMagnifierEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrMagnifierEffect::dumpInfo() const {
+    return SkStringPrintf(
+            "MagnifierEffect(bounds=int4(%d, %d, %d, %d), srcRect=float4(%f, %f, %f, %f), "
+            "xInvZoom=%f, yInvZoom=%f, xInvInset=%f, yInvInset=%f)",
+            bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), srcRect.left(),
+            srcRect.top(), srcRect.right(), srcRect.bottom(), xInvZoom, yInvZoom, xInvInset,
+            yInvInset);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMagnifierEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrMagnifierEffect::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrMagnifierEffect.h b/src/gpu/effects/generated/GrMagnifierEffect.h
index 29322b8..441527d 100644
--- a/src/gpu/effects/generated/GrMagnifierEffect.h
+++ b/src/gpu/effects/generated/GrMagnifierEffect.h
@@ -29,6 +29,9 @@
                 std::move(src), bounds, srcRect, xInvZoom, yInvZoom, xInvInset, yInvInset));
     }
     GrMagnifierEffect(const GrMagnifierEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "MagnifierEffect"; }
     SkIRect bounds;
diff --git a/src/gpu/effects/generated/GrMixerEffect.cpp b/src/gpu/effects/generated/GrMixerEffect.cpp
index 2f1c6a5..bf48339 100644
--- a/src/gpu/effects/generated/GrMixerEffect.cpp
+++ b/src/gpu/effects/generated/GrMixerEffect.cpp
@@ -69,3 +69,8 @@
 std::unique_ptr<GrFragmentProcessor> GrMixerEffect::clone() const {
     return std::make_unique<GrMixerEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrMixerEffect::dumpInfo() const {
+    return SkStringPrintf("MixerEffect(weight=%f)", weight);
+}
+#endif
diff --git a/src/gpu/effects/generated/GrMixerEffect.h b/src/gpu/effects/generated/GrMixerEffect.h
index 70e04ae..8e4647b 100644
--- a/src/gpu/effects/generated/GrMixerEffect.h
+++ b/src/gpu/effects/generated/GrMixerEffect.h
@@ -33,6 +33,9 @@
                 new GrMixerEffect(std::move(inputFP), std::move(fp0), std::move(fp1), weight));
     }
     GrMixerEffect(const GrMixerEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "MixerEffect"; }
     float weight;
diff --git a/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp b/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp
index 4b30fce..9867e14 100644
--- a/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.cpp
@@ -103,3 +103,12 @@
 std::unique_ptr<GrFragmentProcessor> GrOverrideInputFragmentProcessor::clone() const {
     return std::make_unique<GrOverrideInputFragmentProcessor>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrOverrideInputFragmentProcessor::dumpInfo() const {
+    return SkStringPrintf(
+            "OverrideInputFragmentProcessor(useUniform=%s, uniformColor=half4(%f, %f, %f, %f), "
+            "literalColor=half4(%f, %f, %f, %f))",
+            (useUniform ? "true" : "false"), uniformColor.fR, uniformColor.fG, uniformColor.fB,
+            uniformColor.fA, literalColor.fR, literalColor.fG, literalColor.fB, literalColor.fA);
+}
+#endif
diff --git a/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.h b/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.h
index e2cd742..3836bd8 100644
--- a/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrOverrideInputFragmentProcessor.h
@@ -42,6 +42,9 @@
                 new GrOverrideInputFragmentProcessor(std::move(fp), useUniform, color, color));
     }
     GrOverrideInputFragmentProcessor(const GrOverrideInputFragmentProcessor& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "OverrideInputFragmentProcessor"; }
     bool useUniform;
diff --git a/src/gpu/effects/generated/GrRGBToHSLFilterEffect.cpp b/src/gpu/effects/generated/GrRGBToHSLFilterEffect.cpp
index f7b3869..989900a 100644
--- a/src/gpu/effects/generated/GrRGBToHSLFilterEffect.cpp
+++ b/src/gpu/effects/generated/GrRGBToHSLFilterEffect.cpp
@@ -62,3 +62,6 @@
 std::unique_ptr<GrFragmentProcessor> GrRGBToHSLFilterEffect::clone() const {
     return std::make_unique<GrRGBToHSLFilterEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrRGBToHSLFilterEffect::dumpInfo() const { return SkStringPrintf("RGBToHSLFilterEffect"); }
+#endif
diff --git a/src/gpu/effects/generated/GrRGBToHSLFilterEffect.h b/src/gpu/effects/generated/GrRGBToHSLFilterEffect.h
index f4efdd9..fe1ef64 100644
--- a/src/gpu/effects/generated/GrRGBToHSLFilterEffect.h
+++ b/src/gpu/effects/generated/GrRGBToHSLFilterEffect.h
@@ -38,6 +38,9 @@
         return std::unique_ptr<GrFragmentProcessor>(new GrRGBToHSLFilterEffect(std::move(inputFP)));
     }
     GrRGBToHSLFilterEffect(const GrRGBToHSLFilterEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "RGBToHSLFilterEffect"; }
 
diff --git a/src/gpu/effects/generated/GrRRectBlurEffect.cpp b/src/gpu/effects/generated/GrRRectBlurEffect.cpp
index 7ae5849..f3336fc 100644
--- a/src/gpu/effects/generated/GrRRectBlurEffect.cpp
+++ b/src/gpu/effects/generated/GrRRectBlurEffect.cpp
@@ -159,6 +159,13 @@
 std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::clone() const {
     return std::make_unique<GrRRectBlurEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrRRectBlurEffect::dumpInfo() const {
+    return SkStringPrintf("RRectBlurEffect(sigma=%f, rect=float4(%f, %f, %f, %f), cornerRadius=%f)",
+                          sigma, rect.left(), rect.top(), rect.right(), rect.bottom(),
+                          cornerRadius);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRRectBlurEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrRRectBlurEffect::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/effects/generated/GrRRectBlurEffect.h b/src/gpu/effects/generated/GrRRectBlurEffect.h
index c75a5c5..51d3d35 100644
--- a/src/gpu/effects/generated/GrRRectBlurEffect.h
+++ b/src/gpu/effects/generated/GrRRectBlurEffect.h
@@ -115,6 +115,9 @@
                                                      const SkRRect& srcRRect,
                                                      const SkRRect& devRRect);
     GrRRectBlurEffect(const GrRRectBlurEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "RRectBlurEffect"; }
     float sigma;
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.cpp b/src/gpu/effects/generated/GrRectBlurEffect.cpp
index 9c19e2e..fe80cf9 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.cpp
+++ b/src/gpu/effects/generated/GrRectBlurEffect.cpp
@@ -152,6 +152,12 @@
 std::unique_ptr<GrFragmentProcessor> GrRectBlurEffect::clone() const {
     return std::make_unique<GrRectBlurEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrRectBlurEffect::dumpInfo() const {
+    return SkStringPrintf("RectBlurEffect(rect=float4(%f, %f, %f, %f), isFast=%s)", rect.left(),
+                          rect.top(), rect.right(), rect.bottom(), (isFast ? "true" : "false"));
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRectBlurEffect);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrRectBlurEffect::TestCreate(GrProcessorTestData* data) {
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.h b/src/gpu/effects/generated/GrRectBlurEffect.h
index da5382e..329fb42 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.h
+++ b/src/gpu/effects/generated/GrRectBlurEffect.h
@@ -123,6 +123,9 @@
                                      GrSamplerState::Filter::kLinear));
     }
     GrRectBlurEffect(const GrRectBlurEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "RectBlurEffect"; }
     SkRect rect;
diff --git a/src/gpu/gradients/generated/GrClampedGradientEffect.cpp b/src/gpu/gradients/generated/GrClampedGradientEffect.cpp
index eea9a1e..ca703b9 100644
--- a/src/gpu/gradients/generated/GrClampedGradientEffect.cpp
+++ b/src/gpu/gradients/generated/GrClampedGradientEffect.cpp
@@ -114,3 +114,13 @@
 std::unique_ptr<GrFragmentProcessor> GrClampedGradientEffect::clone() const {
     return std::make_unique<GrClampedGradientEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrClampedGradientEffect::dumpInfo() const {
+    return SkStringPrintf(
+            "ClampedGradientEffect(leftBorderColor=half4(%f, %f, %f, %f), "
+            "rightBorderColor=half4(%f, %f, %f, %f), makePremul=%s, colorsAreOpaque=%s)",
+            leftBorderColor.fR, leftBorderColor.fG, leftBorderColor.fB, leftBorderColor.fA,
+            rightBorderColor.fR, rightBorderColor.fG, rightBorderColor.fB, rightBorderColor.fA,
+            (makePremul ? "true" : "false"), (colorsAreOpaque ? "true" : "false"));
+}
+#endif
diff --git a/src/gpu/gradients/generated/GrClampedGradientEffect.h b/src/gpu/gradients/generated/GrClampedGradientEffect.h
index aec0fd5..e03cb29 100644
--- a/src/gpu/gradients/generated/GrClampedGradientEffect.h
+++ b/src/gpu/gradients/generated/GrClampedGradientEffect.h
@@ -30,6 +30,9 @@
                 makePremul, colorsAreOpaque));
     }
     GrClampedGradientEffect(const GrClampedGradientEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "ClampedGradientEffect"; }
     SkPMColor4f leftBorderColor;
diff --git a/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.cpp b/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.cpp
index 4997042..c822301 100644
--- a/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.cpp
+++ b/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.cpp
@@ -137,6 +137,16 @@
 std::unique_ptr<GrFragmentProcessor> GrDualIntervalGradientColorizer::clone() const {
     return std::make_unique<GrDualIntervalGradientColorizer>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrDualIntervalGradientColorizer::dumpInfo() const {
+    return SkStringPrintf(
+            "DualIntervalGradientColorizer(scale01=float4(%f, %f, %f, %f), bias01=float4(%f, %f, "
+            "%f, %f), scale23=float4(%f, %f, %f, %f), bias23=float4(%f, %f, %f, %f), threshold=%f)",
+            scale01.fR, scale01.fG, scale01.fB, scale01.fA, bias01.fR, bias01.fG, bias01.fB,
+            bias01.fA, scale23.fR, scale23.fG, scale23.fB, scale23.fA, bias23.fR, bias23.fG,
+            bias23.fB, bias23.fA, threshold);
+}
+#endif
 
 std::unique_ptr<GrFragmentProcessor> GrDualIntervalGradientColorizer::Make(const SkPMColor4f& c0,
                                                                            const SkPMColor4f& c1,
diff --git a/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.h b/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.h
index 3d3be41..5d81785 100644
--- a/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.h
+++ b/src/gpu/gradients/generated/GrDualIntervalGradientColorizer.h
@@ -24,6 +24,9 @@
                                                      const SkPMColor4f& c3,
                                                      float threshold);
     GrDualIntervalGradientColorizer(const GrDualIntervalGradientColorizer& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "DualIntervalGradientColorizer"; }
     SkPMColor4f scale01;
diff --git a/src/gpu/gradients/generated/GrLinearGradientLayout.cpp b/src/gpu/gradients/generated/GrLinearGradientLayout.cpp
index 4133c0f..d6820b8 100644
--- a/src/gpu/gradients/generated/GrLinearGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrLinearGradientLayout.cpp
@@ -53,6 +53,9 @@
 std::unique_ptr<GrFragmentProcessor> GrLinearGradientLayout::clone() const {
     return std::make_unique<GrLinearGradientLayout>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrLinearGradientLayout::dumpInfo() const { return SkStringPrintf("LinearGradientLayout"); }
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrLinearGradientLayout);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrLinearGradientLayout::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/gradients/generated/GrLinearGradientLayout.h b/src/gpu/gradients/generated/GrLinearGradientLayout.h
index 21d68eb..99d6a10 100644
--- a/src/gpu/gradients/generated/GrLinearGradientLayout.h
+++ b/src/gpu/gradients/generated/GrLinearGradientLayout.h
@@ -25,6 +25,9 @@
     static std::unique_ptr<GrFragmentProcessor> Make(const SkLinearGradient& gradient,
                                                      const GrFPArgs& args);
     GrLinearGradientLayout(const GrLinearGradientLayout& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "LinearGradientLayout"; }
 
diff --git a/src/gpu/gradients/generated/GrRadialGradientLayout.cpp b/src/gpu/gradients/generated/GrRadialGradientLayout.cpp
index 6f8f325..d11a396 100644
--- a/src/gpu/gradients/generated/GrRadialGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrRadialGradientLayout.cpp
@@ -53,6 +53,9 @@
 std::unique_ptr<GrFragmentProcessor> GrRadialGradientLayout::clone() const {
     return std::make_unique<GrRadialGradientLayout>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrRadialGradientLayout::dumpInfo() const { return SkStringPrintf("RadialGradientLayout"); }
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradientLayout);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrRadialGradientLayout::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/gradients/generated/GrRadialGradientLayout.h b/src/gpu/gradients/generated/GrRadialGradientLayout.h
index 0584879..718ee14 100644
--- a/src/gpu/gradients/generated/GrRadialGradientLayout.h
+++ b/src/gpu/gradients/generated/GrRadialGradientLayout.h
@@ -25,6 +25,9 @@
     static std::unique_ptr<GrFragmentProcessor> Make(const SkRadialGradient& gradient,
                                                      const GrFPArgs& args);
     GrRadialGradientLayout(const GrRadialGradientLayout& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "RadialGradientLayout"; }
 
diff --git a/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.cpp b/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.cpp
index 0bf70a1..afebb0a 100644
--- a/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.cpp
+++ b/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.cpp
@@ -88,3 +88,11 @@
 std::unique_ptr<GrFragmentProcessor> GrSingleIntervalGradientColorizer::clone() const {
     return std::make_unique<GrSingleIntervalGradientColorizer>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrSingleIntervalGradientColorizer::dumpInfo() const {
+    return SkStringPrintf(
+            "SingleIntervalGradientColorizer(start=half4(%f, %f, %f, %f), end=half4(%f, %f, %f, "
+            "%f))",
+            start.fR, start.fG, start.fB, start.fA, end.fR, end.fG, end.fB, end.fA);
+}
+#endif
diff --git a/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.h b/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.h
index 15220f5..806b1f7 100644
--- a/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.h
+++ b/src/gpu/gradients/generated/GrSingleIntervalGradientColorizer.h
@@ -23,6 +23,9 @@
                 new GrSingleIntervalGradientColorizer(start, end));
     }
     GrSingleIntervalGradientColorizer(const GrSingleIntervalGradientColorizer& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "SingleIntervalGradientColorizer"; }
     SkPMColor4f start;
diff --git a/src/gpu/gradients/generated/GrSweepGradientLayout.cpp b/src/gpu/gradients/generated/GrSweepGradientLayout.cpp
index 8bb3372..a661b68 100644
--- a/src/gpu/gradients/generated/GrSweepGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrSweepGradientLayout.cpp
@@ -91,6 +91,11 @@
 std::unique_ptr<GrFragmentProcessor> GrSweepGradientLayout::clone() const {
     return std::make_unique<GrSweepGradientLayout>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrSweepGradientLayout::dumpInfo() const {
+    return SkStringPrintf("SweepGradientLayout(bias=%f, scale=%f)", bias, scale);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSweepGradientLayout);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrSweepGradientLayout::TestCreate(GrProcessorTestData* d) {
diff --git a/src/gpu/gradients/generated/GrSweepGradientLayout.h b/src/gpu/gradients/generated/GrSweepGradientLayout.h
index 1e4179d..984b9fa 100644
--- a/src/gpu/gradients/generated/GrSweepGradientLayout.h
+++ b/src/gpu/gradients/generated/GrSweepGradientLayout.h
@@ -25,6 +25,9 @@
     static std::unique_ptr<GrFragmentProcessor> Make(const SkSweepGradient& gradient,
                                                      const GrFPArgs& args);
     GrSweepGradientLayout(const GrSweepGradientLayout& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "SweepGradientLayout"; }
     float bias;
diff --git a/src/gpu/gradients/generated/GrTiledGradientEffect.cpp b/src/gpu/gradients/generated/GrTiledGradientEffect.cpp
index a8022fb..53ff674 100644
--- a/src/gpu/gradients/generated/GrTiledGradientEffect.cpp
+++ b/src/gpu/gradients/generated/GrTiledGradientEffect.cpp
@@ -93,3 +93,10 @@
 std::unique_ptr<GrFragmentProcessor> GrTiledGradientEffect::clone() const {
     return std::make_unique<GrTiledGradientEffect>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrTiledGradientEffect::dumpInfo() const {
+    return SkStringPrintf("TiledGradientEffect(mirror=%s, makePremul=%s, colorsAreOpaque=%s)",
+                          (mirror ? "true" : "false"), (makePremul ? "true" : "false"),
+                          (colorsAreOpaque ? "true" : "false"));
+}
+#endif
diff --git a/src/gpu/gradients/generated/GrTiledGradientEffect.h b/src/gpu/gradients/generated/GrTiledGradientEffect.h
index 9831c97..1f08b21 100644
--- a/src/gpu/gradients/generated/GrTiledGradientEffect.h
+++ b/src/gpu/gradients/generated/GrTiledGradientEffect.h
@@ -28,6 +28,9 @@
                 std::move(colorizer), std::move(gradLayout), mirror, makePremul, colorsAreOpaque));
     }
     GrTiledGradientEffect(const GrTiledGradientEffect& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "TiledGradientEffect"; }
     bool mirror;
diff --git a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
index 9c0afde..52f570b 100644
--- a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
+++ b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.cpp
@@ -185,6 +185,17 @@
 std::unique_ptr<GrFragmentProcessor> GrTwoPointConicalGradientLayout::clone() const {
     return std::make_unique<GrTwoPointConicalGradientLayout>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrTwoPointConicalGradientLayout::dumpInfo() const {
+    return SkStringPrintf(
+            "TwoPointConicalGradientLayout(type=%d, isRadiusIncreasing=%s, isFocalOnCircle=%s, "
+            "isWellBehaved=%s, isSwapped=%s, isNativelyFocal=%s, focalParams=half2(%f, %f))",
+            (int)type, (isRadiusIncreasing ? "true" : "false"),
+            (isFocalOnCircle ? "true" : "false"), (isWellBehaved ? "true" : "false"),
+            (isSwapped ? "true" : "false"), (isNativelyFocal ? "true" : "false"), focalParams.fX,
+            focalParams.fY);
+}
+#endif
 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrTwoPointConicalGradientLayout);
 #if GR_TEST_UTILS
 std::unique_ptr<GrFragmentProcessor> GrTwoPointConicalGradientLayout::TestCreate(
diff --git a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.h b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.h
index 25defb9..091820b 100644
--- a/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.h
+++ b/src/gpu/gradients/generated/GrTwoPointConicalGradientLayout.h
@@ -27,6 +27,9 @@
     static std::unique_ptr<GrFragmentProcessor> Make(const SkTwoPointConicalGradient& gradient,
                                                      const GrFPArgs& args);
     GrTwoPointConicalGradientLayout(const GrTwoPointConicalGradientLayout& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "TwoPointConicalGradientLayout"; }
     Type type;
diff --git a/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.cpp b/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.cpp
index ff9914d..ce61f28 100644
--- a/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.cpp
+++ b/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.cpp
@@ -344,6 +344,34 @@
 std::unique_ptr<GrFragmentProcessor> GrUnrolledBinaryGradientColorizer::clone() const {
     return std::make_unique<GrUnrolledBinaryGradientColorizer>(*this);
 }
+#ifdef SK_DEBUG
+SkString GrUnrolledBinaryGradientColorizer::dumpInfo() const {
+    return SkStringPrintf(
+            "UnrolledBinaryGradientColorizer(intervalCount=%d, scale0_1=float4(%f, %f, %f, %f), "
+            "scale2_3=float4(%f, %f, %f, %f), scale4_5=float4(%f, %f, %f, %f), scale6_7=float4(%f, "
+            "%f, %f, %f), scale8_9=float4(%f, %f, %f, %f), scale10_11=float4(%f, %f, %f, %f), "
+            "scale12_13=float4(%f, %f, %f, %f), scale14_15=float4(%f, %f, %f, %f), "
+            "bias0_1=float4(%f, %f, %f, %f), bias2_3=float4(%f, %f, %f, %f), bias4_5=float4(%f, "
+            "%f, %f, %f), bias6_7=float4(%f, %f, %f, %f), bias8_9=float4(%f, %f, %f, %f), "
+            "bias10_11=float4(%f, %f, %f, %f), bias12_13=float4(%f, %f, %f, %f), "
+            "bias14_15=float4(%f, %f, %f, %f), thresholds1_7=half4(%f, %f, %f, %f), "
+            "thresholds9_13=half4(%f, %f, %f, %f))",
+            intervalCount, scale0_1.fR, scale0_1.fG, scale0_1.fB, scale0_1.fA, scale2_3.fR,
+            scale2_3.fG, scale2_3.fB, scale2_3.fA, scale4_5.fR, scale4_5.fG, scale4_5.fB,
+            scale4_5.fA, scale6_7.fR, scale6_7.fG, scale6_7.fB, scale6_7.fA, scale8_9.fR,
+            scale8_9.fG, scale8_9.fB, scale8_9.fA, scale10_11.fR, scale10_11.fG, scale10_11.fB,
+            scale10_11.fA, scale12_13.fR, scale12_13.fG, scale12_13.fB, scale12_13.fA,
+            scale14_15.fR, scale14_15.fG, scale14_15.fB, scale14_15.fA, bias0_1.fR, bias0_1.fG,
+            bias0_1.fB, bias0_1.fA, bias2_3.fR, bias2_3.fG, bias2_3.fB, bias2_3.fA, bias4_5.fR,
+            bias4_5.fG, bias4_5.fB, bias4_5.fA, bias6_7.fR, bias6_7.fG, bias6_7.fB, bias6_7.fA,
+            bias8_9.fR, bias8_9.fG, bias8_9.fB, bias8_9.fA, bias10_11.fR, bias10_11.fG,
+            bias10_11.fB, bias10_11.fA, bias12_13.fR, bias12_13.fG, bias12_13.fB, bias12_13.fA,
+            bias14_15.fR, bias14_15.fG, bias14_15.fB, bias14_15.fA, thresholds1_7.left(),
+            thresholds1_7.top(), thresholds1_7.right(), thresholds1_7.bottom(),
+            thresholds9_13.left(), thresholds9_13.top(), thresholds9_13.right(),
+            thresholds9_13.bottom());
+}
+#endif
 
 static const int kMaxIntervals = 8;
 std::unique_ptr<GrFragmentProcessor> GrUnrolledBinaryGradientColorizer::Make(
diff --git a/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.h b/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.h
index 1a3d20c..d04dcd3 100644
--- a/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.h
+++ b/src/gpu/gradients/generated/GrUnrolledBinaryGradientColorizer.h
@@ -24,6 +24,9 @@
                                                      const SkScalar* positions,
                                                      int count);
     GrUnrolledBinaryGradientColorizer(const GrUnrolledBinaryGradientColorizer& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "UnrolledBinaryGradientColorizer"; }
     int32_t intervalCount;
diff --git a/src/sksl/README b/src/sksl/README
index 1175155..f8530f0 100644
--- a/src/sksl/README
+++ b/src/sksl/README
@@ -109,6 +109,7 @@
     @fields            (extra private fields, each terminated with a semicolon)
     @make              (replaces the default Make function)
     @clone             (replaces the default clone() function)
+    @dumpInfo          (replaces the default dumpInfo() function)
     @setData(<pdman>)  (extra code for the setData function, where <pdman> is
                         the name of the GrGLSLProgramDataManager)
     @test(<testData>)  (the body of the TestCreate function, where <testData> is
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index a37b93b..8a15124 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -164,67 +164,104 @@
            var.fType.kind() != Type::kSampler_Kind;
 }
 
-void CPPCodeGenerator::writeRuntimeValue(const Type& type, const Layout& layout,
-                                         const String& cppCode) {
+String CPPCodeGenerator::formatRuntimeValue(const Type& type,
+                                            const Layout& layout,
+                                            const String& cppCode,
+                                            std::vector<String>* formatArgs) {
     if (type.isFloat()) {
-        this->write("%f");
-        fFormatArgs.push_back(cppCode);
-    } else if (type == *fContext.fInt_Type) {
-        this->write("%d");
-        fFormatArgs.push_back(cppCode);
-    } else if (type == *fContext.fBool_Type) {
-        this->write("%s");
-        fFormatArgs.push_back("(" + cppCode + " ? \"true\" : \"false\")");
-    } else if (type == *fContext.fFloat2_Type || type == *fContext.fHalf2_Type) {
-        this->write(type.name() + "(%f, %f)");
-        fFormatArgs.push_back(cppCode + ".fX");
-        fFormatArgs.push_back(cppCode + ".fY");
-    } else if (type == *fContext.fFloat4_Type || type == *fContext.fHalf4_Type) {
-        this->write(type.name() + "(%f, %f, %f, %f)");
+        formatArgs->push_back(cppCode);
+        return "%f";
+    }
+    if (type == *fContext.fInt_Type) {
+        formatArgs->push_back(cppCode);
+        return "%d";
+    }
+    if (type == *fContext.fBool_Type) {
+        formatArgs->push_back("(" + cppCode + " ? \"true\" : \"false\")");
+        return "%s";
+    }
+    if (type == *fContext.fFloat2_Type || type == *fContext.fHalf2_Type) {
+        formatArgs->push_back(cppCode + ".fX");
+        formatArgs->push_back(cppCode + ".fY");
+        return type.name() + "(%f, %f)";
+    }
+    if (type == *fContext.fFloat3_Type || type == *fContext.fHalf3_Type) {
+        formatArgs->push_back(cppCode + ".fX");
+        formatArgs->push_back(cppCode + ".fY");
+        formatArgs->push_back(cppCode + ".fZ");
+        return type.name() + "(%f, %f, %f)";
+    }
+    if (type == *fContext.fFloat4_Type || type == *fContext.fHalf4_Type) {
         switch (layout.fCType) {
             case Layout::CType::kSkPMColor:
-                fFormatArgs.push_back("SkGetPackedR32(" + cppCode + ") / 255.0");
-                fFormatArgs.push_back("SkGetPackedG32(" + cppCode + ") / 255.0");
-                fFormatArgs.push_back("SkGetPackedB32(" + cppCode + ") / 255.0");
-                fFormatArgs.push_back("SkGetPackedA32(" + cppCode + ") / 255.0");
+                formatArgs->push_back("SkGetPackedR32(" + cppCode + ") / 255.0");
+                formatArgs->push_back("SkGetPackedG32(" + cppCode + ") / 255.0");
+                formatArgs->push_back("SkGetPackedB32(" + cppCode + ") / 255.0");
+                formatArgs->push_back("SkGetPackedA32(" + cppCode + ") / 255.0");
                 break;
             case Layout::CType::kSkPMColor4f:
-                fFormatArgs.push_back(cppCode + ".fR");
-                fFormatArgs.push_back(cppCode + ".fG");
-                fFormatArgs.push_back(cppCode + ".fB");
-                fFormatArgs.push_back(cppCode + ".fA");
+                formatArgs->push_back(cppCode + ".fR");
+                formatArgs->push_back(cppCode + ".fG");
+                formatArgs->push_back(cppCode + ".fB");
+                formatArgs->push_back(cppCode + ".fA");
                 break;
             case Layout::CType::kSkV4:
-                fFormatArgs.push_back(cppCode + ".x");
-                fFormatArgs.push_back(cppCode + ".y");
-                fFormatArgs.push_back(cppCode + ".z");
-                fFormatArgs.push_back(cppCode + ".w");
+                formatArgs->push_back(cppCode + ".x");
+                formatArgs->push_back(cppCode + ".y");
+                formatArgs->push_back(cppCode + ".z");
+                formatArgs->push_back(cppCode + ".w");
                 break;
-            case Layout::CType::kSkRect: // fall through
+            case Layout::CType::kSkRect:
             case Layout::CType::kDefault:
-                fFormatArgs.push_back(cppCode + ".left()");
-                fFormatArgs.push_back(cppCode + ".top()");
-                fFormatArgs.push_back(cppCode + ".right()");
-                fFormatArgs.push_back(cppCode + ".bottom()");
+                formatArgs->push_back(cppCode + ".left()");
+                formatArgs->push_back(cppCode + ".top()");
+                formatArgs->push_back(cppCode + ".right()");
+                formatArgs->push_back(cppCode + ".bottom()");
                 break;
             default:
                 SkASSERT(false);
         }
-    } else if (type.kind() == Type::kEnum_Kind) {
-        this->write("%d");
-        fFormatArgs.push_back("(int) " + cppCode);
-    } else if (type == *fContext.fInt4_Type ||
-               type == *fContext.fShort4_Type ||
-               type == *fContext.fByte4_Type) {
-        this->write(type.name() + "(%d, %d, %d, %d)");
-        fFormatArgs.push_back(cppCode + ".left()");
-        fFormatArgs.push_back(cppCode + ".top()");
-        fFormatArgs.push_back(cppCode + ".right()");
-        fFormatArgs.push_back(cppCode + ".bottom()");
-    } else {
-        printf("unsupported runtime value type '%s'\n", String(type.fName).c_str());
-        SkASSERT(false);
+        return type.name() + "(%f, %f, %f, %f)";
     }
+    if (type.kind() == Type::kMatrix_Kind) {
+        SkASSERT(type.componentType() == *fContext.fFloat_Type ||
+                 type.componentType() == *fContext.fHalf_Type);
+
+        String format = type.name() + "(";
+        for (int c = 0; c < type.columns(); ++c) {
+            for (int r = 0; r < type.rows(); ++r) {
+                String& arg = formatArgs->emplace_back();
+                arg.appendf("%s.rc(%d, %d)", cppCode.c_str(), r, c);
+                format += "%f, ";
+            }
+        }
+
+        // Replace trailing ", " with ")".
+        format.pop_back();
+        format.back() = ')';
+        return format;
+    }
+    if (type.kind() == Type::kEnum_Kind) {
+        formatArgs->push_back("(int) " + cppCode);
+        return "%d";
+    }
+    if (type == *fContext.fInt4_Type ||
+        type == *fContext.fShort4_Type ||
+        type == *fContext.fByte4_Type) {
+        formatArgs->push_back(cppCode + ".left()");
+        formatArgs->push_back(cppCode + ".top()");
+        formatArgs->push_back(cppCode + ".right()");
+        formatArgs->push_back(cppCode + ".bottom()");
+        return type.name() + "(%d, %d, %d, %d)";
+    }
+
+    SkDEBUGFAILF("unsupported runtime value type '%s'\n", String(type.fName).c_str());
+    return "";
+}
+
+void CPPCodeGenerator::writeRuntimeValue(const Type& type, const Layout& layout,
+                                         const String& cppCode) {
+    this->write(this->formatRuntimeValue(type, layout, cppCode, &fFormatArgs));
 }
 
 void CPPCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
@@ -1065,8 +1102,8 @@
 void CPPCodeGenerator::writeClone() {
     if (!this->writeSection(kCloneSection)) {
         if (fSectionAndParameterHelper.getSection(kFieldsSection)) {
-            fErrors.error(0, "fragment processors with custom @fields must also have a custom"
-                             "@clone");
+            fErrors.error(/*offset=*/0, "fragment processors with custom @fields must also have a "
+                                        "custom @clone");
         }
         this->writef("%s::%s(const %s& src)\n"
                      ": INHERITED(k%s_ClassID, src.optimizationFlags())", fFullName.c_str(),
@@ -1102,6 +1139,55 @@
     }
 }
 
+void CPPCodeGenerator::writeDumpInfo() {
+    this->writef("#ifdef SK_DEBUG\n"
+                 "SkString %s::dumpInfo() const {\n", fFullName.c_str());
+
+    if (!this->writeSection(kDumpInfoSection)) {
+        if (fSectionAndParameterHelper.getSection(kFieldsSection)) {
+            fErrors.error(/*offset=*/0, "fragment processors with custom @fields must also have a "
+                                        "custom @dumpInfo");
+        }
+
+        this->writef("    return SkStringPrintf(\"%s", fName.c_str());
+
+        String formatString;
+        std::vector<String> argumentList;
+
+        for (const Variable* param : fSectionAndParameterHelper.getParameters()) {
+            // dumpInfo() doesn't need to log child FPs.
+            if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+                continue;
+            }
+
+            // Add this field onto the format string and argument list.
+            String fieldName = HCodeGenerator::FieldName(String(param->fName).c_str());
+            String runtimeValue = this->formatRuntimeValue(param->fType, param->fModifiers.fLayout,
+                                                           param->fName, &argumentList);
+            formatString.appendf("%s%s=%s",
+                                 formatString.empty() ? "" : ", ",
+                                 fieldName.c_str(),
+                                 runtimeValue.c_str());
+        }
+
+        // Append finished format string.
+        if (!formatString.empty()) {
+            this->writef("(%s)", formatString.c_str());
+        }
+
+        // Close-quote, then append each argument.
+        this->write("\"");
+        for (const String& argument : argumentList) {
+            this->writef(", %s", argument.c_str());
+        }
+
+        this->write(");");
+    }
+
+    this->write("\n}\n"
+                "#endif\n");
+}
+
 void CPPCodeGenerator::writeTest() {
     const Section* test = fSectionAndParameterHelper.getSection(kTestCodeSection);
     if (test) {
@@ -1294,6 +1380,7 @@
     this->write("    return true;\n"
                 "}\n");
     this->writeClone();
+    this->writeDumpInfo();
     this->writeOnTextureSampler();
     this->writeTest();
     this->writeSection(kCppEndSection);
diff --git a/src/sksl/SkSLCPPCodeGenerator.h b/src/sksl/SkSLCPPCodeGenerator.h
index d7ee69c..4973f21 100644
--- a/src/sksl/SkSLCPPCodeGenerator.h
+++ b/src/sksl/SkSLCPPCodeGenerator.h
@@ -67,6 +67,8 @@
 
     // writes a printf escape that will be filled in at runtime by the given C++ expression string
     void writeRuntimeValue(const Type& type, const Layout& layout, const String& cppCode);
+    String formatRuntimeValue(const Type& type, const Layout& layout, const String& cppCode,
+                              std::vector<String>* formatArgs);
 
     void writeVarInitializer(const Variable& var, const Expression& value) override;
 
@@ -88,6 +90,8 @@
 
     void writeClone();
 
+    void writeDumpInfo();
+
     void writeTest();
 
     // If the returned C++ is included in the generated code, then the variable name stored in
diff --git a/src/sksl/SkSLHCodeGenerator.cpp b/src/sksl/SkSLHCodeGenerator.cpp
index 6f9375b..2f87bb3 100644
--- a/src/sksl/SkSLHCodeGenerator.cpp
+++ b/src/sksl/SkSLHCodeGenerator.cpp
@@ -355,6 +355,9 @@
     this->writeSection(kClassSection);
     this->writeMake();
     this->writef("    %s(const %s& src);\n"
+                 "#ifdef SK_DEBUG\n"
+                 "    SkString dumpInfo() const override;\n"
+                 "#endif\n"
                  "    std::unique_ptr<GrFragmentProcessor> clone() const override;\n"
                  "    const char* name() const override { return \"%s\"; }\n",
                  fFullName.c_str(), fFullName.c_str(), fName.c_str());
diff --git a/src/sksl/SkSLSectionAndParameterHelper.h b/src/sksl/SkSLSectionAndParameterHelper.h
index b6a2840..e35f27d 100644
--- a/src/sksl/SkSLSectionAndParameterHelper.h
+++ b/src/sksl/SkSLSectionAndParameterHelper.h
@@ -24,6 +24,7 @@
 inline constexpr char kConstructorParamsSection[] =  "constructorParams";
 inline constexpr char kCppSection[] =                "cpp";
 inline constexpr char kCppEndSection[] =             "cppEnd";
+inline constexpr char kDumpInfoSection[] =           "dumpInfo";
 inline constexpr char kEmitCodeSection[] =           "emitCode";
 inline constexpr char kFieldsSection[] =             "fields";
 inline constexpr char kHeaderSection[] =             "header";
@@ -74,6 +75,7 @@
                !strcmp(name, kConstructorParamsSection) ||
                !strcmp(name, kCppSection) ||
                !strcmp(name, kCppEndSection) ||
+               !strcmp(name, kDumpInfoSection) ||
                !strcmp(name, kEmitCodeSection) ||
                !strcmp(name, kFieldsSection) ||
                !strcmp(name, kHeaderSection) ||
diff --git a/tests/SkSLFPTest.cpp b/tests/SkSLFPTest.cpp
index 74bb16f..c900b42 100644
--- a/tests/SkSLFPTest.cpp
+++ b/tests/SkSLFPTest.cpp
@@ -113,6 +113,9 @@
         return std::unique_ptr<GrFragmentProcessor>(new GrTest());
     }
     GrTest(const GrTest& src);
+#ifdef SK_DEBUG
+    SkString dumpInfo() const override;
+#endif
     std::unique_ptr<GrFragmentProcessor> clone() const override;
     const char* name() const override { return "Test"; }
 private:
@@ -472,6 +475,7 @@
          R"__SkSL__(
              @fields { fields section }
              @clone { }
+             @dumpInfo { }
              void main() {
                  sk_OutColor = half4(1);
              }
@@ -527,6 +531,22 @@
              " testDataName section }\n"
              "#endif"
          });
+    test(r,
+         *SkSL::ShaderCapsFactory::Default(),
+         R"__SkSL__(
+             @dumpInfo {dump all the fields}
+             void main() {
+                 sk_OutColor = half4(1);
+             }
+         )__SkSL__",
+         /*expectedH=*/{},
+         /*expectedCPP=*/{
+R"__Cpp__(#ifdef SK_DEBUG
+SkString GrTest::dumpInfo() const {
+dump all the fields
+}
+#endif)__Cpp__"
+         });
 }
 
 DEF_TEST(SkSLFPMainCoords, r) {