এম্বেড থাকা সিস্টেমগুলি ডিবাগ করার জন্য প্রিন্টফ () খারাপ কেন?


16

আমি অনুমান এটা একটি খারাপ জিনিস ব্যবহার করে একটি মাইক্রোকন্ট্রোলার-ভিত্তিক প্রকল্প ডিবাগ করার চেষ্টা করতে printf()

আমি বুঝতে পারি যে আপনার আউটপুট দেওয়ার কোনও পূর্বনির্ধারিত জায়গা নেই এবং এটি মূল্যবান পিনগুলি গ্রাস করতে পারে। একই সময়ে আমি দেখেছি লোকেরা কাস্টম DEBUG_PRINT()ম্যাক্রো দিয়ে আইডিই টার্মিনালে আউটপুট দেওয়ার জন্য একটি ইউআরটি টিএক্স পিন ব্যবহার করে ।


12
কে বলেছে যে এটা খারাপ? "সাধারণত সেরা নয়" কোনও অযোগ্য "খারাপ" এর মতো নয়।
স্পিহ্রো পেফানি

6
সেখানে কতটা ওভারহেড রয়েছে সে সম্পর্কে এই সমস্ত কথা, আপনার আউটপুটটি কেবল "আমি এখানে আছি" বার্তা আউটপুট করার দরকার হলে আপনার কোনও প্রিন্টফের প্রয়োজন হয় না, কেবল একটি ইউআআআরটিতে একটি স্ট্রিং প্রেরণের জন্য একটি রুটিন। এটি, প্লাস ইউআআরটি আরম্ভ করার কোডটি সম্ভবত কোডের 100 বাইটের নিচে। কয়েকটি কয়েকটি হেক্স মানের আউটপুট দেওয়ার ক্ষমতা যুক্ত করা এগুলি এত বেশি করে না।
tcrosley

7
@ চেটান ভরগভা - সি হেডার ফাইলগুলি সাধারণত সম্পাদনযোগ্যকে কোড যুক্ত করে না। তারা ঘোষণা আছে; যদি বাকী কোডটি ঘোষিত জিনিসগুলি ব্যবহার না করে তবে things জিনিসগুলির জন্য কোডটি লিঙ্কযুক্ত হয় না you আপনি যদি ব্যবহার করেন printfঅবশ্যই, প্রয়োগ করার জন্য প্রয়োজনীয় সমস্ত কোড printfসম্পাদনযোগ্যের সাথে সংযুক্ত হয়ে যায়। কোডটি এটি ব্যবহার করেছিল, এটি শিরোনামের কারণে নয়।
পিট বেকার

2
@ চেতান ভর্তাভা আপনার নিজের পছন্দ অনুসারে <স্টডিও এইচ> অন্তর্ভুক্ত করার দরকার নেই যদি আমি বর্ণিত স্ট্রিং আউটপুট করতে নিজের নিজস্ব রুটিন রোল করি (
ইউআর্টে

2
@ টিক্রোসলে আমি মনে করি আপনার যদি একটি ভাল আধুনিক সংকলক থাকে তবে আপনি যদি ফর্ম্যাট স্ট্রিং জিসিসি ছাড়াই সাধারণ ক্ষেত্রে প্রিন্টফ ব্যবহার করেন এবং বেশিরভাগ অন্যরা এটিকে আরও দক্ষ একটি সহজ কল দিয়ে প্রতিস্থাপন করেন যা আপনার বর্ণনা অনুযায়ী অনেক কিছু করে।
মূল্য

উত্তর:


24

আমি প্রিন্টফ () ব্যবহারের কয়েকটি অসুবিধা নিয়ে আসতে পারি। মনে রাখবেন যে "এম্বেডেড সিস্টেম" এমন কোনও কিছু থেকে কয়েকশ বাইটের প্রোগ্রাম মেমরির সাথে পুরো গজায় র্যাক-মাউন্ট কিউএনএক্স আরটিওস-চালিত সিস্টেমের সাথে গিগাবাইট র‍্যাম এবং ননভোলটেইল মেমরির টেরাবাইট হতে পারে।

  • এটি ডেটা প্রেরণের জন্য কোথাও কোথাও প্রয়োজন। সম্ভবত আপনার সিস্টেমে একটি ডিবাগ বা প্রোগ্রামিং পোর্ট রয়েছে, সম্ভবত আপনি না। যদি আপনি না করেন (বা আপনি যার সাথে কাজ করছেন না) এটি খুব সহজ নয়।

  • এটি সমস্ত প্রসঙ্গে হালকা ওজনের কাজ নয়। আপনার কাছে মাত্র কয়েক কে মেমরির সাথে একটি মাইক্রোকন্ট্রোলার থাকলে এটি বড় ব্যাপার হতে পারে, কারণ প্রিন্টফের সাথে লিঙ্ক করা নিজেই 4K খায়। আপনার যদি 32 কে বা 256 কে মাইক্রোকন্ট্রোলার থাকে তবে এটি সম্ভবত কোনও সমস্যা নয়, আপনার যদি একটি বড় এম্বেডেড সিস্টেম থাকে তবে তা ছেড়ে দিন।

  • মেমরি বরাদ্দ বা বাধা সম্পর্কিত কিছু ধরণের সমস্যাগুলি খুঁজে পাওয়ার জন্য এটি খুব কম বা কোনও কাজে আসে না এবং বিবৃতি অন্তর্ভুক্ত করা হয় বা না থাকাকালীন প্রোগ্রামের আচরণ পরিবর্তন করতে পারে।

  • সময় সংবেদনশীল স্টাফ নিয়ে কাজ করার জন্য এটি বেশ বেহুদা। আপনি একটি যুক্তি বিশ্লেষক এবং একটি অসিলোস্কোপ বা একটি প্রোটোকল বিশ্লেষক, এমনকি একটি সিমুলেটর দিয়ে আরও ভাল হতে চাই।

  • আপনার যদি একটি বড় প্রোগ্রাম থাকে এবং আপনার চারপাশে প্রিন্টফের স্টেটমেন্টগুলি পরিবর্তন করতে এবং সেগুলি পরিবর্তন করার জন্য আপনাকে অনেক সময় পুনরায় সংকলন করতে হয় তবে আপনি অনেক সময় নষ্ট করতে পারেন।

এটি কী জন্য ভাল - এটি একটি পূর্বরূপিত পদ্ধতিতে ডেটা আউটপুট দেওয়ার দ্রুত উপায় যা প্রতিটি সি প্রোগ্রামার জেনে যায় যে শূন্য লার্নিং কার্ভটি কীভাবে ব্যবহার করতে হয়। আপনি যদি ডিবাগ করছেন এমন কলম্যান ফিল্টারের জন্য যদি আপনার কোনও ম্যাট্রিক্স থুথু প্রয়োজন হয় তবে ম্যাটল্যাব যে ফর্ম্যাটটি পড়তে পারে সেটিতে এটি ছুঁড়ে ফেলা ভাল। অবশ্যই একবারে ডিবাগার বা এমুলেটরের মধ্যে র‌্যামের অবস্থানগুলি দেখার চেয়ে ভাল better ।

আমি মনে করি এটি চলাচলের কোনও অকেজো তীর নয়, তবে এটি জিডিবি বা অন্যান্য ডিবাগার, এমুলেটর, লজিক বিশ্লেষক, অসিলোস্কোপস, স্ট্যাটিক কোড বিশ্লেষণ সরঞ্জামগুলি, কোড কভারেজ সরঞ্জামগুলি এবং এর সাথে সাথে খুব কম ব্যবহার করা উচিত।


3
বেশিরভাগ printf()বাস্তবায়ন থ্রেড-নিরাপদ নয় (অর্থাত্ পুনরায় প্রবেশকারী নয়) যা কোনও চুক্তি হত্যাকারী নয়, তবে এটি মাল্টি-থ্রেড পরিবেশে ব্যবহার করার সময় মনে রাখা উচিত।
জোরবার্ট

1
@ জারউবার্ট একটি ভাল পয়েন্ট তুলে ধরেছে .. এবং এমনকি অপারেটিং সিস্টেম ব্যতীত পরিবেশেও আইএসআরগুলির সরাসরি কার্যকর ডিবাগিং করা কঠিন। অবশ্যই আপনি যদি কোনও আইএসআর-এ প্রিন্টফ () বা ভাসমান পয়েন্ট গণিত করছেন তবে সম্ভবত পদ্ধতিটি বন্ধ রয়েছে।
স্পিহ্রো পেফানি

@ জারউবার্ট সফ্টওয়্যার বিকাশকারীরা কোন মাল্টি-থ্রেড এনভায়রনমেন্টে (এমন একটি হার্ডওয়্যার সেটিংয়ে যেখানে লজিক অ্যানালাইজার এবং অসিলোস্কোপের ব্যবহার ব্যবহারিক নয়) কাজ করছে কোন ডিবাগিং সরঞ্জামগুলি রয়েছে?
মিন ট্রান

1
অতীতে আমি নিজের থ্রেড-সেফ প্রিন্টফ () রোলড করেছি; টার্মিনালে খুব সংক্ষিপ্ত তথ্য আউট করতে খালি পায়ে পুটস () বা পুটচার () সমতুল্য ব্যবহৃত হয়; বাইনারি ডেটা সংরক্ষণ করে এমন একটি অ্যারে যা আমি ফেলেছি এবং পরীক্ষা-নিরীক্ষার পরে ব্যাখ্যা করেছি; কোনও এলইডি জ্বলতে বা অসিলোস্কোপের সাহায্যে সময় পরিমাপ করতে ডাল জেনার জন্য আমি / ও পোর্ট ব্যবহার করি; ডি / এ এর ​​সাথে একটি সংখ্যা ছড়িয়ে দিন এবং ভিওএম দিয়ে পরিমাপ করা ... তালিকাটি আপনার কল্পনা হিসাবে দীর্ঘ এবং বিপরীতভাবে আপনার বাজেটের মতো বড় big :)
জেবার্ট

19

কিছু অন্যান্য সূক্ষ্ম জবাব ছাড়াও সিরিয়াল বাড রেটে কোনও বন্দরে ডেটা প্রেরণের কাজটি আপনার লুপ সময়ের সাথে সম্মতিতে খালি ধীর হতে পারে এবং আপনার প্রোগ্রামের বাকি কাজগুলি (যে কোনও ডিবাগ করতে পারে) এর প্রভাব ফেলতে পারে প্রক্রিয়ার মধ্যে)।

অন্যান্য লোকেরা আপনাকে বলছে যে, এই কৌশলটি ব্যবহার করা সম্পর্কে "খারাপ" কিছুই নেই, তবে এটি অন্যান্য ডিবাগ কৌশলগুলির মতোই এর সীমাবদ্ধতাও রয়েছে। যতক্ষণ আপনি জানেন এবং এই সীমাবদ্ধতাগুলি মোকাবেলা করতে পারেন ততক্ষণ আপনার কোড সঠিক করতে আপনাকে সহায়তা করার জন্য এটি অত্যন্ত সুবিধাজনক সাধ্য হতে পারে।

এম্বেড থাকা সিস্টেমগুলির একটি নির্দিষ্ট অস্বচ্ছতা থাকে যা সাধারণভাবে ডিবাগিংকে কিছুটা সমস্যা দেয়।


8
"এম্বেড থাকা সিস্টেমগুলির একটি নির্দিষ্ট অস্বচ্ছতা থাকে" এর জন্য +1। যদিও আমি আশঙ্কা করি যে এই বিবৃতিটি কেবল এম্বেডের সাথে কাজ করার শালীন অভিজ্ঞতা আছে তাদেরাই অনুধাবনযোগ্য হতে পারে, তবে এটি পরিস্থিতির একটি সুন্দর, সংক্ষিপ্ত সংক্ষিপ্তসার তৈরি করে। এটি আসলে "এম্বেডড" সংজ্ঞাটির কাছাকাছি।
এনজাহেঙ্কে

5

printfএকটি মাইক্রোকন্ট্রোলার ব্যবহারের চেষ্টা করার মধ্যে দুটি প্রধান সমস্যা রয়েছে ।

প্রথমত, সঠিক বন্দরে আউটপুটটি পাইপ করা ব্যথা হতে পারে। সবসময় না। তবে কিছু প্ল্যাটফর্ম অন্যদের চেয়ে বেশি কঠিন। কিছু কনফিগারেশন ফাইল দুর্বল নথিভুক্ত হতে পারে এবং প্রচুর পরীক্ষার প্রয়োজন হতে পারে।

দ্বিতীয়টি স্মৃতি। একটি পূর্ণ প্রস্ফুটিত printfগ্রন্থাগারটি বিআইজি হতে পারে। কখনও কখনও আপনার সমস্ত বিন্যাস নির্দিষ্টকরণের প্রয়োজন হয় না যদিও এবং বিশেষিত সংস্করণগুলি উপলভ্য। উদাহরণস্বরূপ, stdio.h এভিআর সরবরাহ করাতে তিনটি ভিন্ন printfভিন্ন আকার এবং কার্যকারিতা রয়েছে।

যেহেতু উল্লিখিত সমস্ত বৈশিষ্ট্যগুলির সম্পূর্ণ বাস্তবায়ন মোটামুটি বড় হয়ে যায়, তাই vfprintf()লিঙ্কার বিকল্পগুলি ব্যবহার করে তিনটি ভিন্ন স্বাদের নির্বাচন করা যেতে পারে। ডিফল্ট vfprintf()ভাসমান পয়েন্ট রূপান্তর ব্যতীত উল্লিখিত সমস্ত কার্যকারিতা কার্যকর করে। এর একটি সংক্ষিপ্ত সংস্করণ vfprintf()পাওয়া যায় যা কেবলমাত্র খুব মৌলিক পূর্ণসংখ্যা এবং স্ট্রিং রূপান্তর সুবিধা কার্যকর করে, তবে কেবলমাত্র #অতিরিক্ত বিকল্প রূপান্তর পতাকা ব্যবহার করে নির্দিষ্ট করা যেতে পারে (এই পতাকাগুলি বিন্যাসের স্পেসিফিকেশন থেকে সঠিকভাবে পার্স করা হয় তবে কেবল উপেক্ষা করা হবে)।

আমার একটি উদাহরণ ছিল যেখানে কোনও লাইব্রেরি উপলব্ধ ছিল না এবং আমার ন্যূনতম স্মৃতি ছিল। সুতরাং কাস্টম ম্যাক্রো ব্যবহার করা ছাড়া আমার আর কোনও উপায় ছিল না। তবে ব্যবহার printfবা না করা আপনার প্রয়োজনীয়তার সাথে খাপ খায় এমন একটি is


ভবিষ্যতে প্রকল্পগুলিতে আমি আমার ভুল এড়াতে পারি যাতে দয়া করে আমার উত্তরে কী ভুল তা দয়া করে ব্যাখ্যা করতে পারেন?
embedded.kyle

4

"টাইমিং-সংবেদনশীল জিনিস" সম্পর্কে স্পিহ্রো পেফানি কী বলছিলেন তা যুক্ত করার জন্য: আসুন একটি উদাহরণ নেওয়া যাক। ধরা যাক আপনার কাছে একটি গাইরোস্কোপ রয়েছে যা থেকে আপনার এম্বেড করা সিস্টেম প্রতি সেকেন্ডে 1000 পরিমাপ নিচ্ছে। আপনি এই পরিমাপগুলি ডিবাগ করতে চান, তাই আপনাকে সেগুলি মুদ্রণ করতে হবে। সমস্যা: এগুলি মুদ্রণের ফলে সিস্টেম প্রতি সেকেন্ডে 1000 টি পরিমাপ পড়তে ব্যস্ত হয়ে পড়ে, যার ফলে জাইরোস্কোপের বাফার ওভারফ্লো হয়ে যায়, যার ফলে দুর্নীতিগ্রস্থ ডেটা পড়তে (এবং মুদ্রিত) হয়। এবং তাই, তথ্য মুদ্রণ করে, আপনি ডেটাটি দূষিত করেছেন, আপনি যখন ভাবছেন যে যখন তথ্যটি পড়ার ক্ষেত্রে কোনও বাগ আছে তখন সম্ভবত সেখানে নেই। একটি তথাকথিত হাইজেনব্যাগ।


হাঃ হাঃ হাঃ! "হাইজেনবগ" আসলেই কি প্রযুক্তিগত শব্দ? আমি অনুমান করি যে এটি কণার রাষ্ট্র এবং হাইজেনবার্গের নীতিমালা পরিমাপের সাথে করতে পারে ...
জেটটা.বিজ্ঞানী

3

প্রিন্টফ () দিয়ে ডিবাগিং না করার বৃহত কারণটি হ'ল এটি সাধারণত অদক্ষ, অপর্যাপ্ত এবং অপ্রয়োজনীয়।

অপর্যাপ্ত: প্রিন্টফ () এবং স্বজন একটি ছোট মাইক্রোকন্ট্রোলারের মধ্যে যা পাওয়া যায় তার তুলনায় প্রচুর ফ্ল্যাশ এবং র‌্যাম ব্যবহার করে তবে বড় অদক্ষতা প্রকৃত ডিবাগিংয়ে। লগ করা হচ্ছে তা পরিবর্তনের জন্য লক্ষ্যটি পুনরায় সংশোধন এবং পুনরায় প্রোগ্রাম করা দরকার, যা প্রক্রিয়াটি ধীর করে দেয়। এটি এমন একটি ইউআআআরটিও ব্যবহার করে যা আপনি অন্যথায় দরকারী কাজের জন্য ব্যবহার করতে পারেন।

অপর্যাপ্ত: কেবলমাত্র এতগুলি বিশদ যা আপনি সিরিয়াল লিঙ্কে আউটপুট করতে পারেন। যদি প্রোগ্রামটি স্তব্ধ হয়ে যায়, আপনি ঠিক কোথায় জানেন না, কেবল শেষ আউটপুট সম্পূর্ণ হয়েছিল।

অপ্রয়োজনীয়: অনেক মাইক্রোকন্ট্রোলার দূর থেকে ডিবাগ করা যায়। প্রসেসরকে বিরতি দিতে, রেজিস্টার এবং র‌্যামে উঁকি দিতে এবং পুনরায় সংযোগ না করে চলমান প্রসেসরের অবস্থার পরিবর্তন করতেও জেটিএইচ বা মালিকানাধীন প্রোটোকল ব্যবহার করা যেতে পারে। এ কারণেই ডিবাগারগুলি মুদ্রণ বিবৃতিগুলির চেয়ে ডিবাগিংয়ের আরও ভাল উপায়, এমনকি অনেকগুলি স্থান এবং শক্তি থাকা পিসিতেও।

দুর্ভাগ্যজনক যে newbies জন্য সবচেয়ে সাধারণ মাইক্রোকন্ট্রোলার প্ল্যাটফর্ম, আরডুইনোতে কোনও ডিবাগার নেই। এভিআর রিমোট ডিবাগিংকে সমর্থন করে তবে এটেলের ডিবাগওয়াইআরআই প্রোটোকল মালিকানাধীন এবং নথিভুক্ত। আপনি জিডিবি দিয়ে ডিবাগ করার জন্য একটি অফিসিয়াল ডেভ বোর্ড ব্যবহার করতে পারেন, তবে যদি আপনার কাছে থাকে তবে আপনি আরডুইনো সম্পর্কে সম্ভবত খুব বেশি চিন্তিত নন।


লগ করা হচ্ছে তা নিয়ে খেলতে আপনি কী ফাংশন পয়েন্টার ব্যবহার করতে পারেন না এবং পুরো গুচ্ছতা যোগ করতে পারেন?
স্কট সিডম্যান

3

printf () নিজের কাজ করে না। এটি অন্যান্য অনেক ফাংশনকে কল করে এবং আপনার যদি স্ট্যাকের সামান্য জায়গা থাকে তবে আপনি আপনার স্ট্যাকের সীমাটির কাছাকাছি ইস্যুগুলি ডিবাগ করতে এটিকে মোটেও ব্যবহার করতে পারবেন না। সংকলক এবং মাইক্রোকন্ট্রোলারের উপর নির্ভর করে ফরম্যাটের স্ট্রিংটি ফ্ল্যাশ থেকে রেফারেন্স না করে মেমরিতে রাখা যেতে পারে। আপনি প্রিন্টফ স্টেটমেন্টগুলির সাথে আপনার কোডটি মরিচ করে দিলে এটি উল্লেখযোগ্যভাবে যুক্ত হতে পারে। এটি আরডুইনো পরিবেশের একটি বড় সমস্যা - কয়েক ডজন বা শত শত প্রিন্টফ স্টেটমেন্ট ব্যবহার করে হঠাৎ মনে হয় এলোমেলো সমস্যা দেখা দেয় কারণ তারা তাদের স্ট্যাক দিয়ে তাদের স্তূপটি ওভাররাইট করছে।


2
ডাউনভোট নিজেই যে প্রতিক্রিয়া জানায় আমি তার প্রশংসা করি, তবুও এটি আমার এবং অন্যদের পক্ষে আরও সহায়ক হবে যদি এই উত্তরটির সাথে দ্বিমত পোষণকারীরা সমস্যাগুলি ব্যাখ্যা করে। আমরা সবাই জ্ঞান শিখতে এবং ভাগ করে নেওয়ার জন্য, নিজের ভাগ করে নেওয়ার বিষয়ে বিবেচনা করি।
অ্যাডাম ডেভিস

3

এমনকি যদি কেউ লগিং কনসোলের কোনও ফর্মের জন্য ডেটা স্পিট করতে চায়, তবে printfফাংশনটি সাধারণত এটি করার খুব ভাল উপায় নয়, যেহেতু এটি পাস ফর্ম্যাট স্ট্রিংটি পরীক্ষা করে রান-টাইমে পার্স করতে হয়; এমনকি কোডটি ব্যতীত অন্য কোনও ফর্ম্যাট স্পেসিফায়ার ব্যবহার না করে %04X, নিয়ন্ত্রককে সাধারণত সমস্ত কোড অন্তর্ভুক্ত করতে হবে যা স্বেচ্ছাসেবী বিন্যাসের স্ট্রিংগুলি পার্স করার জন্য প্রয়োজন। যে কোনও সঠিক নিয়ামক ব্যবহার করছেন তার উপর নির্ভর করে কোড ব্যবহারের ক্ষেত্রে এটি আরও কার্যকর হতে পারে:

void log_string(const char *st)
{
  int ch;
  do
  {
    ch = *st++;
    if (ch==0) break;
    log_char(ch);
  } while(1);
}
void log_hexdigit(unsigned char d)
{
  d&=15;
  if (d > 9) d+=7;
  log_char(d+'0');
}
void log_hexbyte(unsigned char b)
{ log_hexdigit(b >> 4); log_hexdigit(b); }
void log_hexi16(uint16_t s)
{ log_hexbyte(s >> 8); log_hexbyte(s); }
void log_hexi32(uint32_t i)
{ log_hexbyte(i >> 24); log_hexbyte(i >> 16); log_hexbyte(i >> 8); log_hexbyte(i); }
void log_hexi32p(uint32_t *p) // On a platform where pointers are less than 32 bits
{ log_hexi32(*p); }

কিছু আমাদের PIC মাইক্রোকন্ট্রোলারের উপর, log_hexi32(l)সম্ভবত 9 নির্দেশাবলী গ্রহণ করা হবে এবং 17 নিতে পারে (যদি lদ্বিতীয় ব্যাংকে হয়), যখন log_hexi32p(&l)2. লাগবে log_hexi32pফাংশন নিজেই দীর্ঘ 14 নির্দেশাবলী সম্পর্কে করা লেখা যেতে পারে, তাই এটি নিজেই জন্য দিতে যদি দুইবার বলা ।


2

এমন একটি বিন্দু যা অন্য উত্তরগুলির মধ্যে কেউ উল্লেখ করেনি: একটি বুনিয়াদী মাইক্রোতে (IE কেবল প্রধান () লুপ থাকে এবং সম্ভবত যে কোনও সময়ে আইএসআর চলমান রয়েছে, কোনও মাল্টি-থ্রেড্ড ওএস নয়) ক্র্যাশ / স্টপ / পায় তবে একটি লুপ আটকে, আপনার মুদ্রণ ফাংশন সহজভাবে ঘটবে না

Also, people have said "don't use printf" or "stdio.h takes a lot of space" but not given much alternative - embedded.kyle makes mention of simplified alternatives, and that is exactly the sort of thing you should probably be doing as a matter of course on a basic embedded system. A basic routine to squirt a few characters out of the UART could be a few bytes of code.


If your printf doesn't happen, you've learned plenty about where your codeis problematic.
Scott Seidman

Assuming you only have one printf that might happen, yes. But interrupts can fire hundreds of times in the time it takes a printf() call to get anything out of the UART
John U
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.